Просмотр исходного кода

花掌柜-新增配送方式设置

ouyang 2 недель назад
Родитель
Сommit
9122651835

+ 1 - 0
hdApp/src/admin/home/homeMenus.js

@@ -57,6 +57,7 @@ export const APPLY_MENU_GROUPS = [
       { name: '收款码', icon: homeIcon('pay-code'), url: '/admin/cg/code', pf: 1 },
       { name: '收款流水', icon: homeIcon('pay-code-flow'), url: '/admin/order/scanPay', pf: 1 },
       { name: '绑定跑腿', icon: homeIcon('delivery'), url: '/admin/delivery/deliveryManage', pf: 1 },
+      { name: '配送方式', icon: homeIcon('delivery-outline'), url: '/admin/home/shMethod', pf: 1 },
       { name: '配置范围', icon: homeIcon('send_area'), url: '/admin/shop/sk', pf: 1 }
     ]
   },

+ 851 - 0
hdApp/src/admin/home/shMethod.vue

@@ -0,0 +1,851 @@
+<!--
+  配送方式设置页
+  用途:花店应用-门店设置-配送方式,维护送货/自取/跑腿/快递/物流配置
+  来源:与 ghsApp/pagesOrder/shMethod.vue 同源能力
+-->
+<template>  <view class="sh-method-page app-content">
+    <!-- 顶部 Tab 切换 -->
+    <view class="tab-bar">
+      <view
+        v-for="(tab, index) in tabs"
+        :key="index"
+        class="tab-item"
+        :class="{ active: tabIndex === index }"
+        @click="changeTab(index)"
+      >
+        {{ tab.name }}
+      </view>
+    </view>
+
+    <!-- 配置表单区域 -->
+    <scroll-view scroll-y class="form-scroll">
+      <view class="form-container">
+
+        <tui-list-cell class="line-cell" :hover="false">
+          <view class="tui-title">名称</view>
+          <input type="text" v-model="form.name" maxlength="4" @focus="form.name = ''" class="tui-input" placeholder="请填写名称,最多4个字" />
+        </tui-list-cell>
+
+        <!-- 状态 -->
+        <tui-list-cell class="line-cell" :hover="false">
+          <view class="tui-title">状态</view>
+          <view class="btn-group">
+            <button
+              class="admin-button-com middle"
+              :class="[form.status == 1 ? 'blue' : 'default']"
+              @click="form.status = 1"
+            >
+              启用
+            </button>
+            <button
+              class="admin-button-com middle"
+              :class="[form.status == 0 ? 'blue' : 'default']"
+              @click="form.status = 0"
+              style="margin-left: 20upx;"
+            >
+              禁用
+            </button>
+          </view>
+        </tui-list-cell>
+
+        <!-- 位置 -->
+        <tui-list-cell class="line-cell" :hover="false">
+          <view class="tui-title">位置</view>
+          <input type="number" v-model="form.sort" @focus="form.sort = ''" class="tui-input" placeholder="请填写顺序" />
+        </tui-list-cell>
+
+        <!-- 送货 / 快递 特有字段 -->
+        <block v-if="form.style === 0 || form.style === 4">
+          <tui-list-cell class="line-cell" :hover="false">
+            <view class="tui-title">最低消费金额</view>
+            <input type="digit" v-model="form.minAmount" @focus="form.minAmount = ''" class="tui-input" placeholder="不限制填0" />
+          </tui-list-cell>
+          <tui-list-cell class="line-cell" :hover="false">
+            <view class="tui-title">最低消费数量</view>
+            <input type="number" v-model="form.minNum" @focus="form.minNum = ''" class="tui-input" placeholder="不限制填0" />
+          </tui-list-cell>
+          <tui-list-cell class="line-cell" :hover="false">
+            <view class="tui-title">不满条件</view>
+            <view class="btn-group">
+              <button
+                class="admin-button-com middle"
+                :class="[form.unMeet == 0 ? 'blue' : 'default']"
+                @click="form.unMeet = 0"
+              >
+                收运费
+              </button>
+              <button
+                class="admin-button-com middle"
+                :class="[form.unMeet == 2 ? 'blue' : 'default']"
+                @click="form.unMeet = 2"
+                style="margin-left: 10upx;"
+              >
+                收包装费
+              </button>
+              <button
+                class="admin-button-com middle"
+                :class="[form.unMeet == 1 ? 'blue' : 'default']"
+                @click="form.unMeet = 1"
+                style="margin-left: 10upx;"
+              >
+                不能买
+              </button>
+            </view>
+          </tui-list-cell>
+          <tui-list-cell class="line-cell" :hover="false" v-if="form.unMeet != 1">
+            <view class="tui-title">{{ form.unMeet == 2 ? '包装费' : '运费' }}</view>
+            <input type="digit" v-model="form.unMeetFee" @focus="form.unMeetFee = ''" class="tui-input" placeholder="请填写金额" />
+          </tui-list-cell>
+        </block>
+
+        <!-- 跑腿 特有字段 -->
+        <block v-if="form.style === 2">
+          <!-- 满减规则圈起来 -->
+          <view class="rule-section-wrapper">
+            <view class="section-header no-margin">
+              <text class="section-title">满减规则</text>
+              <button class="admin-button-com mini blue" @click="addReduceRule">添加</button>
+            </view>
+            <view class="rule-list" v-if="form.reduceRules && form.reduceRules.length > 0">
+              <view class="rule-card" v-for="(rule, index) in form.reduceRules" :key="index">
+                <view class="rule-row">
+                  <text class="rule-label">满</text>
+                  <input type="number" v-model="rule.meetNum" @focus="clearReduceRuleInputOnFocus(rule, 'meetNum')" @blur="normalizeReduceRuleInput(rule, 'meetNum')" class="rule-input" placeholder="0" />
+                  <text class="rule-label">扎和</text>
+                  <input type="digit" v-model="rule.meetAmount" @focus="clearReduceRuleInputOnFocus(rule, 'meetAmount')" @blur="normalizeReduceRuleInput(rule, 'meetAmount')" class="rule-input-large" placeholder="0.00" />
+                  <text class="rule-label">元</text>
+                  <input type="digit" v-model="rule.freeKm" @focus="clearReduceRuleInputOnFocus(rule, 'freeKm')" @blur="normalizeReduceRuleInput(rule, 'freeKm')" class="rule-input-large" placeholder="0" />
+                  <text class="rule-label">公里内免跑腿费</text>
+                </view>
+                <button class="admin-button-com mini rule-del-btn" @click="removeReduceRule(index)">
+                  删除
+                </button>
+              </view>
+            </view>
+            <view class="no-data-tip" v-else>暂无满减规则</view>
+          </view>
+
+          <view class="notice-text">
+            不符合满减规则时,将通过跑腿计算运费,跑腿无法使用时,将使用自定义计费
+          </view>
+
+          <!-- 自定义计费 -->
+          <view class="section-header">
+            <text class="section-title">自定义计费</text>
+          </view>
+          <tui-list-cell class="line-cell" :hover="false">
+            <view class="tui-title">起步价</view>
+            <view class="input-row">
+              <input type="digit" v-model="form.startKm" @focus="form.startKm = ''" class="tui-input-sm" placeholder="0" />
+              <text class="unit-text">km内</text>
+              <input type="digit" v-model="form.startPrice" @focus="form.startPrice = ''" class="tui-input-sm" placeholder="0.00" />
+              <text class="unit-text">元</text>
+            </view>
+          </tui-list-cell>
+          <tui-list-cell class="line-cell" :hover="false">
+            <view class="tui-title">超出起步</view>
+            <view class="input-row">
+              <text class="unit-text">每公里加</text>
+              <input type="digit" v-model="form.perKmPrice" @focus="form.perKmPrice = ''" class="tui-input-sm" placeholder="0.00" />
+              <text class="unit-text">元</text>
+            </view>
+          </tui-list-cell>
+          <tui-list-cell class="line-cell" :hover="false">
+            <view class="tui-title">临时涨价</view>
+            <view class="input-row">
+              <input type="digit" v-model="form.changeRate" @focus="form.changeRate = ''" class="tui-input-sm" placeholder="0" />
+              <text class="unit-text">%</text>
+              <text class="unit-text">填负值时为降价</text>
+            </view>
+          </tui-list-cell>
+        </block>
+
+        <!-- 物流 特有字段 -->
+        <block v-if="form.style === 3">
+          <tui-list-cell class="line-cell" :hover="false">
+            <view class="tui-title">配送物流</view>
+            <view style="margin-left: auto;">
+              <button class="admin-button-com middle blue" @click="manageWl">管理物流</button>
+            </view>
+          </tui-list-cell>
+        </block>
+
+        <!-- 说明项区域圈起来 -->
+        <view class="explain-section-wrapper">
+          <view class="section-header no-margin">
+            <text class="section-title">说明(最多5个)</text>
+            <button class="admin-button-com mini blue" @click="addExplain">添加</button>
+          </view>
+          <view class="explain-list" v-if="form.explains && form.explains.length > 0">
+            <view class="explain-card" v-for="(exp, index) in form.explains" :key="index">
+              <textarea
+                v-model="exp.explain"
+                class="explain-textarea"
+                placeholder="最多50个字"
+                maxlength="50"
+                :class="[
+                  exp.color == 2 ? 'text-red' : '',
+                  exp.color == 3 ? 'text-blue' : '',
+                  exp.fontWeight == 2 ? 'text-bold' : ''
+                ]"
+              />
+              <view class="explain-actions">
+                <view class="action-group">
+                  <button
+                    class="admin-button-com mini"
+                    :class="[exp.color == 1 ? 'blue' : 'default']"
+                    @click="setExplainColor(index, 1)"
+                  >
+                    黑色
+                  </button>
+                  <button
+                    class="admin-button-com mini"
+                    :class="[exp.color == 2 ? 'blue' : 'default']"
+                    @click="setExplainColor(index, 2)"
+                    style="margin-left: 10upx;"
+                  >
+                    红色
+                  </button>
+                  <button
+                    class="admin-button-com mini"
+                    :class="[exp.color == 3 ? 'blue' : 'default']"
+                    @click="setExplainColor(index, 3)"
+                    style="margin-left: 10upx;"
+                  >
+                    蓝色
+                  </button>
+                </view>
+                <view class="action-group" style="margin-left: 20upx;">
+                  <button
+                    class="admin-button-com mini"
+                    :class="[exp.fontWeight == 1 ? 'blue' : 'default']"
+                    @click="setExplainWeight(index, 1)"
+                  >
+                    常规
+                  </button>
+                  <button
+                    class="admin-button-com mini"
+                    :class="[exp.fontWeight == 2 ? 'blue' : 'default']"
+                    @click="setExplainWeight(index, 2)"
+                    style="margin-left: 10upx;"
+                  >
+                    加粗
+                  </button>
+                </view>
+              <button class="admin-button-com mini explain-del-btn" @click="removeExplain(index)">
+                删除
+              </button>
+              </view>
+            </view>
+          </view>
+          <view class="no-data-tip" v-else>暂无说明项</view>
+        </view>
+      </view>
+    </scroll-view>
+
+    <!-- 底部保存按钮 -->
+    <view class="footer-btn-wrap">
+      <view class="sort-indicator">
+        <text class="sort-label">显示顺序:</text>
+        <text class="sort-value">{{ sortedMethodsText }}</text>
+      </view>
+      <button class="admin-button-com big blue" @click="submitSave">保存</button>
+    </view>
+  </view>
+</template>
+
+<script>
+/**
+ * 花店配送方式设置
+ * 路由:admin/home/shMethod
+ */
+import TuiListCell from '@/components/plugin/list-cell'
+import { getShMethodConfig, saveShMethodConfig, getShMethodSorts } from '@/api/sh-method'
+export default {
+  name: "shMethod",
+  components: {
+    TuiListCell
+  },
+  data() {
+    return {
+      tabs: [
+        { name: "送货", value: 0 },
+        { name: "自取", value: 1 },
+        { name: "跑腿", value: 2 },
+        { name: "快递", value: 4 },
+        { name: "物流", value: 3 }
+      ],
+      tabIndex: 0,
+      sortsList: [], // 存储所有配送方式的排序和别名信息
+      form: {
+        id: 0,
+        style: 0,
+        name: "",
+        status: 1,
+        sort: 0,
+        minAmount: 0.00,
+        minNum: 0,
+        unMeet: 0,
+        unMeetFee: 0.00,
+        startKm: 0.00,
+        startPrice: 0.00,
+        perKmPrice: 0.00,
+        changeRate: 0.00,
+        explains: [],
+        reduceRules: []
+      }
+    };
+  },
+  computed: {
+    // 动态计算并按顺序值由小到大列出所有配送方式别名
+    sortedMethodsText() {
+      // 过滤掉已禁用的配送方式(status === 0)
+      const enabledList = this.sortsList.filter(item => item.status !== 0);
+      if (enabledList.length === 0) {
+        return "暂无启用的配送方式";
+      }
+      // 复制一份数组以避免直接修改 state
+      const sorted = [...enabledList];
+      // 按 sort 升序,若 sort 相同则按 style 升序
+      sorted.sort((a, b) => {
+        if (a.sort !== b.sort) {
+          return a.sort - b.sort;
+        }
+        return a.style - b.style;
+      });
+      // 拼接成 "送货 > 自取 > 跑腿..." 的格式
+      return sorted.map(item => item.name).join(" - ");
+    }
+  },
+  onLoad() {
+    this.loadConfig(0);
+    this.loadSorts();
+  },
+  methods: {
+    init(){},
+    /**
+     * 加载所有配送方式的排序和别名信息
+     */
+    loadSorts() {
+      getShMethodSorts()
+        .then(res => {
+          if (res.code == 1 && res.data) {
+            this.sortsList = res.data;
+          }
+        })
+        .catch(err => {
+          console.error("加载排序信息失败", err);
+        });
+    },
+    /**
+     * 切换 Tab
+     * @param {number} index - 选中的 Tab 索引
+     */
+    changeTab(index) {
+      if (this.tabIndex === index) return;
+      this.tabIndex = index;
+      this.loadConfig(this.tabs[index].value);
+    },
+
+    /**
+     * 加载当前配送方式配置
+     * @param {number} style - 配送类型 (0-4)
+     */
+    loadConfig(style) {
+      getShMethodConfig({ style }).then(res => {
+          if (res.code == 1 && res.data) {
+            const data = res.data;
+            this.form = {
+              id: data.id || 0,
+              style: style,
+              name: data.name || "",
+              status: data.status != null ? Number(data.status) : 1,
+              sort: data.sort != null ? Number(data.sort) : 0,
+              minAmount: data.minAmount != null ? parseFloat(data.minAmount) : 0.00,
+              minNum: data.minNum != null ? Number(data.minNum) : 0,
+              unMeet: data.unMeet != null ? Number(data.unMeet) : 0,
+              unMeetFee: data.unMeetFee != null ? parseFloat(data.unMeetFee) : 0.00,
+              startKm: data.startKm != null ? parseFloat(data.startKm) : 0.00,
+              startPrice: data.startPrice != null ? parseFloat(data.startPrice) : 0.00,
+              perKmPrice: data.perKmPrice != null ? parseFloat(data.perKmPrice) : 0.00,
+              changeRate: data.changeRate != null ? parseFloat(data.changeRate) : 0.00,
+              explains: (data.explains || []).map(exp => ({
+                ...exp,
+                color: exp.color != null ? Number(exp.color) : 1,
+                fontWeight: exp.fontWeight != null ? Number(exp.fontWeight) : 1
+              })),
+              reduceRules: (data.reduceRules || []).map(rule => ({
+                ...rule,
+                meetNum: rule.meetNum != null ? Number(rule.meetNum) : 0,
+                // 接口可能返回 10.00,展示时去掉小数点后多余的 0
+                meetAmount: rule.meetAmount != null && rule.meetAmount !== '' ? parseFloat(Number(rule.meetAmount)) : 0,
+                freeKm: rule.freeKm != null && rule.freeKm !== '' ? parseFloat(Number(rule.freeKm)) : 0,
+                sort: rule.sort != null ? Number(rule.sort) : 0
+              }))
+            }
+          } else {
+            this.$msg(res.msg || "加载失败")
+          }
+        }).catch(err => {
+          this.$msg("加载失败,请重试");
+        })
+    },
+
+    /**
+     * 添加满减规则
+     */
+    addReduceRule() {
+      this.form.reduceRules.push({
+        meetNum: 0,
+        meetAmount: 0,
+        freeKm: 0,
+        sort: this.form.reduceRules.length
+      });
+    },
+
+    /**
+     * 满减规则输入框获焦时清空,便于重新输入
+     * @param {Object} rule - 满减规则项
+     * @param {string} field - 字段名 meetNum | meetAmount | freeKm
+     */
+    clearReduceRuleInputOnFocus(rule, field) {
+      this.$set(rule, field, "");
+    },
+
+    /**
+     * 满减规则输入框失焦时规范化数值,去掉小数点后多余的 0
+     * @param {Object} rule - 满减规则项
+     * @param {string} field - 字段名 meetNum | meetAmount | freeKm
+     */
+    normalizeReduceRuleInput(rule, field) {
+      const value = rule[field];
+      if (value === "" || value == null) {
+        this.$set(rule, field, 0);
+        return;
+      }
+      const num = Number(value);
+      if (Number.isNaN(num)) {
+        this.$set(rule, field, 0);
+        return;
+      }
+      const normalized = field === "meetNum" ? parseInt(num, 10) : parseFloat(String(num));
+      this.$set(rule, field, normalized);
+    },
+
+    /**
+     * 删除满减规则
+     * @param {number} index - 规则索引
+     */
+    removeReduceRule(index) {
+      this.$util.confirmModal({ content: "确认删除该满减规则吗?" }, () => {
+        this.form.reduceRules.splice(index, 1);
+      });
+    },
+
+    /**
+     * 添加说明项
+     */
+    addExplain() {
+      if (this.form.explains.length >= 5) {
+        this.$msg("最多只能添加5条说明");
+        return;
+      }
+      this.form.explains.push({
+        explain: "",
+        color: 1, // 1: 黑色, 2: 红色, 3: 蓝色
+        fontWeight: 1, // 1: 常规, 2: 加粗
+        sort: this.form.explains.length
+      });
+    },
+
+    /**
+     * 删除说明项
+     * @param {number} index - 说明索引
+     */
+    removeExplain(index) {
+      this.$util.confirmModal({ content: "确认删除该说明吗?" }, () => {
+        this.form.explains.splice(index, 1);
+      });
+    },
+
+    /**
+     * 设置说明项颜色
+     * @param {number} index - 说明项索引
+     * @param {number} color - 颜色值 (1: 黑色, 2: 红色, 3: 蓝色)
+     */
+    setExplainColor(index, color) {
+      const exp = this.form.explains[index];
+      if (exp) {
+        exp.color = color;
+        // 强制触发 Vue 2 数组/对象响应式更新
+        this.$set(this.form.explains, index, { ...exp });
+      }
+    },
+
+    /**
+     * 设置说明项字重
+     * @param {number} index - 说明项索引
+     * @param {number} fontWeight - 字重值 (1: 常规, 2: 加粗)
+     */
+    setExplainWeight(index, fontWeight) {
+      const exp = this.form.explains[index];
+      if (exp) {
+        exp.fontWeight = fontWeight;
+        // 强制触发 Vue 2 数组/对象响应式更新
+        this.$set(this.form.explains, index, { ...exp });
+      }
+    },
+
+    /**
+     * 跳转至物流管理
+     */
+    manageWl() {
+      this.$util.pageTo({ url: "/admin/shop/wl" });
+    },
+
+    /**
+     * 保存当前配置
+     */
+    submitSave() {
+      if (this.$util.isEmpty(this.form.name)) {
+        this.$msg("请填写别名");
+        return;
+      }
+      if (this.form.name.length > 4) {
+        this.$msg("名称最多4个字");
+        return;
+      }
+
+      // 验证说明项内容
+      for (let i = 0; i < this.form.explains.length; i++) {
+        if (this.$util.isEmpty(this.form.explains[i].explain)) {
+          this.$msg(`请填写第 ${i + 1} 条说明内容`);
+          return;
+        }
+      }
+      saveShMethodConfig(this.form).then(res => {
+          if (res.code == 1) {
+            this.$msg("保存成功");
+            // 重新加载配置刷新数据
+            this.loadConfig(this.form.style);
+            // 重新加载排序和别名信息
+            this.loadSorts();
+          } else {
+            this.$msg(res.msg || "保存失败");
+          }
+        })
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.sh-method-page {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  background-color: #f5f5f5;
+}
+
+/* 顶部 Tab 栏 */
+.tab-bar {
+  display: flex;
+  background-color: #ffffff;
+  border-bottom: 1upx solid #eeeeee;
+  position: sticky;
+  top: 0;
+  z-index: 10;
+  height: 90upx;
+  align-items: center;
+}
+
+.tab-item {
+  flex: 1;
+  text-align: center;
+  font-size: 34upx;
+  color: #666666;
+  height: 90upx;
+  line-height: 90upx;
+  position: relative;
+  transition: all 0.2s ease;
+  font-weight: bold;
+  &.active {
+    color: #3385ff;
+    font-weight: bold;
+
+    &::after {
+      content: "";
+      position: absolute;
+      bottom: 0;
+      left: 50%;
+      transform: translateX(-50%);
+      width: 40upx;
+      height: 4upx;
+      background-color: #3385ff;
+      border-radius: 2upx;
+    }
+  }
+}
+
+/* 表单区域 */
+.form-scroll {
+  flex: 1;
+  overflow: hidden;
+}
+
+.form-container {
+  padding: 20upx 20upx 160upx 20upx;
+}
+
+.line-cell {
+  background-color: #ffffff;
+  border-radius: 12upx;
+  margin-bottom: 20upx;
+}
+
+.tui-title {
+  font-size: 30upx;
+  color: #333333;
+  width: 200upx;
+}
+
+.tui-input {
+  flex: 1;
+  font-size: 30upx;
+  color: #333333;
+  text-align: right;
+}
+
+.btn-group {
+  display: flex;
+  align-items: center;
+  margin-left: auto;
+}
+
+/* 跑腿自定义计费输入行 */
+.input-row {
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  flex: 1;
+}
+
+.tui-input-sm {
+  width: 120upx;
+  height: 60upx;
+  border: 1upx solid #dddddd;
+  border-radius: 8upx;
+  font-size: 28upx;
+  text-align: center;
+  color: #333333;
+  background-color: #fafafa;
+}
+
+.unit-text {
+  font-size: 26upx;
+  color: #666666;
+  margin: 0 10upx;
+}
+
+/* 模块标题 */
+.section-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin: 30upx 10upx 15upx 10upx;
+}
+
+.section-title {
+  font-size: 30upx;
+  font-weight: bold;
+  color: #333333;
+}
+
+/* 满减规则卡片 */
+.rule-list {
+  margin-bottom: 20upx;
+}
+
+.rule-card {
+  background-color: #ffffff;
+  border-radius: 12upx;
+  padding: 20upx 10upx;
+  margin-bottom: 20upx;
+  border: 1upx solid #eef2f9;
+  display: flex;
+  flex-direction: column;
+}
+
+.rule-row {
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+  font-size: 26upx;
+  color: #333333;
+}
+
+.rule-label {
+  margin: 0 8upx;
+}
+
+.rule-input {
+  width: 80upx;
+  height: 50upx;
+  border: 1upx solid #cccccc;
+  border-radius: 6upx;
+  text-align: center;
+  font-size: 26upx;
+  background-color: #fafafa;
+}
+
+.rule-input-large {
+  width: 90upx;
+  height: 50upx;
+  border: 1upx solid #cccccc;
+  border-radius: 6upx;
+  text-align: center;
+  font-size: 26upx;
+  background-color: #fafafa;
+}
+
+.rule-del-btn {
+  margin-top: 15upx;
+  align-self: flex-end; /* 靠右边 */
+  background-color: #fff5f5 !important;
+  color: #ff3841 !important;
+  border: 1upx solid #ffcccc !important;
+}
+
+/* 提示文字 */
+.notice-text {
+  font-size: 32upx;
+  color: #ff6900;
+  line-height: 36upx;
+  padding: 15upx 20upx;
+  background-color: #fffaf5;
+  border-radius: 8upx;
+  border: 1upx solid #ffe8d6;
+  margin: 10upx 0upx;
+}
+
+/* 说明项区域圈起来 */
+.explain-section-wrapper,
+.rule-section-wrapper {
+  background-color: #ffffff;
+  border-radius: 12upx;
+  padding: 24upx;
+  margin-top: 30upx;
+  border: 1upx solid #eef2f9;
+  box-shadow: 0 2upx 8upx rgba(0, 0, 0, 0.02);
+}
+
+.no-margin {
+  margin-top: 0 !important;
+  margin-left: 0 !important;
+  margin-right: 0 !important;
+  margin-bottom: 20upx !important;
+}
+
+/* 说明卡片 */
+.explain-list {
+  margin-bottom: 20upx;
+}
+
+.explain-card {
+  background-color: #ffffff;
+  border-radius: 12upx;
+  padding: 20upx;
+  margin-bottom: 20upx;
+  border: 1upx solid #eef2f9;
+}
+
+.explain-textarea {
+  width: 100%;
+  height: 135upx;
+  font-size: 30upx;
+  color: #333333;
+  background-color: #fafafa;
+  border: 1upx solid #eeeeee;
+  border-radius: 8upx;
+  padding: 15upx;
+  box-sizing: border-box;
+  margin-bottom: 15upx;
+}
+
+.explain-actions {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.action-group {
+  display: flex;
+  align-items: center;
+}
+
+.explain-del-btn {
+  margin-left: auto;
+  background-color: #fff5f5 !important;
+  color: #ff3841 !important;
+  border: 1upx solid #ffcccc !important;
+}
+
+/* 文本样式控制 */
+.text-red {
+  color: #ff3841 !important;
+}
+
+.text-blue {
+  color: #3385ff !important;
+}
+
+.text-bold {
+  font-weight: bold !important;
+}
+
+.no-data-tip {
+  font-size: 26upx;
+  color: #999999;
+  text-align: center;
+  padding: 40upx 0;
+}
+
+/* 底部保存按钮 */
+.footer-btn-wrap {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  background-color: #ffffff;
+  padding: 20upx 30upx;
+  border-top: 1upx solid #eeeeee;
+  z-index: 10;
+  display: flex;
+  justify-content: space-between; /* 居左边展示顺序,居右边保存按钮 */
+  align-items: center;
+}
+
+.sort-indicator {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  flex: 1;
+  margin-right: 20upx;
+}
+
+.sort-label {
+  font-size: 28upx;
+  color: #999999;
+}
+
+.sort-value {
+  font-size: 28upx;
+  color: #333333;
+  margin-top: 4upx;
+}
+
+.footer-btn-wrap .admin-button-com.big {
+  width: 180upx !important; /* 按钮长一些 */
+  margin: 0 !important; /* 清除默认居中 margin */
+}
+</style>

+ 302 - 0
hdApp/src/admin/shop/wl.vue

@@ -0,0 +1,302 @@
+<!--
+  物流选项管理
+  用途:配送方式-物流(style=3) 关联的物流名称维护
+-->
+<template>	<div class="app-content" @click="globalClick">
+		<div class="list-wrap">
+			<block v-if="!$util.isEmpty(list.data)">
+				<div class="list" v-for="(item, index) in list.data" :key="index">
+					<div class="list-index">{{ item.id }}</div>
+					<div class="list-det" @click="editItem(item)">
+						<div class="app-size-28 app-color-0">{{ item.name || '' }}</div>
+						<div class="list-operate">
+							<div class="operate-det">
+								<span>顺序 {{ item.inTurn }}</span>
+							</div>
+							<view class="operate-icon"><i class="iconfont iconbianji" style="font-size:38upx;"></i></view>
+							<view class="operate-icon" @click.stop="delWlFn(item)"><i class="iconfont iconshanchu1" style="font-size:38upx;"></i></view>
+						</div>
+					</div>
+				</div>
+				<operate-module v-if="operateShow" :top="operateTop" :right="30" @clickFn="operateList" :btnData="btnData" />
+			</block>
+			<block v-else>
+				<app-wrapper-empty title="暂无数据" :is-empty="$util.isEmpty(list.data)" />
+			</block>
+		</div>
+		<div class="app-footer">
+			<button class="admin-button-com big blue" @click="addCategoryFn">添加物流</button>
+		</div>
+		<modal-module :show="modifyModal" :maskClosable="false" @cancel="modalCancel" @click="replaceModalClick" :title="modifyTitle" padding="30rpx 30rpx">
+			<template v-slot:content>
+				<div class="app-modal-input-wrap modify-modal">
+					<div class="inp-list-line">
+						<div class="line-label">名称</div>
+						<div class="line-input">
+							<input type="text" v-model="replaceForm.name" :adjust-position="false" class="inp-input" placeholder="请填写名称" />
+						</div>
+					</div>
+					<div class="inp-list-line">
+						<div class="line-label">顺序</div>
+						<div class="line-input">
+							<input type="number" v-model="replaceForm.inTurn" @focus="replaceForm.inTurn=''" :adjust-position="false" class="inp-input" placeholder="越大越靠前" />
+						</div>
+					</div>
+				</div>
+			</template>
+		</modal-module>
+		<modal-module :show="delModal" :maskClosable="false" @cancel="modalCancel" @click="delModalClick" content="确定删除?" color="#333" :size="32" padding="30upx 30upx"></modal-module>
+	</div>
+</template>
+<script>
+import ModalModule from '@/components/plugin/modal'
+import AppUploader from '@/components/app-uploader'
+import OperateModule from '@/admin/home/components/module/operate'
+import AppWrapperEmpty from '@/components/app-wrapper-empty'
+import { list } from '@/mixins'
+import { getWlList, delWl, addWl, updateWl } from '@/api/shop'
+export default {
+	name: 'item-class-list',
+	components: {
+		ModalModule,
+		AppUploader,
+		OperateModule,
+		AppWrapperEmpty
+	},
+	mixins: [list],
+	data() {
+		return {
+			modifyModal: false,
+			modifyTitle: '添加',
+			replaceForm: {
+				id:0,
+				name: '',
+				inTurn: 100
+			},
+			delModal: false,
+			operateShow: false,
+			operateData: {},
+			operateIndex: null,
+			operateTop: 0
+		}
+	},
+	computed: {
+		btnData() {
+			return [
+				{
+					text: '编辑',
+					icon: 'iconbianji',
+					click: () => {
+						this.modifyModal = true
+						this.modifyTitle = '编辑'
+						this.modifyForm = {
+							name: this.operateData.name,
+							id: this.operateData.id,
+							inTurn: this.operateData.inTurn
+						}
+					}
+				},
+				{
+					text: '删除',
+					icon: 'iconshanchu1',
+					click: () => {
+						this.delModal = true
+					}
+				}
+			]
+		}
+	},
+	onPullDownRefresh() {
+		this.resetList()
+		this.getWlData().then(res => {
+			uni.stopPullDownRefresh()
+		})
+	},
+	onReachBottom() {
+		if (!this.list.finished) {
+			this.getWlData().then(res => {
+				uni.stopPullDownRefresh()
+			})
+		} else {
+			uni.stopPullDownRefresh()
+		}
+	},
+	onLoad() {
+	},
+	methods: {
+		delWlFn(item){
+			let that = this
+			that.$util.confirmModal({content:'确认删除?'},() => {
+				delWl({id:item.id}).then(res=>{
+					if(res.code == 1){
+						that.$msg('删除成功')
+						that.resetList()
+						that.getWlData()
+					}
+				})
+			})
+		},
+		editItem(item){
+				this.modifyModal = true
+				this.modifyTitle = '编辑'
+				this.replaceForm = {
+                    id:item.id,
+					name: item.name,
+					inTurn: item.inTurn
+				}			
+		},
+		operateList(index){
+			if(index === 0){
+				this.modifyModal = true
+				this.modifyTitle = '编辑'
+				this.replaceForm = {
+                    id:this.operateData.id,
+					name: this.operateData.name,
+					inTurn: this.operateData.inTurn
+				}
+			}else if(index === 1) {
+				this.delModal = true
+			}
+		},
+		async init() {
+			this.getWlData()
+		},
+		getWlData() {
+			return getWlList({page: this.list.page}).then(res => {
+				this.completes(res)
+			})
+		},
+		replaceClass() {
+			if (!this.replaceForm.name) {
+				this.$msg('请填写物流名称')
+				return false
+			}
+			let host = this.replaceForm.id == 0 ?  addWl : updateWl;
+			let form = JSON.parse(JSON.stringify(this.replaceForm))
+			host({ ...form }).then(res => {
+				this.$msg('操作成功')
+				this.modalCancel()
+				this.operateCancle()
+				setTimeout(() => {
+					this.resetList()
+					this.getWlData()
+				}, 1000)
+			})
+		},
+		addCategoryFn() {
+			this.resetForm()
+			this.modifyModal = true
+			this.modifyTitle = '新增'
+		},
+		resetForm() {
+			this.replaceForm = {
+				name: '',
+				inTurn: 0,
+                id:0
+			}
+		},
+		replaceModalClick(e) {
+			if (e.index === 0) {
+				this.modalCancel()
+			} else {
+				this.replaceClass()
+			}
+		},
+		modalCancel() {
+			this.delModal = false
+			this.modifyModal = false
+		},
+		switchChangeFn(e) {
+			this.replaceForm.status = e.detail.value ? 1 : 0
+		},
+		delModalClick(e) {
+			this.modalCancel()
+		},
+		operateCancle() {
+			this.operateData = {}
+			this.operateIndex = null
+			this.operateShow = false
+		},
+        globalClick(){
+            this.operateCancle()
+        }
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+	.app-content {
+		min-height: calc(100vh - 100upx);
+		padding-bottom: 100upx;
+	}
+	.list-wrap {
+		position: relative;
+		background-color: #fff;
+		.list {
+			height: 130upx;
+			@include disFlex(center, flex-start);
+			margin: 0 30upx 0 30upx;
+			color: $fontColor3;
+			border-bottom: 1upx solid $borderColor;
+			.list-index {
+				width: 80upx;
+			}
+			.list-img {
+				img {
+					height: 100%;
+				}
+			}
+			.list-det {
+				width: calc(100% - 50upx);
+				margin-left: 10upx;
+				& > div {
+					margin-top: 14upx;
+					&:first-child {
+						margin-top: 0;
+					}
+				}
+				.list-operate {
+					@include disFlex(center, space-between);
+					.operate-det {
+						& > span {
+							margin-left: 60upx;
+							&:first-child {
+								margin-left: 0;
+							}
+						}
+					}
+					.add-price{
+						text-decoration:underline;
+						font-size:30upx;
+						margin-left:10upx;
+					}
+					.operate-icon{
+						margin-left:5upx;
+					}
+				}
+			}
+		}
+	}
+	.app-footer {
+		justify-content: flex-end;
+		.admin-button-com {
+			width: 200upx;
+			margin-right: 30upx;
+		}
+	}
+	.modify-modal {
+		.prompt-text {
+			color: red;
+			position: relative;
+			right: 52upx;
+			bottom: 24upx;
+		}
+		.line-label {
+			width: 140upx;
+			flex-shrink: 0;
+		}
+		.switch-wrap {
+			margin-top: 30upx;
+		}
+	}
+</style>

+ 20 - 0
hdApp/src/api/sh-method/index.js

@@ -0,0 +1,20 @@
+/**
+ * 配送方式配置 API
+ * 用途:花店应用-门店设置-配送方式设置页
+ */
+import https from '@/plugins/luch-request_0.0.7/request'
+
+/** 获取指定 style 的配送配置 */
+export const getShMethodConfig = data => {
+  return https.get('/sh-method/get-config', data)
+}
+
+/** 获取各配送方式排序与别名 */
+export const getShMethodSorts = () => {
+  return https.get('/sh-method/get-sorts')
+}
+
+/** 保存配送配置 */
+export const saveShMethodConfig = data => {
+  return https.post('/sh-method/save-config', data)
+}

+ 25 - 0
hdApp/src/api/shop/index.js

@@ -145,4 +145,29 @@ export const getRechargeSetting = data => {
 //充值设置
 export const saveRechargeSetting = data => {
 	return https.post('/shop/save-recharge-setting', data)
+}
+
+/** 物流选项:更新 */
+export const updateWl = data => {
+	return https.post('/wl/update', data)
+}
+
+/** 物流选项:新增 */
+export const addWl = data => {
+	return https.post('/wl/add', data)
+}
+
+/** 物流选项:删除 */
+export const delWl = data => {
+	return https.get('/wl/del-wl', data)
+}
+
+/** 物流选项:全部 */
+export const getAllWl = data => {
+	return https.get('/wl/get-all-wl', data)
+}
+
+/** 物流选项:列表 */
+export const getWlList = data => {
+	return https.get('/wl/list', data)
 }

+ 3 - 1
hdApp/src/pages.json

@@ -14,6 +14,7 @@
 		{ "path": "admin/home/order", "style": { "navigationBarTitleText": "订单", "enablePullDownRefresh": true, "navigationStyle": "custom" } },
 		{ "path": "admin/home/member", "style": { "navigationBarTitleText": "客户", "enablePullDownRefresh": true, "navigationStyle": "custom" } },
 		{ "path": "admin/home/apply", "style": { "navigationBarTitleText": "应用", "enablePullDownRefresh": true } },
+		{ "path": "admin/home/shMethod", "style": { "navigationBarTitleText": "配送方式" } },
 		{ "path": "admin/home/close","style": {"navigationBarTitleText": "申请注销账号"}},
 		{"path": "admin/home/register","style": {"navigationBarTitleText": "服务协议"}},
 		{"path": "admin/home/privacy","style": {"navigationBarTitleText": "隐私政策"}},
@@ -308,7 +309,8 @@
 				{"path": "addressList","style": {"navigationBarTitleText": "请选择地址"}},
 				{"path": "addressAdd","style": {"navigationBarTitleText": "添加地址"}},
 				{"path": "addressUpdate","style": {"navigationBarTitleText": "修改地址"}},
-				{"path": "myShop","style": {"navigationBarTitleText": "我的门店"}}
+				{"path": "myShop","style": {"navigationBarTitleText": "我的门店"}},
+				{"path": "wl","style": {"navigationBarTitleText": "物流选项"}}
 			]
 		},
 		{