rk
2026-04-10 56bc142d33106db9f226abe39f60d0059d702338
admin/src/components/business/OperaCityPriceRuleWindow.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,349 @@
<template>
  <GlobalWindow
    :title="title"
    :visible.sync="visible"
    width="60%"
  >
    <el-tabs v-model="activeTab">
      <el-tab-pane label="就地寄存" name="local">
        <div class="price-tip">根据行李尺寸以及存放天数收费(不足一天按一天计价)</div>
        <div class="price-items">
          <div class="price-item" v-for="(item, index) in jiudiTableData" :key="index">
            <span class="price-label">其中{{item.categoryName}}每天收费</span>
            <el-input style="width: 200px;" v-model="item.unitPrice" type="number" placeholder="请输入"></el-input>
            <span class="price-unit">元</span>
          </div>
        </div>
      </el-tab-pane>
      <el-tab-pane label="异地寄存" name="remote">
        <div class="remote-config-title">异地寄送配置</div>
        <div class="remote-header">
          <span class="remote-tip">根据行李尺寸、配送里程及配送价格收取费用(不足1公里按1公里计价)</span>
        </div>
        <el-table :data="remoteTableData" border stripe>
          <el-table-column prop="categoryName" label="行李尺寸"></el-table-column>
          <el-table-column label="起送里程(公里)">
            <template slot-scope="{row}">
              <el-input v-model="row.startDistance" type="number" placeholder="请输入"></el-input>
            </template>
          </el-table-column>
          <el-table-column label="起送价格(元)">
            <template slot-scope="{row}">
              <el-input v-model="row.startPrice" type="number" placeholder="请输入"></el-input>
            </template>
          </el-table-column>
          <el-table-column label="续送里程(公里)">
            <template slot-scope="{row}">
              <el-input v-model="row.extraDistance" type="number" placeholder="请输入"></el-input>
            </template>
          </el-table-column>
          <el-table-column label="续送价格(元)">
            <template slot-scope="{row}">
              <el-input v-model="row.extraPrice" type="number" placeholder="请输入"></el-input>
            </template>
          </el-table-column>
        </el-table>
      </el-tab-pane>
      <el-tab-pane label="预计时效" name="time">
        <div class="remote-config-title">预计时效规则</div>
        <div class="remote-header">
          <span class="remote-tip">根据配送里程预计配送时长并展示在小程序下单页面</span>
        </div>
        <el-table :data="timeTableData" border stripe>
          <el-table-column label="起送里程(公里)">
            <template slot-scope="{row}">
              <el-input v-model="row.startDistance" type="number" placeholder="请输入"></el-input>
            </template>
          </el-table-column>
          <el-table-column label="起送时长(小时)">
            <template slot-scope="{row}">
              <el-input v-model="row.startTime" type="number" placeholder="请输入"></el-input>
            </template>
          </el-table-column>
          <el-table-column label="续送里程(公里)">
            <template slot-scope="{row}">
              <el-input v-model="row.continueDistance" type="number" placeholder="请输入"></el-input>
            </template>
          </el-table-column>
          <el-table-column label="续送时长(小时)">
            <template slot-scope="{row}">
              <el-input v-model="row.continueTime" type="number" placeholder="请输入"></el-input>
            </template>
          </el-table-column>
        </el-table>
      </el-tab-pane>
      <el-tab-pane label="门店注册押金" name="deposit">
        <div class="price-tip">根据所在城市以及注册类型不同,平台收取不同数额押金。押金会在门店退网后线下原额退还(门店线上微信支付的押金因微信官方要求具有时效性)</div>
        <div class="price-items">
          <div class="price-item" v-for="(item, index) in storeDepositData" :key="index">
            <span class="price-label">其中{{item.fieldTypeName}}收取押金</span>
            <el-input style="width: 200px;" v-model="item.depositAmount" type="number" placeholder="请输入"></el-input>
            <span class="price-unit">元</span>
          </div>
        </div>
      </el-tab-pane>
      <el-tab-pane label="分成比例" name="share">
        <div class="price-tip">根据订单中不同运营角色而独立配置不同分成比例</div>
        <div class="price-items">
          <div class="price-item" v-for="(item, index) in shareTableData" :key="index">
            <span class="price-label">{{item.fieldType === 4 ? '在订单中作为配送员时,' : '其中门店类型为'}}{{item.fieldTypeName}}分成比例为</span>
            <el-input style="width: 100px;" v-model="item.ratio" type="number" placeholder="请输入"></el-input>
            <span class="price-unit">%</span>
          </div>
        </div>
      </el-tab-pane>
    </el-tabs>
    <div slot="footer" class="dialog-footer">
      <el-button @click="visible = false">取消</el-button>
      <el-button type="primary" @click="confirm" :loading="isWorking">确定</el-button>
    </div>
  </GlobalWindow>
