Browse Source

销花宝后天 开单功能界面优化

ouyang 1 day ago
parent
commit
6802dd3231

+ 2 - 112
ghs/src/components/order/OrderCommitDialog.vue

@@ -1,8 +1,8 @@
 <!--
   开单确认提交弹窗
   开单确认提交弹窗
-  UI/业务对齐 ghsApp affirm:配送、打折、查看财务、付款与备注提交。
-  供 POS add.vue 与旧 confirm 页复用。
+  UI/业务对齐 ghsApp affirm:配送、打折、付款与备注提交。
+  查看财务在 POS 购物车侧;本弹窗供 add.vue 复用。
 -->
 <template>
   <el-dialog
@@ -214,34 +214,6 @@
           </span>
         </div>
       </div>
-      <!-- 有财务权限才显示:成本/毛利(对齐 affirm 查看财务) -->
-      <div class="accounts-list" v-if="hasFinance == 1">
-        <span class="accounts-label">财务</span>
-        <div class="accounts-center accounts-center--row finance-bar">
-          <template v-if="showFinance == 1">
-            <span class="finance-sum">
-              成本 ¥{{ financeAvCost }},毛利 ¥{{ financeProfit }}
-            </span>
-            <el-button type="text" @click="toggleFinance">关闭财务</el-button>
-          </template>
-          <el-button v-else type="text" @click="toggleFinance">查看财务</el-button>
-        </div>
-      </div>
-      <div v-if="hasFinance == 1 && showFinance == 1 && !$util.isEmpty(selectList)" class="finance-items">
-        <div
-          v-for="item in selectList"
-          :key="item.id"
-          class="finance-item"
-          :class="{ warn: Number(item.avCost) > Number(item.userPrice) && Number(item.bigCount) > 0 }"
-        >
-          <span class="name" :title="item.name">{{ item.name }}</span>
-          <span class="meta">
-            开价 ¥{{ parseFloat(item.userPrice) || 0 }}
-            · 成本 ¥{{ item.avCost != null ? parseFloat(item.avCost) : 0 }}
-            · 毛利 ¥{{ unitProfit(item) }}
-          </span>
-        </div>
-      </div>
       <div class="accounts-list">
         <span class="accounts-label">{{ form.hasPay == 2 ? '应收金额' : '实收金额' }}</span>
         <div class="accounts-center">
