소스 검색

将逻辑修复

Huanyi 2 주 전
부모
커밋
9e5c97c048
4개의 변경된 파일342개의 추가작업 그리고 201개의 파일을 삭제
  1. 8 0
      src/api/erp/order/types.ts
  2. 35 0
      src/api/system/phone/index.ts
  3. 229 201
      src/views/order/index.vue
  4. 70 0
      src/views/system/phone/index.vue

+ 8 - 0
src/api/erp/order/types.ts

@@ -24,7 +24,15 @@ export interface ErpOrderDetailVo {
   rowId: string;
   orderId: string;
   modelId: string;
+  modelNum?: string;
   modelName: string;
+  material?: string;
+  surfaceId?: string;
+  surfaceName?: string;
+  packId?: string;
+  packName?: string;
+  length?: number;
+  wallThickness?: number;
   colorId: string;
   colorName: string;
   count: number;

+ 35 - 0
src/api/system/phone/index.ts

@@ -0,0 +1,35 @@
+import request from '@/utils/request';
+
+export interface PhoneVO {
+  id: number;
+  expertPhone: string;
+  servicePhone: string;
+  updateTime: string;
+}
+
+export interface PhoneForm {
+  id: number;
+  expertPhone: string;
+  servicePhone: string;
+}
+
+/**
+ * 查询联系电话
+ */
+export const getPhone = (id: number) => {
+  return request<PhoneVO>({
+    url: `/system/phone/${id}`,
+    method: 'get'
+  });
+};
+
+/**
+ * 修改联系电话
+ */
+export const updatePhone = (data: PhoneForm) => {
+  return request<void>({
+    url: '/system/phone',
+    method: 'put',
+    data
+  });
+};

+ 229 - 201
src/views/order/index.vue

@@ -1,73 +1,84 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
-      <el-form-item label="订单单号" prop="code">
-        <el-input v-model="queryParams.code" placeholder="请输入订单单号" clearable style="width: 200px"
-          @keyup.enter="handleQuery" />
-      </el-form-item>
-      <el-form-item label="下单人" prop="placer">
-        <el-input v-model="queryParams.placer" placeholder="请输入下单人" clearable style="width: 200px"
-          @keyup.enter="handleQuery" />
-      </el-form-item>
-      <el-form-item label="订单状态" prop="status">
-        <el-select v-model="queryParams.status" placeholder="订单状态" clearable style="width: 200px">
-          <el-option label="已撤销" :value="-1" />
-          <el-option label="待审核" :value="0" />
-          <el-option label="已驳回" :value="1" />
-          <el-option label="待签批" :value="2" />
-          <el-option label="生产中" :value="3" />
-          <el-option label="已完成" :value="4" />
-        </el-select>
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
-        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
-      </el-form-item>
-    </el-form>
-
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['erp:order:add']">新增</el-button>
-      </el-col>
-      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
-
-    <el-table v-loading="loading" :data="orderList">
-      <el-table-column label="订单单号" align="center" prop="code" />
-      <el-table-column label="下单人" align="center" prop="placerName" />
-      <el-table-column label="下单时间" align="center" prop="placeTime" width="180">
-        <template #default="scope">
-          <span>{{ parseTime(scope.row.placeTime) }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="总支数" align="center" prop="totalCount" />
-      <el-table-column label="状态" align="center" prop="status">
-        <template #default="scope">
-          <el-tag v-if="scope.row.status === -1" type="info">已撤销</el-tag>
-          <el-tag v-else-if="scope.row.status === 0" type="warning">待审核</el-tag>
-          <el-tag v-else-if="scope.row.status === 1" type="danger">已驳回</el-tag>
-          <el-tag v-else-if="scope.row.status === 2" type="primary">待签批</el-tag>
-          <el-tag v-else-if="scope.row.status === 3" type="info">生产中</el-tag>
-          <el-tag v-else-if="scope.row.status === 4" type="success">已完成</el-tag>
-          <el-tag v-else type="info">未知</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
-        <template #default="scope">
-          <span>{{ parseTime(scope.row.createTime) }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-        <template #default="scope">
-          <el-button link type="primary" icon="Stamp" @click="handleAudit(scope.row)" v-if="scope.row.status === 0"
-            v-hasPermi="['erp:order:audit']">审核</el-button>
-          <el-button link type="primary" icon="Timer" @click="handleViewAuditHistory(scope.row)">审核记录</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
-      v-model:limit="queryParams.pageSize" @pagination="getList" />
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter"
+      :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="search">
+        <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="85px">
+          <el-form-item label="订单单号" prop="code">
+            <el-input v-model="queryParams.code" placeholder="请输入订单单号" clearable
+              @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="下单人" prop="placer">
+            <el-input v-model="queryParams.placer" placeholder="请输入下单人" clearable
+              @keyup.enter="handleQuery" />
+          </el-form-item>
+
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+            <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <!-- 状态筛选标签栏(内嵌于卡片) -->
+      <div class="status-tabs">
+        <div
+          v-for="(tab, index) in statusTabs"
+          :key="index"
+          class="tab-item"
+          :class="{ active: currentTab === tab.value }"
+          @click="handleTabClick(tab.value)"
+        >
+          {{ tab.label }}
+        </div>
+      </div>
+
+      <el-table v-loading="loading" :data="orderList" border>
+        <el-table-column label="订单单号" align="center" prop="code" />
+        <el-table-column label="下单人" align="center" prop="placerName" />
+        <el-table-column label="下单时间" align="center" prop="placeTime" width="180">
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.placeTime) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="总支数" align="center" prop="totalCount" />
+        <el-table-column label="状态" align="center" prop="status">
+          <template #default="scope">
+            <el-tag v-if="scope.row.status === -1" type="info">已撤销</el-tag>
+            <el-tag v-else-if="scope.row.status === 0" type="warning">待审核</el-tag>
+            <el-tag v-else-if="scope.row.status === 1" type="danger">已驳回</el-tag>
+            <el-tag v-else-if="scope.row.status === 2" type="primary">待签批</el-tag>
+            <el-tag v-else-if="scope.row.status === 3" type="info">生产中</el-tag>
+            <el-tag v-else-if="scope.row.status === 4" type="success">已完成</el-tag>
+            <el-tag v-else type="info">未知</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.createTime) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="查看详情" placement="top">
+              <el-button v-hasPermi="['order:order:query']" link type="primary" icon="View"
+                @click="handleViewOrder(scope.row)" />
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
 
     <!-- 添加或修改订单对话框 -->
     <el-dialog :title="title" v-model="open" width="1000px" append-to-body>
@@ -132,66 +143,59 @@
       </template>
     </el-dialog>
 
-    <!-- 审核记录对话框 -->
-    <el-dialog title="审核记录" v-model="auditHistoryOpen" width="600px" append-to-body>
-      <el-timeline v-if="auditHistoryList.length > 0">
-        <el-timeline-item
-          v-for="(item, index) in auditHistoryList"
-          :key="index"
-          hide-timestamp
-          :color="item.auditResult === 1 ? '#67c23a' : '#f56c6c'"
-        >
-          <div class="audit-card" :class="item.auditResult === 1 ? 'card-pass' : 'card-reject'">
-            <div class="audit-card-header">
-              <el-tag :type="item.auditResult === 1 ? 'success' : 'danger'" effect="dark" size="small">
-                {{ item.auditResult === 1 ? '通过' : '驳回' }}
-              </el-tag>
-              <span class="audit-card-auditor">
-                <el-icon><User /></el-icon> {{ item.auditorName || '未知' }}
-              </span>
-            </div>
-            <div v-if="item.rejectReason" class="audit-card-reason">
-              <span class="reason-label">驳回理由</span>
-              <span class="reason-text">{{ item.rejectReason }}</span>
-            </div>
-            <div class="audit-card-time">
-              <el-icon><Clock /></el-icon> {{ parseTime(item.auditTime) }}
+    <!-- 订单详情对话框 -->
+    <el-dialog title="订单详情" v-model="detailOpen" width="800px" append-to-body>
+      <template v-if="detailOrder">
+        <div class="section-title">订单信息</div>
+        <el-descriptions :column="2" border size="small">
+          <el-descriptions-item label="订单单号" :span="2">{{ detailOrder.code }}</el-descriptions-item>
+          <el-descriptions-item label="下单人">{{ detailOrder.placerName }}</el-descriptions-item>
+          <el-descriptions-item label="订单状态">
+            <el-tag v-if="detailOrder.status === -1" type="info">已撤销</el-tag>
+            <el-tag v-else-if="detailOrder.status === 0" type="warning">待审核</el-tag>
+            <el-tag v-else-if="detailOrder.status === 1" type="danger">已驳回</el-tag>
+            <el-tag v-else-if="detailOrder.status === 2" type="primary">待签批</el-tag>
+            <el-tag v-else-if="detailOrder.status === 3" type="info">生产中</el-tag>
+            <el-tag v-else-if="detailOrder.status === 4" type="success">已完成</el-tag>
+            <el-tag v-else type="info">未知</el-tag>
+          </el-descriptions-item>
+          <el-descriptions-item label="下单时间">{{ parseTime(detailOrder.placeTime) }}</el-descriptions-item>
+          <el-descriptions-item label="创建时间">{{ parseTime(detailOrder.createTime) }}</el-descriptions-item>
+          <el-descriptions-item label="总支数">{{ detailOrder.totalCount }}</el-descriptions-item>
+        </el-descriptions>
+
+        <div class="section-title" style="margin-top: 20px">订单明细</div>
+        <div class="detail-scroll" v-if="detailOrder.details && detailOrder.details.length > 0">
+          <div class="detail-card" v-for="(detail, dIdx) in detailOrder.details" :key="dIdx">
+            <div class="detail-card-head">规格清单 #{{ dIdx + 1 }}</div>
+            <el-descriptions :column="2" border size="small">
+              <el-descriptions-item label="产品型号" :span="2">{{ detail.modelNum || '未知型号' }}</el-descriptions-item>
+              <el-descriptions-item label="型号名称" :span="2">{{ detail.modelName || '铝型材主料' }}</el-descriptions-item>
+              <el-descriptions-item label="型材材质">{{ detail.material || '6063-T5' }}</el-descriptions-item>
+              <el-descriptions-item label="需求支数">
+                <span class="count-red">{{ detail.count || 0 }} 支</span>
+              </el-descriptions-item>
+              <el-descriptions-item label="表面处理">{{ detail.surfaceName || '无' }}</el-descriptions-item>
+              <el-descriptions-item label="包装方式">{{ detail.packName || '普通包装' }}</el-descriptions-item>
+              <el-descriptions-item label="订单长度">{{ formatNum(detail.length) }} mm</el-descriptions-item>
+              <el-descriptions-item label="型材壁厚">{{ formatNum(detail.wallThickness, '1.2') }} mm</el-descriptions-item>
+            </el-descriptions>
+            <div class="detail-remark" v-if="detail.remark">
+              <span class="remark-label">备注:</span>{{ detail.remark }}
             </div>
           </div>
-        </el-timeline-item>
-      </el-timeline>
-      <el-empty v-else description="暂无审核记录" />
-    </el-dialog>
-
-    <!-- 订单审核对话框 -->
-    <el-dialog title="订单审核" v-model="auditOpen" width="500px" append-to-body>
-      <el-form ref="auditRef" :model="auditForm" :rules="auditRules" label-width="80px">
-        <el-form-item label="审核结果" prop="auditResult">
-          <el-radio-group v-model="auditForm.auditResult">
-            <el-radio :label="1">通过</el-radio>
-            <el-radio :label="2">驳回</el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="驳回原因" prop="rejectReason" v-if="auditForm.auditResult === 2">
-          <el-input v-model="auditForm.rejectReason" type="textarea" placeholder="请输入驳回原因" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button type="primary" @click="submitAudit">确 定</el-button>
-          <el-button @click="auditOpen = false">取 消</el-button>
         </div>
+        <el-empty v-else description="暂无明细" :image-size="60" />
       </template>
     </el-dialog>
   </div>
 </template>
 
 <script setup name="Order">
-import { listOrder, getOrder, addOrder, auditOrder } from "@/api/erp/order";
+import { listOrder, getOrder, addOrder } from "@/api/erp/order";
 import { listModel } from "@/api/erp/model";
 import { listColor } from "@/api/erp/color";
-import { getAuditHistory } from "@/api/erp/orderAudit";
-import { User, Clock } from '@element-plus/icons-vue';
+import { View } from '@element-plus/icons-vue';
 
 /** @Author: Antigravity */
 
@@ -203,9 +207,20 @@ const loading = ref(true);
 const showSearch = ref(true);
 const total = ref(0);
 const title = ref("");
-const auditOpen = ref(false);
-const auditHistoryOpen = ref(false);
-const auditHistoryList = ref([]);
+const detailOpen = ref(false);
+const detailOrder = ref(null);
+const currentTab = ref(undefined);
+const statusTabs = [
+  { label: '全部', value: undefined },
+  { label: '已撤销', value: -1 },
+  { label: '待审核', value: 0 },
+  { label: '已驳回', value: 1 },
+  { label: '待签批', value: 2 },
+  { label: '生产中', value: 3 },
+  { label: '已完成', value: 4 }
+];
+
+const queryFormRef = ref();
 
 const modelOptions = ref([]);
 const colorOptions = ref([]);
@@ -223,18 +238,14 @@ const data = reactive({
   rules: {
     placer: [{ required: true, message: "下单人不能为空", trigger: "blur" }]
   },
-  auditForm: {},
-  auditRules: {
-    auditResult: [{ required: true, message: "请选择审核结果", trigger: "change" }],
-    rejectReason: [{ required: true, message: "请输入驳回原因", trigger: "blur" }]
-  }
 });
 
-const { queryParams, form, rules, auditForm, auditRules } = toRefs(data);
+const { queryParams, form, rules } = toRefs(data);
 
 /** 查询订单列表 */
 function getList() {
   loading.ref = true;
+  queryParams.value.status = currentTab.value;
   listOrder(queryParams.value).then(response => {
     orderList.value = response.rows;
     total.value = response.total;
@@ -273,12 +284,20 @@ function reset() {
 /** 搜索按钮操作 */
 function handleQuery() {
   queryParams.value.pageNum = 1;
+  queryParams.value.status = currentTab.value;
   getList();
 }
 
+/** 状态标签点击 */
+function handleTabClick(value) {
+  currentTab.value = value;
+  handleQuery();
+}
+
 /** 重置按钮操作 */
 function resetQuery() {
-  proxy.resetForm("queryRef");
+  queryFormRef.value?.resetFields();
+  currentTab.value = undefined;
   handleQuery();
 }
 
@@ -289,37 +308,14 @@ function handleAdd() {
   title.value = "添加订单";
 }
 
-/** 审核按钮操作 */
-function handleAudit(row) {
-  auditForm.value = {
-    orderId: row.rowId,
-    auditResult: 1,
-    rejectReason: undefined
-  };
-  auditOpen.value = true;
-}
-
-/** 查看审核记录 */
-function handleViewAuditHistory(row) {
-  auditHistoryList.value = [];
-  auditHistoryOpen.value = true;
-  getAuditHistory(row.rowId).then(res => {
-    auditHistoryList.value = res.data || [];
+/** 查看订单详情 */
+function handleViewOrder(row) {
+  detailOpen.value = true;
+  detailOrder.value = null;
+  getOrder(row.rowId).then(res => {
+    detailOrder.value = res.data;
   }).catch(() => {
-    proxy.$modal.msgError("获取审核记录失败");
-  });
-}
-
-/** 提交审核 */
-function submitAudit() {
-  proxy.$refs["auditRef"].validate(valid => {
-    if (valid) {
-      auditOrder(auditForm.value).then(response => {
-        proxy.$modal.msgSuccess("审核成功");
-        auditOpen.value = false;
-        getList();
-      });
-    }
+    proxy.$modal.msgError("获取订单详情失败");
   });
 }
 
@@ -370,67 +366,99 @@ function handleErpOrderDetailSelectionChange(selection) {
   checkedErpOrderDetail.value = selection.map(item => item.index);
 }
 
+/** 格式化数值,匹配小程序端的 toFixed(4) 显示 */
+function formatNum(val, fallback = '0') {
+  if (val === null || val === undefined || val === '') return fallback;
+  const num = Number(val);
+  return isNaN(num) ? fallback : num.toFixed(4);
+}
+
 getOptions();
 getList();
 </script>
 
 <style scoped>
-.audit-card {
-  background: #fff;
-  border-radius: 8px;
-  padding: 16px 20px;
-  border-left: 4px solid #ebeef5;
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
-  transition: box-shadow 0.2s;
+
+
+/* ========== 状态筛选标签栏 ========== */
+.status-tabs {
+  display: flex;
+  gap: 2px;
+  margin-bottom: 16px;
+  border-bottom: 1px solid #ebeef5;
+  padding-bottom: 0;
+}
+.tab-item {
+  position: relative;
+  padding: 10px 20px;
+  font-size: 14px;
+  color: #606266;
+  cursor: pointer;
+  transition: color 0.25s ease;
+  user-select: none;
 }
-.audit-card:hover {
-  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
+.tab-item::after {
+  content: '';
+  position: absolute;
+  bottom: -1px;
+  left: 50%;
+  transform: translateX(-50%) scaleX(0);
+  width: 100%;
+  height: 2px;
+  background: #409eff;
+  transition: transform 0.25s ease;
 }
-.audit-card.card-pass {
-  border-left-color: #67c23a;
+.tab-item:hover {
+  color: #409eff;
 }
-.audit-card.card-reject {
-  border-left-color: #f56c6c;
+.tab-item.active {
+  color: #409eff;
+  font-weight: 600;
+}
+.tab-item.active::after {
+  transform: translateX(-50%) scaleX(1);
 }
 
-.audit-card-header {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
+/* ========== 详情弹框样式 ========== */
+.section-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: #303133;
   margin-bottom: 12px;
+  padding-left: 10px;
+  border-left: 3px solid #C1001C;
 }
-.audit-card-auditor {
-  font-size: 14px;
-  color: #606266;
-  display: flex;
-  align-items: center;
-  gap: 4px;
+.detail-scroll {
+  max-height: 420px;
+  overflow-y: auto;
+  padding-right: 2px;
 }
-
-.audit-card-reason {
-  background: #fef0f0;
+.detail-card {
+  background: #fff;
+  border: 1px solid #ebeef5;
   border-radius: 6px;
-  padding: 10px 14px;
+  padding: 14px;
   margin-bottom: 12px;
 }
-.reason-label {
-  font-size: 12px;
-  color: #f56c6c;
-  font-weight: 500;
-  display: block;
-  margin-bottom: 4px;
-}
-.reason-text {
+.detail-card-head {
   font-size: 13px;
-  color: #c03639;
-  line-height: 1.5;
+  font-weight: 600;
+  color: #303133;
+  margin-bottom: 10px;
 }
-
-.audit-card-time {
-  font-size: 13px;
+.count-red {
+  color: #ff3b30;
+  font-weight: 600;
+}
+.detail-remark {
+  margin-top: 8px;
+  padding: 6px 10px;
+  background: #fafafa;
+  border-radius: 4px;
+  font-size: 12px;
+  color: #606266;
+}
+.remark-label {
   color: #909399;
-  display: flex;
-  align-items: center;
-  gap: 4px;
 }
 </style>

+ 70 - 0
src/views/system/phone/index.vue

@@ -0,0 +1,70 @@
+<template>
+  <div class="p-2">
+    <el-card shadow="never">
+      <template #header>
+        <span class="font-bold text-base">联系电话管理</span>
+      </template>
+
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="140px" class="mt-4">
+        <el-form-item label="华晟型材专家电话" prop="expertPhone">
+          <el-input v-model="form.expertPhone" placeholder="请输入专家电话" style="width: 300px" maxlength="20" />
+        </el-form-item>
+        <el-form-item label="客服电话" prop="servicePhone">
+          <el-input v-model="form.servicePhone" placeholder="请输入客服电话" style="width: 300px" maxlength="20" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" :loading="saving" @click="handleSave">
+            保存
+          </el-button>
+          <el-button @click="loadPhone">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { getPhone, updatePhone, PhoneForm } from '@/api/system/phone';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const saving = ref(false);
+const formRef = ref();
+
+const form = reactive<PhoneForm>({
+  id: 1,
+  expertPhone: '',
+  servicePhone: ''
+});
+
+const rules = {
+  expertPhone: [{ required: true, message: '专家电话不能为空', trigger: 'blur' }],
+  servicePhone: [{ required: true, message: '客服电话不能为空', trigger: 'blur' }]
+};
+
+/** 加载联系电话 */
+const loadPhone = async () => {
+  const res = await getPhone(1);
+  form.id = res.data.id;
+  form.expertPhone = res.data.expertPhone;
+  form.servicePhone = res.data.servicePhone;
+};
+
+/** 保存 */
+const handleSave = () => {
+  formRef.value?.validate(async (valid: boolean) => {
+    if (!valid) return;
+    saving.value = true;
+    try {
+      await updatePhone({ ...form });
+      proxy?.$modal.msgSuccess('保存成功');
+    } finally {
+      saving.value = false;
+    }
+  });
+};
+
+onMounted(() => {
+  loadPhone();
+});
+</script>