</template>
<script>
import BaseOpera from '@/components/base/BaseOpera'
import GlobalWindow from '@/components/common/GlobalWindow'
import {
  localStorage,
  remoteDelivery,
  estimatedDelivery,
  storeDeposit,
  revenueShare,
  batchSave,
  batchSaveRemoteDelivery,
  batchSaveEstimatedDelivery,
  batchSaveStoreDeposit,
  batchSaveRevenueShare
} from '@/api/business/pricingRule'
export default {
  name: 'OperaCityPriceRuleWindow',
  extends: BaseOpera,
  components: { GlobalWindow },
  data () {
    return {
      activeTab: 'local',
      jiudiTableData: [],
      remoteTableData: [],
      timeTableData: [],
      storeDepositData: [],
      shareTableData: [],
      form: {
        cityId: null
      }
    }
  },
  created () {
    this.config({
      api: '/business/pricingRule',
      'field.id': 'id'
    })
  },
  methods: {
    open (title, target) {
      this.title = title
      this.visible = true
      this.activeTab = 'local'
      this.jiudiTableData = []
      this.remoteTableData = []
      this.timeTableData = []
      this.storeDepositData = []
      this.shareTableData = []
      this.form = {
        cityId: target
      }
      this.fetchPriceRule()
      this.fetchRemoteDelivery()
      this.fetchEstimatedDelivery()
      this.fetchStoreDeposit()
      this.fetchRevenueShare()
    },
    // æŸ¥è¯¢å°±åœ°å­˜å–规则列表
    fetchPriceRule () {
      localStorage(this.form.cityId)
        .then(data => {
          this.jiudiTableData = data
        })
        .catch(e => {
          this.$tip.apiFailed(e)
        })
    },
    // æŸ¥è¯¢å¼‚地寄送规则列表
    fetchRemoteDelivery () {
      remoteDelivery(this.form.cityId)
        .then(data => {
          this.remoteTableData = data
        })
        .catch(e => {
          this.$tip.apiFailed(e)
        })
    },
    // æŸ¥è¯¢å¼‚地寄送规则列表
    fetchEstimatedDelivery () {
      estimatedDelivery(this.form.cityId)
        .then(data => {
          this.timeTableData = [
            data
          ]
        })
        .catch(e => {
          this.$tip.apiFailed(e)
        })
    },
    // æŸ¥è¯¢é—¨åº—注册押金规则列表
    fetchStoreDeposit () {
      storeDeposit(this.form.cityId)
        .then(data => {
          this.storeDepositData = data
        })
        .catch(e => {
          this.$tip.apiFailed(e)
        })
    },
    // æŸ¥è¯¢åˆ†æˆæ¯”例规则列表
    fetchRevenueShare () {
      revenueShare(this.form.cityId)
        .then(data => {
          this.shareTableData = data
        })
        .catch(e => {
          this.$tip.apiFailed(e)
        })
    },
    // ä¿å­˜æ‰€æœ‰è§„则
    confirm () {
      if (!this.validateLocalStorage()) return
      if (!this.validateRemoteDelivery()) return
      if (!this.validateEstimatedDelivery()) return
      if (!this.validateStoreDeposit()) return
      if (!this.validateRevenueShare()) return
      this.isWorking = true
      Promise.all([
        batchSave({ cityId: this.form.cityId, items: this.jiudiTableData }),
        batchSaveRemoteDelivery({ cityId: this.form.cityId, items: this.remoteTableData }),
        batchSaveEstimatedDelivery({ cityId: this.form.cityId, ...this.timeTableData[0] }),
        batchSaveStoreDeposit({ cityId: this.form.cityId, items: this.storeDepositData }),
        batchSaveRevenueShare({ cityId: this.form.cityId, items: this.shareTableData })
      ])
        .then(() => {
          this.visible = false
          this.$tip.apiSuccess('保存成功')
          this.$emit('success')
        })
        .catch(e => {
          this.$tip.apiFailed(e)
        })
        .finally(() => {
          this.isWorking = false
        })
    },
    // æ ¡éªŒå°±åœ°å¯„å­˜
    validateLocalStorage () {
      for (const item of this.jiudiTableData) {
        if (item.unitPrice === null || item.unitPrice === undefined || item.unitPrice === '') {
          this.$message.error('请填写所有就地寄存的收费金额')
          return false
        }
      }
      return true
    },
    // æ ¡éªŒå¼‚地寄存
    validateRemoteDelivery () {
      for (const item of this.remoteTableData) {
        if (item.startDistance === null || item.startDistance === undefined || item.startDistance === '' ||
            item.startPrice === null || item.startPrice === undefined || item.startPrice === '' ||
            item.extraDistance === null || item.extraDistance === undefined || item.extraDistance === '' ||
            item.extraPrice === null || item.extraPrice === undefined || item.extraPrice === '') {
          this.$message.error('请填写所有异地寄送的配置信息')
          return false
        }
      }
      return true
    },
    // æ ¡éªŒé¢„计时效
    validateEstimatedDelivery () {
      const item = this.timeTableData[0]
      if (!item || item.startDistance === null || item.startDistance === undefined || item.startDistance === '' ||
          item.startTime === null || item.startTime === undefined || item.startTime === '' ||
          item.continueDistance === null || item.continueDistance === undefined || item.continueDistance === '' ||
          item.continueTime === null || item.continueTime === undefined || item.continueTime === '') {
        this.$message.error('请填写预计时效的所有配置信息')
        return false
      }
      return true
    },
    // æ ¡éªŒé—¨åº—注册押金
    validateStoreDeposit () {
      for (const item of this.storeDepositData) {
        if (item.depositAmount === null || item.depositAmount === undefined || item.depositAmount === '') {
          this.$message.error('请填写所有门店注册押金的金额')
          return false
        }
      }
      return true
    },
    // æ ¡éªŒåˆ†æˆæ¯”例
    validateRevenueShare () {
      for (const item of this.shareTableData) {
        if (item.ratio === null || item.ratio === undefined || item.ratio === '') {
          this.$message.error('请填写所有分成比例')
          return false
        }
      }
      return true
    }
  }
}
</script>
<style scoped>
.price-tip {
  margin-bottom: 20px;
  color: #666;
  font-size: 14px;
}
.price-items {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.price-item {
  display: flex;
  align-items: center;
  gap: 10px;
}
.price-label {
  flex-shrink: 0;
  font-weight: 500;
}
.price-unit {
  color: #666;
  white-space: nowrap;
}
.dialog-footer {
  text-align: right;
  padding: 0 20px;
  box-sizing: border-box;
  border-top: 1px solid #eee;
}
.remote-config-title {
  font-weight: bold;
  margin-bottom: 15px;
}
.remote-header {
  display: flex;
  align-items: center;
  gap: 15px;
  margin-bottom: 15px;
}
.remote-tip {
  color: #666;
  font-size: 14px;
}
.remote-pagination {
  margin-top: 15px;
  text-align: right;
}
</style>