@@ -380,10 +352,6 @@ export default {
       discountVisible: false,
       discountValue: 0,
       customDiscount: '',
-      /** 员工是否有财务查看权限(staff.finance) */
-      hasFinance: 0,
-      /** 是否展开成本/毛利 */
-      showFinance: 0,
       discountList: [
         { name: '1折', value: 0.1 },
         { name: '2折', value: 0.2 },
@@ -447,21 +415,6 @@ export default {
       if (this.currentShop.pfLevel && this.currentShop.pfLevel == 1) return true
       return false
     },
-    /** 购物车总成本(大单位×avCost,对齐 App allCount.avCost) */
-    financeAvCost() {
-      let avCost = 0
-      if (!this.$util.isEmpty(this.selectList)) {
-        this.selectList.forEach(item => {
-          const unitCost = item.avCost != null ? Number(item.avCost) : 0
-          avCost += unitCost * Number(item.bigCount || 0)
-        })
-      }
-      return parseFloat(avCost.toFixed(2))
-    },
-    /** 毛利 = 花材合计 - 成本(不含运费打包费) */
-    financeProfit() {
-      return parseFloat((Number(this.goodsPrice || 0) - Number(this.financeAvCost)).toFixed(2))
-    },
     /** 扎数合计,用于花材满减规则 */
     totalBigCount() {
       let big = 0
@@ -536,14 +489,12 @@ export default {
       this.form.deliveryRemark = ''
       this.discountValue = 0
       this.customDiscount = ''
-      this.showFinance = 0
       this.pickupTimeValue = dayjs().format('YYYY-MM-DD HH:mm')
       this.$nextTick(() => {
         this.modifyPrice = this.finalPrice
       })
       this.loadShopAndStaff()
       this.loadCustomDetail()
-      this.loadStaffFinance()
     },
     loadShopAndStaff() {
       const that = this
@@ -557,10 +508,6 @@ export default {
           that.hcMap = []
         }
         const currentStaff = res.staff || {}
-        // shopInfo 里若已带 finance,可先用
-        if (currentStaff.finance != null) {
-          that.hasFinance = Number(currentStaff.finance) || 0
-        }
         that.$service.shopAdmin.getAllStaff().then(staffRes => {
           that.staffList = staffRes.list || []
           if (!that.$util.isEmpty(that.staffList)) {
@@ -574,23 +521,6 @@ export default {
         })
       })
     },
-    /** 拉取员工财务权限(对齐 App getStaffInfo → staff.finance) */
-    loadStaffFinance() {
-      this.$service.shopAdmin.getStaffInfo().then(res => {
-        const staff = res.staff || (res.data && res.data.staff) || res || {}
-        this.hasFinance = Number(staff.finance) || 0
-      }).catch(() => {})
-    },
-    /** 展开/收起成本毛利 */
-    toggleFinance() {
-      this.showFinance = this.showFinance == 1 ? 0 : 1
-    },
-    /** 单品单位毛利:开价 - 成本 */
-    unitProfit(item) {
-      const price = Number(item.userPrice) || 0
-      const cost = item.avCost != null ? Number(item.avCost) : 0
-      return parseFloat((price - cost).toFixed(2))
-    },
     /** 拉客户详情:默认物流名、地址(跑腿询价用) */
     loadCustomDetail() {
       if (!this.customId) return
@@ -879,46 +809,6 @@ export default {
     display: flex;
     align-items: center;
   }
-  .finance-bar {
-    color: #3385ff;
-    font-weight: 700;
-    .finance-sum {
-      margin-right: 8px;
-    }
-  }
-  .finance-items {
-    margin: -4px 0 12px 84px;
-    padding: 8px 10px;
-    background: #f5f7fa;
-    border-radius: 4px;
-    max-height: 160px;
-    overflow-y: auto;
-  }
-  .finance-item {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    font-size: 12px;
-    color: #606266;
-    padding: 4px 0;
-    .name {
-      flex: 1;
-      min-width: 0;
-      margin-right: 8px;
-      overflow: hidden;
-      text-overflow: ellipsis;
-      white-space: nowrap;
-      font-weight: 600;
-      color: #303133;
-    }
-    .meta {
-      flex-shrink: 0;
-    }
-    &.warn .meta {
-      color: #f56c6c;
-      font-weight: 700;
-    }
-  }
   .shop-name {
     font-weight: 700;
     color: #303133;

+ 9 - 0
ghs/src/components/order/pos/PosItemEditDialog.vue

@@ -22,6 +22,7 @@
 
     <div class="edit-row">
       <el-input
+        class="edit-input-center"
         style="width: 160px"
         placeholder="单价"
         type="number"
@@ -38,6 +39,7 @@
       <el-input
         v-if="unitType === 0"
         ref="qtyInput"
+        class="edit-input-center"
         style="width: 160px"
         placeholder="数量"
         type="number"
@@ -49,6 +51,7 @@
       <el-input
         v-if="unitType === 1"
         ref="qtyInput"
+        class="edit-input-center"
         style="width: 160px"
         placeholder="数量"
         type="number"
@@ -284,6 +287,12 @@ export default {
   justify-content: flex-start;
   margin-bottom: 12px;
 }
+/* 单价、数量输入内容居中 */
+.edit-input-center {
+  ::v-deep .el-input__inner {
+    text-align: center;
+  }
+}
 .unit {
   margin-left: 8px;
   margin-right: 8px;

+ 191 - 52
ghs/src/views/order/add.vue

@@ -9,7 +9,7 @@
     <audio ref="noStockPlay"><source src="@/static/noStock.mp3" type="audio/mpeg"></audio>
     <audio ref="successPlay"><source src="@/static/success1.mp3" type="audio/mpeg"></audio>
 
-    <!-- 顶栏:左侧当前客户名,中间搜商品,右侧选客/快捷开单 -->
+    <!-- 顶栏整块居中:客户名 + 搜商品 + 选客/快捷开单 -->
     <div class="pos-header">
       <div class="pos-customer-name" :title="headerCustomerLabel">{{ headerCustomerLabel }}</div>
       <div class="pos-search-wrap">
@@ -55,9 +55,10 @@
             v-for="item in globalItemData"
             :key="item.id"
             class="pos-goods-card"
+            @click="openEditByCover(item)"
           >
             <div v-if="item.limitBuy > 0" class="pos-limit-tag">限购{{ item.limitBuy }}份</div>
-            <div class="pos-goods-cover" @click="openEditByCover(item)">
+            <div class="pos-goods-cover">
               <img :src="item.cover || item.bigCover" alt="" />
             </div>
             <div class="pos-goods-body">
@@ -74,20 +75,21 @@
                 <span class="spec">{{ formatSpec(item) }}</span>
                 <span v-if="goodsOrangeTag(item)" class="pos-orange-tag">{{ goodsOrangeTag(item) }}</span>
               </div>
-              <div class="pos-stepper">
+              <!-- 加减单独 stop,避免触发整卡弹窗 -->
+              <div class="pos-stepper" @click.stop>
                 <button
                   type="button"
                   class="pos-step-btn pos-step-btn--minus"
-                  @click.stop="stepItem(item, 'sub')"
+                  @click="stepItem(item, 'sub')"
                 >−</button>
                 <div
                   class="pos-step-num"
-                  @click.stop="openEditByCover(item)"
+                  @click="openEditByCover(item)"
                 >{{ displayCount(item) }}</div>
                 <button
                   type="button"
                   class="pos-step-btn pos-step-btn--plus"
-                  @click.stop="stepItem(item, 'add')"
+                  @click="stepItem(item, 'add')"
                 >+</button>
               </div>
             </div>
@@ -110,22 +112,27 @@
             @click="openEditCart(row)"
           >
             <img class="pos-cart-cover" :src="row.cover || row.bigCover" alt="" />
-            <div class="pos-cart-info">
-              <div class="name">{{ row.name }}</div>
-              <div class="sub">
-                <template v-if="Number(row.bigCount) > 0">{{ row.bigCount }}{{ row.bigUnit || '扎' }}</template>
-                <template v-if="Number(row.bigCount) > 0 && Number(row.smallCount) > 0">/</template>
-                <template v-if="Number(row.smallCount) > 0">{{ row.smallCount }}{{ row.smallUnit || '支' }}</template>
-                · ¥{{ parseFloat(row.userPrice) }}
+            <div class="pos-cart-main">
+              <div class="pos-cart-top">
+                <div class="name" :title="row.name">{{ row.name }}</div>
+                <div class="line-price">¥{{ lineAmount(row) }}</div>
               </div>
-            </div>
-            <div class="pos-cart-right">
-              <div class="line-price">¥{{ lineAmount(row) }}</div>
-              <div class="pos-stepper pos-stepper--sm" @click.stop>
-                <el-button size="mini" type="primary" plain circle icon="el-icon-minus" @click="stepItem(row, 'sub')" />
-                <!-- 点数量弹窗改量,与商品卡一致 -->
-                <span class="num num--click" @click="openEditCart(row)">{{ displayCount(row) }}</span>
-                <el-button size="mini" type="primary" circle icon="el-icon-plus" @click="stepItem(row, 'add')" />
+              <div class="pos-cart-mid">
+                <div class="unit-price">¥{{ parseFloat(row.userPrice) }}</div>
+                <div class="pos-stepper pos-stepper--sm" @click.stop>
+                  <el-button size="mini" type="primary" plain circle icon="el-icon-minus" @click="stepItem(row, 'sub')" />
+                  <span class="num num--click" @click="openEditCart(row)">{{ displayCount(row) }}</span>
+                  <el-button size="mini" type="primary" circle icon="el-icon-plus" @click="stepItem(row, 'add')" />
+                </div>
+              </div>
+              <!-- 查看财务时:成本/毛利挂在对应商品下方(对齐设计图) -->
+              <div
+                v-if="showFinance == 1"
+                class="pos-cart-finance"
+                :class="{ warn: Number(row.avCost) > Number(row.userPrice) && Number(row.bigCount) > 0 }"
+              >
+                成本 ¥{{ row.avCost != null ? parseFloat(row.avCost) : 0 }}
+                毛利 ¥{{ unitProfit(row) }}
               </div>
             </div>
           </div>
@@ -135,8 +142,18 @@
             合计: ¥ {{ allPrice }},{{ allCount.kind || 0 }}种,{{ allCount.bigLength || 0 }}扎
             <template v-if="allCount.weight">,{{ allCount.weight }}公斤</template>
           </div>
-          <div class="sum-price">¥ {{ allPrice }}</div>
-          <div class="shop-tip">当前在 {{ shopDisplayName }}</div>
+          <!-- 总价、当前在同一行;查看财务单独一行 -->
+          <div class="footer-main-row">
+            <div class="sum-price">¥ {{ allPrice }}</div>
+            <div class="shop-tip">当前在 {{ shopDisplayName }}</div>
+          </div>
+          <div v-if="hasFinance == 1" class="footer-finance" @click.stop>
+            <template v-if="showFinance == 1">
+              <span class="footer-finance-sum">成本 ¥{{ financeAvCost }},毛利 ¥{{ financeProfit }}</span>
+              <el-button type="text" class="cart-finance-link" @click="toggleFinance">关闭财务</el-button>
+            </template>
+            <el-button v-else type="text" class="cart-finance-link" @click="toggleFinance">查看财务</el-button>
+          </div>
           <el-button type="primary" class="submit-btn" icon="el-icon-check" @click="openCommit">提交订单</el-button>
         </div>
       </aside>
@@ -225,7 +242,10 @@ export default {
       editVisible: false,
       editItem: {},
       editMode: 'add',
-      commitVisible: false
+      commitVisible: false,
+      /** 员工财务权限;购物车内查看成本毛利 */
+      hasFinance: 0,
+      showFinance: 0
     }
   },
   computed: {
@@ -236,11 +256,27 @@ export default {
         return this.currentCustomName
       }
       return '未选客户'
+    },
+    /** 购物车总成本 */
+    financeAvCost() {
+      let avCost = 0
+      if (!this.$util.isEmpty(this.selectList)) {
+        this.selectList.forEach(item => {
+          const unitCost = item.avCost != null ? Number(item.avCost) : 0
+          avCost += unitCost * Number(item.bigCount || 0)
+        })
+      }
+      return parseFloat(avCost.toFixed(2))
+    },
+    /** 毛利 = 花材合计 - 成本 */
+    financeProfit() {
+      return parseFloat((Number(this.allPrice || 0) - Number(this.financeAvCost)).toFixed(2))
     }
   },
   mounted() {
     this.bootstrap()
     this.bindHotkeys()
+    this.loadStaffFinance()
   },
   beforeDestroy() {
     document.onkeydown = null
@@ -255,6 +291,28 @@ export default {
     successPlay() {
       this.$refs.successPlay && this.$refs.successPlay.play()
     },
+    /** 拉取员工财务权限(对齐 App staff.finance) */
+    loadStaffFinance() {
+      this.$service.shopAdmin.getStaffInfo().then(res => {
+        const staff = res.staff || (res.data && res.data.staff) || res || {}
+        this.hasFinance = Number(staff.finance) || 0
+      }).catch(() => {
+        // 兜底:merchant/detail 的 staff.finance
+        this.$service.common.shopInfo().then(res => {
+          const staff = res.staff || {}
+          this.hasFinance = Number(staff.finance) || 0
+        }).catch(() => {})
+      })
+    },
+    toggleFinance() {
+      this.showFinance = this.showFinance == 1 ? 0 : 1
+    },
+    /** 单品单位毛利 */
+    unitProfit(item) {
+      const price = Number(item.userPrice) || 0
+      const cost = item.avCost != null ? Number(item.avCost) : 0
+      return parseFloat((price - cost).toFixed(2))
+    },
     /** 进页:门店名、快捷客户、分类花材,并聚焦搜索 */
     async bootstrap() {
       const query = this.$route.query || {}
@@ -531,6 +589,8 @@ export default {
   flex-shrink: 0;
   display: flex;
   align-items: center;
+  /* 客户名 + 搜索 + 按钮整块水平居中 */
+  justify-content: center;
   padding: 10px 12px;
   background: #fff;
   border-bottom: 1px solid #ebeef5;
@@ -549,10 +609,11 @@ export default {
 }
 
 .pos-search-wrap {
-  flex: 1;
+  /* 相对 520 再短 1/3 */
+  flex: 0 0 347px;
+  width: 347px;
+  max-width: 347px;
   min-width: 160px;
-  /* 相对原 520 加宽一倍,便于小笔记本连续搜品 */
-  max-width: 1040px;
   ::v-deep .el-input__inner {
     height: 40px;
     line-height: 40px;
@@ -617,7 +678,7 @@ export default {
   gap: 12px;
 }
 
-/* 横向商品卡:左方图 + 右信息区 */
+/* 横向商品卡:整卡可点弹窗,步进器单独 stop */
 .pos-goods-card {
   position: relative;
   display: flex;
@@ -629,6 +690,10 @@ export default {
   border-radius: 8px;
   box-sizing: border-box;
   overflow: hidden;
+  cursor: pointer;
+  &:hover {
+    border-color: #b3d8ff;
+  }
 }
 
 .pos-limit-tag {
@@ -766,7 +831,8 @@ export default {
 }
 
 .pos-step-num {
-  width: 44px;
+  /* 加减中间数量框加宽一倍,便于点选改量 */
+  width: 88px;
   height: 36px;
   margin: 0 4px;
   border: 1px solid #dcdfe6;
@@ -798,7 +864,7 @@ export default {
   }
   .num--click {
     display: inline-block;
-    min-width: 36px;
+    min-width: 72px;
     height: 28px;
     line-height: 26px;
     margin: 0 4px;
@@ -848,49 +914,76 @@ export default {
 
 .pos-cart-row {
   display: flex;
-  align-items: center;
-  padding: 8px;
+  align-items: flex-start;
+  padding: 10px 8px;
   border-radius: 6px;
   cursor: pointer;
+  border-bottom: 1px solid #f0f2f5;
   &:hover {
     background: #f5f7fa;
   }
 }
 
 .pos-cart-cover {
-  width: 48px;
-  height: 48px;
+  width: 56px;
+  height: 56px;
   border-radius: 4px;
   object-fit: cover;
   flex-shrink: 0;
   background: #f0f2f5;
 }
 
-.pos-cart-info {
+.pos-cart-main {
   flex: 1;
   min-width: 0;
-  margin-left: 8px;
+  margin-left: 10px;
+}
+
+.pos-cart-top {
+  display: flex;
+  align-items: flex-start;
+  justify-content: space-between;
   .name {
-    font-size: 13px;
-    font-weight: 600;
+    flex: 1;
+    min-width: 0;
+    margin-right: 8px;
+    font-size: 14px;
+    font-weight: 700;
+    color: #303133;
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
   }
-  .sub {
-    margin-top: 4px;
-    font-size: 12px;
-    color: #909399;
+  .line-price {
+    flex-shrink: 0;
+    color: #303133;
+    font-weight: 700;
+    font-size: 14px;
   }
 }
 
-.pos-cart-right {
-  flex-shrink: 0;
+.pos-cart-mid {
+  margin-top: 6px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  .unit-price {
+    color: #3385ff;
+    font-weight: 700;
+    font-size: 14px;
+  }
+}
+
+/* 单品成本/毛利:挂在步进器下方右侧 */
+.pos-cart-finance {
+  margin-top: 6px;
   text-align: right;
-  .line-price {
+  font-size: 12px;
+  color: #8b7355;
+  line-height: 1.3;
+  &.warn {
     color: #f56c6c;
     font-weight: 700;
-    margin-bottom: 4px;
   }
 }
 
@@ -901,23 +994,64 @@ export default {
   background: #fff;
 }
 
+/* 合计字号相对原先加大后的 24px 再缩小 1/3 → 16px */
 .sum-line {
-  font-size: 12px;
+  font-size: 16px;
   color: #606266;
+  margin-bottom: 8px;
+  line-height: 1.4;
+}
+
+/* 总价与当前在同一行 */
+.footer-main-row {
+  display: flex;
+  align-items: baseline;
+  flex-wrap: wrap;
   margin-bottom: 6px;
 }
 
 .sum-price {
-  font-size: 22px;
+  flex-shrink: 0;
+  /* 相对 44px 缩小 1/3 */
+  font-size: 29px;
   font-weight: 700;
   color: #f56c6c;
-  margin-bottom: 4px;
+  line-height: 1.1;
+  margin-right: 10px;
 }
 
 .shop-tip {
-  font-size: 12px;
-  color: #909399;
-  margin-bottom: 10px;
+  flex-shrink: 0;
+  /* 相对 14px 再大 1/3 */
+  font-size: 19px;
+  color: #606266;
+  line-height: 1.2;
+  max-width: 220px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* 查看财务单独一行 */
+.footer-finance {
+  display: flex;
+  align-items: center;
+  margin-bottom: 12px;
+}
+
+.footer-finance-sum {
+  font-size: 14px;
+  color: #3385ff;
+  font-weight: 700;
+  margin-right: 4px;
+}
+
+.cart-finance-link {
+  padding: 0;
+  font-size: 14px;
+  color: #3385ff;
+  font-weight: 700;
+  text-decoration: underline;
 }
 
 .submit-btn {
@@ -944,6 +1078,11 @@ export default {
     max-width: 140px;
     font-size: 14px;
   }
+  .pos-search-wrap {
+    flex-basis: 280px;
+    width: 280px;
+    max-width: 280px;
+  }
   .pos-goods-cover {
     width: 84px;
     height: 84px;