|
|
@@ -0,0 +1,805 @@
|
|
|
+<!--
|
|
|
+ 花掌柜 PC 端配送方式设置
|
|
|
+ 布局参考设计稿:上方基础信息表单 + 下方 2×2 规则卡片;接口对齐小程序 psMethod
|
|
|
+-->
|
|
|
+<template>
|
|
|
+ <div class="app-list-content ps-method-page" v-loading="loading">
|
|
|
+ <el-tabs class="ps-tabs" v-model="tabIndex" @tab-click="onTabClick">
|
|
|
+ <el-tab-pane
|
|
|
+ v-for="(tab, index) in tabs"
|
|
|
+ :key="tab.value"
|
|
|
+ :label="tab.name"
|
|
|
+ :name="String(index)"
|
|
|
+ ></el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+
|
|
|
+ <div class="ps-body">
|
|
|
+ <!-- 上方:基础配置(标签左、控件右,整行分隔) -->
|
|
|
+ <div class="base-panel">
|
|
|
+ <div class="base-row">
|
|
|
+ <div class="base-lab">别名</div>
|
|
|
+ <div class="base-val">
|
|
|
+ <el-input v-model="form.name" maxlength="4" size="small" placeholder="最多4个字" style="width: 200px;"></el-input>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <template v-if="form.style === 2">
|
|
|
+ <div class="base-row">
|
|
|
+ <div class="base-lab">算运费方式</div>
|
|
|
+ <div class="base-val">
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ class="seg-btn"
|
|
|
+ :class="{ active: form.calcType == 0 }"
|
|
|
+ @click="form.calcType = 0"
|
|
|
+ >用跑腿</button>
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ class="seg-btn"
|
|
|
+ :class="{ active: form.calcType == 1 }"
|
|
|
+ @click="form.calcType = 1"
|
|
|
+ >用自定义</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="base-row">
|
|
|
+ <div class="base-lab">跑腿提供方</div>
|
|
|
+ <div class="base-val">
|
|
|
+ <span class="provider-text">{{ deliveryBindText }}</span>
|
|
|
+ <el-button type="primary" size="mini" plain @click="onManageProvider">管理</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <div class="base-row">
|
|
|
+ <div class="base-lab">状态</div>
|
|
|
+ <div class="base-val">
|
|
|
+ <el-radio-group v-model="form.status" @change="onStatusRadio">
|
|
|
+ <el-radio :label="1">启用</el-radio>
|
|
|
+ <el-radio :label="0">禁用</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="base-row">
|
|
|
+ <div class="base-lab">顺序</div>
|
|
|
+ <div class="base-val">
|
|
|
+ <el-input v-model="form.sort" type="number" size="small" style="width: 100px;"></el-input>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 送货 / 快递门槛 -->
|
|
|
+ <template v-if="form.style === 0 || form.style === 4">
|
|
|
+ <div class="base-row">
|
|
|
+ <div class="base-lab">最低消费金额</div>
|
|
|
+ <div class="base-val">
|
|
|
+ <el-input v-model="form.minAmount" size="small" placeholder="不限制填0" style="width: 140px;"></el-input>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="base-row">
|
|
|
+ <div class="base-lab">最低消费数量</div>
|
|
|
+ <div class="base-val">
|
|
|
+ <el-input v-model="form.minNum" type="number" size="small" placeholder="不限制填0" style="width: 140px;"></el-input>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="base-row">
|
|
|
+ <div class="base-lab">不满条件</div>
|
|
|
+ <div class="base-val">
|
|
|
+ <button type="button" class="seg-btn" :class="{ active: form.unMeet == 0 }" @click="form.unMeet = 0">收运费</button>
|
|
|
+ <button type="button" class="seg-btn" :class="{ active: form.unMeet == 2 }" @click="form.unMeet = 2">收包装费</button>
|
|
|
+ <button type="button" class="seg-btn" :class="{ active: form.unMeet == 1 }" @click="form.unMeet = 1">不能买</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="base-row" v-if="form.unMeet != 1">
|
|
|
+ <div class="base-lab">{{ form.unMeet == 2 ? '包装费' : '运费' }}</div>
|
|
|
+ <div class="base-val">
|
|
|
+ <el-input v-model="form.unMeetFee" size="small" style="width: 140px;"></el-input>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <div class="base-row" v-if="form.style === 3">
|
|
|
+ <div class="base-lab">配送物流</div>
|
|
|
+ <div class="base-val">
|
|
|
+ <span class="provider-text">请在小程序维护</span>
|
|
|
+ <el-button type="primary" size="mini" plain @click="onManageWl">管理</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="base-row last">
|
|
|
+ <div class="base-lab">显示顺序</div>
|
|
|
+ <div class="base-val order-preview">{{ sortedMethodsText }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 下方:2×2 规则区(跑腿 Tab);其它 Tab 仅说明占满 -->
|
|
|
+ <div class="rule-grid" v-if="form.style === 2">
|
|
|
+ <!-- 左上:花束运费 -->
|
|
|
+ <div class="rule-card">
|
|
|
+ <div class="rule-title">花束运费</div>
|
|
|
+ <div class="rule-line">
|
|
|
+ <span>花束包配送的</span>
|
|
|
+ <el-input v-model="form.hsFreeKm" size="mini" class="num-input"></el-input>
|
|
|
+ <span>公里内免费配送</span>
|
|
|
+ </div>
|
|
|
+ <div class="rule-line">
|
|
|
+ <span>{{ form.hsFreeKm || 0 }}公里以上 每公里加收</span>
|
|
|
+ <el-input v-model="form.hsPerKmPrice" size="mini" class="num-input"></el-input>
|
|
|
+ <span>元</span>
|
|
|
+ </div>
|
|
|
+ <div class="rule-tip">没有包配送的,按实际跑腿费或自定义计费规则</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 右上:花材满减 -->
|
|
|
+ <div class="rule-card">
|
|
|
+ <div class="rule-head">
|
|
|
+ <div class="rule-title">花材满减规则</div>
|
|
|
+ <el-button type="primary" size="mini" @click="addReduceRule">添加项</el-button>
|
|
|
+ </div>
|
|
|
+ <div v-if="form.reduceRules && form.reduceRules.length">
|
|
|
+ <div v-for="(rule, index) in form.reduceRules" :key="'r-' + index" class="rule-line">
|
|
|
+ <span>满</span>
|
|
|
+ <el-input
|
|
|
+ v-model="rule.meetNum"
|
|
|
+ size="mini"
|
|
|
+ class="num-input-sm"
|
|
|
+ @focus="clearReduceRuleInputOnFocus(rule, 'meetNum')"
|
|
|
+ @blur="normalizeReduceRuleInput(rule, 'meetNum')"
|
|
|
+ ></el-input>
|
|
|
+ <span>扎,</span>
|
|
|
+ <el-input
|
|
|
+ v-model="rule.meetAmount"
|
|
|
+ size="mini"
|
|
|
+ class="num-input"
|
|
|
+ @focus="clearReduceRuleInputOnFocus(rule, 'meetAmount')"
|
|
|
+ @blur="normalizeReduceRuleInput(rule, 'meetAmount')"
|
|
|
+ ></el-input>
|
|
|
+ <span>元,</span>
|
|
|
+ <el-input
|
|
|
+ v-model="rule.freeKm"
|
|
|
+ size="mini"
|
|
|
+ class="num-input-sm"
|
|
|
+ @focus="clearReduceRuleInputOnFocus(rule, 'freeKm')"
|
|
|
+ @blur="normalizeReduceRuleInput(rule, 'freeKm')"
|
|
|
+ ></el-input>
|
|
|
+ <span>公里内免费配送</span>
|
|
|
+ <a class="link-del" @click="removeReduceRule(index)">删除</a>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="empty-tip">暂无满减规则,点击添加项</div>
|
|
|
+ <div class="rule-tip">花材不符合满减规则时,将通过跑腿计算运费;跑腿无法使用时,将使用自定义计费</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 左下:自定义计费 -->
|
|
|
+ <div class="rule-card">
|
|
|
+ <div class="rule-title">
|
|
|
+ 自定义计费
|
|
|
+ <span class="badge" v-if="form.calcType == 1">当前生效</span>
|
|
|
+ <span class="badge gray" v-else>异常兜底</span>
|
|
|
+ </div>
|
|
|
+ <div class="rule-line">
|
|
|
+ <span class="fixed-lab">起步价</span>
|
|
|
+ <el-input v-model="form.startKm" size="mini" class="num-input-sm"></el-input>
|
|
|
+ <span>km内</span>
|
|
|
+ <el-input v-model="form.startPrice" size="mini" class="num-input"></el-input>
|
|
|
+ <span>元</span>
|
|
|
+ </div>
|
|
|
+ <div class="rule-line">
|
|
|
+ <span class="fixed-lab">超出起步</span>
|
|
|
+ <span>每公里加</span>
|
|
|
+ <el-input v-model="form.perKmPrice" size="mini" class="num-input"></el-input>
|
|
|
+ <span>元</span>
|
|
|
+ </div>
|
|
|
+ <div class="rule-line">
|
|
|
+ <span class="fixed-lab">临时涨价</span>
|
|
|
+ <el-input v-model="form.changeRate" size="mini" class="num-input"></el-input>
|
|
|
+ <span>% (降价请填负数)</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 右下:说明 -->
|
|
|
+ <div class="rule-card">
|
|
|
+ <div class="rule-head">
|
|
|
+ <div class="rule-title">说明</div>
|
|
|
+ <el-button type="primary" size="mini" @click="addExplain">添加</el-button>
|
|
|
+ </div>
|
|
|
+ <div v-if="form.explains && form.explains.length" class="explain-list">
|
|
|
+ <div v-for="(exp, index) in form.explains" :key="'e-' + index" class="explain-item">
|
|
|
+ <el-input
|
|
|
+ type="textarea"
|
|
|
+ v-model="exp.explain"
|
|
|
+ :maxlength="50"
|
|
|
+ :rows="2"
|
|
|
+ placeholder="最多50字"
|
|
|
+ class="explain-textarea"
|
|
|
+ :class="{
|
|
|
+ 'text-red': exp.color == 2,
|
|
|
+ 'text-blue': exp.color == 3,
|
|
|
+ 'text-bold': exp.fontWeight == 2
|
|
|
+ }"
|
|
|
+ ></el-input>
|
|
|
+ <div class="explain-actions">
|
|
|
+ <button type="button" class="seg-btn mini" :class="{ active: exp.color == 1 }" @click="setExplainColor(index, 1)">黑色</button>
|
|
|
+ <button type="button" class="seg-btn mini" :class="{ active: exp.color == 2 }" @click="setExplainColor(index, 2)">红色</button>
|
|
|
+ <button type="button" class="seg-btn mini" :class="{ active: exp.color == 3 }" @click="setExplainColor(index, 3)">蓝色</button>
|
|
|
+ <button type="button" class="seg-btn mini" :class="{ active: exp.fontWeight == 1 }" @click="setExplainWeight(index, 1)">常规</button>
|
|
|
+ <button type="button" class="seg-btn mini" :class="{ active: exp.fontWeight == 2 }" @click="setExplainWeight(index, 2)">加粗</button>
|
|
|
+ <a class="link-del" @click="removeExplain(index)">删除</a>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="empty-tip">暂无说明,点击添加</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 非跑腿:说明单独一块 -->
|
|
|
+ <div class="rule-grid single" v-else>
|
|
|
+ <div class="rule-card explain-card-half">
|
|
|
+ <div class="rule-head">
|
|
|
+ <div class="rule-title">说明</div>
|
|
|
+ <el-button type="primary" size="mini" @click="addExplain">添加</el-button>
|
|
|
+ </div>
|
|
|
+ <div v-if="form.explains && form.explains.length" class="explain-list">
|
|
|
+ <div v-for="(exp, index) in form.explains" :key="'e-' + index" class="explain-item">
|
|
|
+ <el-input
|
|
|
+ type="textarea"
|
|
|
+ v-model="exp.explain"
|
|
|
+ :maxlength="50"
|
|
|
+ :rows="2"
|
|
|
+ placeholder="最多50字"
|
|
|
+ class="explain-textarea"
|
|
|
+ :class="{
|
|
|
+ 'text-red': exp.color == 2,
|
|
|
+ 'text-blue': exp.color == 3,
|
|
|
+ 'text-bold': exp.fontWeight == 2
|
|
|
+ }"
|
|
|
+ ></el-input>
|
|
|
+ <div class="explain-actions">
|
|
|
+ <button type="button" class="seg-btn mini" :class="{ active: exp.color == 1 }" @click="setExplainColor(index, 1)">黑色</button>
|
|
|
+ <button type="button" class="seg-btn mini" :class="{ active: exp.color == 2 }" @click="setExplainColor(index, 2)">红色</button>
|
|
|
+ <button type="button" class="seg-btn mini" :class="{ active: exp.color == 3 }" @click="setExplainColor(index, 3)">蓝色</button>
|
|
|
+ <button type="button" class="seg-btn mini" :class="{ active: exp.fontWeight == 1 }" @click="setExplainWeight(index, 1)">常规</button>
|
|
|
+ <button type="button" class="seg-btn mini" :class="{ active: exp.fontWeight == 2 }" @click="setExplainWeight(index, 2)">加粗</button>
|
|
|
+ <a class="link-del" @click="removeExplain(index)">删除</a>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="empty-tip">暂无说明,点击添加</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="ps-footer">
|
|
|
+ <!-- 两个按钮都靠左:取消在前,保存紧跟其后(略放大) -->
|
|
|
+ <el-button size="small" @click="onCancel">取消</el-button>
|
|
|
+ <el-button type="primary" class="btn-save" :loading="saving" @click="submitSave">保存设置</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+/**
|
|
|
+ * 配送方式设置:布局按设计稿 2×2;逻辑对齐小程序 psMethod
|
|
|
+ */
|
|
|
+export default {
|
|
|
+ name: 'ps-method',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ loading: false,
|
|
|
+ saving: false,
|
|
|
+ tabs: [
|
|
|
+ { name: '送货', value: 0 },
|
|
|
+ { name: '自取', value: 1 },
|
|
|
+ { name: '跑腿', value: 2 },
|
|
|
+ { name: '快递', value: 4 },
|
|
|
+ { name: '物流', value: 3 }
|
|
|
+ ],
|
|
|
+ tabIndex: '0',
|
|
|
+ sortsList: [],
|
|
|
+ deliveryMerchantBound: false,
|
|
|
+ form: this.createEmptyForm(0)
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ sortedMethodsText() {
|
|
|
+ const enabledList = this.sortsList.filter(item => item.status !== 0);
|
|
|
+ if (!enabledList.length) return '暂无启用的配送方式';
|
|
|
+ const sorted = [...enabledList].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(' - ');
|
|
|
+ },
|
|
|
+ /** 设计稿跑腿提供方:已绑定显示「已绑定」,未绑定显示「-」 */
|
|
|
+ deliveryBindText() {
|
|
|
+ return this.deliveryMerchantBound ? '已绑定' : '-';
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.loadConfig(0);
|
|
|
+ this.loadSorts();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ createEmptyForm(style) {
|
|
|
+ return {
|
|
|
+ id: 0,
|
|
|
+ style: style,
|
|
|
+ name: '',
|
|
|
+ status: 1,
|
|
|
+ sort: 0,
|
|
|
+ minAmount: 0,
|
|
|
+ minNum: 0,
|
|
|
+ unMeet: 0,
|
|
|
+ unMeetFee: 0,
|
|
|
+ calcType: 0,
|
|
|
+ hsFreeKm: 0,
|
|
|
+ hsPerKmPrice: 0,
|
|
|
+ startKm: 0,
|
|
|
+ startPrice: 0,
|
|
|
+ perKmPrice: 0,
|
|
|
+ changeRate: 0,
|
|
|
+ explains: [],
|
|
|
+ reduceRules: []
|
|
|
+ };
|
|
|
+ },
|
|
|
+ onTabClick() {
|
|
|
+ const index = Number(this.tabIndex);
|
|
|
+ const style = this.tabs[index].value;
|
|
|
+ this.loadConfig(style);
|
|
|
+ if (style === 2) this.loadDeliveryBindStatus();
|
|
|
+ },
|
|
|
+ loadSorts() {
|
|
|
+ this.$service.psMethod
|
|
|
+ .getSorts()
|
|
|
+ .then(res => {
|
|
|
+ this.sortsList = Array.isArray(res) ? res : (res && res.list) || [];
|
|
|
+ })
|
|
|
+ .catch(() => {});
|
|
|
+ },
|
|
|
+ loadDeliveryBindStatus() {
|
|
|
+ return this.$service.delivery
|
|
|
+ .platformList()
|
|
|
+ .then(res => {
|
|
|
+ const list = (res && res.platformList) || [];
|
|
|
+ this.deliveryMerchantBound = list.some(item => !!item.is_authorized);
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ this.deliveryMerchantBound = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ onManageProvider() {
|
|
|
+ this.$message.info('请前往小程序「跑腿管理」完成授权绑定');
|
|
|
+ },
|
|
|
+ onManageWl() {
|
|
|
+ this.$message.info('请前往小程序「管理物流」维护承运商');
|
|
|
+ },
|
|
|
+ onCancel() {
|
|
|
+ this.loadConfig(this.form.style);
|
|
|
+ this.$message.success('已重新加载当前配置');
|
|
|
+ },
|
|
|
+ onStatusRadio(val) {
|
|
|
+ const next = Number(val);
|
|
|
+ const prev = next === 1 ? 0 : 1;
|
|
|
+ if (next === 1 && Number(this.form.style) === 2) {
|
|
|
+ if (!this.hasValidCustomConfig()) {
|
|
|
+ this.$message.warning('请先完整填写自定义计费规则后再启用');
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.form.status = prev;
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!this.canEnableRunner()) {
|
|
|
+ this.$message.warning('请先绑定跑腿商户或配置完整的自定义计费后再启用');
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.form.status = prev;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ hasValidCustomConfig() {
|
|
|
+ return (
|
|
|
+ (Number(this.form.startKm) || 0) > 0 &&
|
|
|
+ (Number(this.form.startPrice) || 0) > 0 &&
|
|
|
+ (Number(this.form.perKmPrice) || 0) > 0
|
|
|
+ );
|
|
|
+ },
|
|
|
+ canEnableRunner() {
|
|
|
+ return this.deliveryMerchantBound || this.hasValidCustomConfig();
|
|
|
+ },
|
|
|
+ validateRunnerBeforeSave() {
|
|
|
+ if (Number(this.form.style) !== 2) return '';
|
|
|
+ if (!this.hasValidCustomConfig()) {
|
|
|
+ return '请完整填写自定义计费规则(起步公里、起步价、超出每公里价)';
|
|
|
+ }
|
|
|
+ if (Number(this.form.status) === 1 && !this.canEnableRunner()) {
|
|
|
+ return '请先绑定跑腿商户或配置完整的自定义计费后再启用';
|
|
|
+ }
|
|
|
+ return '';
|
|
|
+ },
|
|
|
+ loadConfig(style) {
|
|
|
+ this.loading = true;
|
|
|
+ this.$service.psMethod
|
|
|
+ .getConfig({ style })
|
|
|
+ .then(data => {
|
|
|
+ const res = data || {};
|
|
|
+ this.form = {
|
|
|
+ id: res.id || 0,
|
|
|
+ style: style,
|
|
|
+ name: res.name || '',
|
|
|
+ status: res.status != null ? Number(res.status) : 1,
|
|
|
+ sort: res.sort != null ? Number(res.sort) : 0,
|
|
|
+ minAmount: res.minAmount != null ? parseFloat(res.minAmount) : 0,
|
|
|
+ minNum: res.minNum != null ? Number(res.minNum) : 0,
|
|
|
+ unMeet: res.unMeet != null ? Number(res.unMeet) : 0,
|
|
|
+ unMeetFee: res.unMeetFee != null ? parseFloat(res.unMeetFee) : 0,
|
|
|
+ calcType: res.calcType != null ? Number(res.calcType) : 0,
|
|
|
+ hsFreeKm: res.hsFreeKm != null ? parseFloat(res.hsFreeKm) : 0,
|
|
|
+ hsPerKmPrice: res.hsPerKmPrice != null ? parseFloat(res.hsPerKmPrice) : 0,
|
|
|
+ startKm: res.startKm != null ? parseFloat(res.startKm) : 0,
|
|
|
+ startPrice: res.startPrice != null ? parseFloat(res.startPrice) : 0,
|
|
|
+ perKmPrice: res.perKmPrice != null ? parseFloat(res.perKmPrice) : 0,
|
|
|
+ changeRate: res.changeRate != null ? parseFloat(res.changeRate) : 0,
|
|
|
+ explains: (res.explains || []).map(exp => ({
|
|
|
+ ...exp,
|
|
|
+ color: exp.color != null ? Number(exp.color) : 1,
|
|
|
+ fontWeight: exp.fontWeight != null ? Number(exp.fontWeight) : 1
|
|
|
+ })),
|
|
|
+ reduceRules: (res.reduceRules || []).map(rule => ({
|
|
|
+ ...rule,
|
|
|
+ meetNum: rule.meetNum != null ? Number(rule.meetNum) : 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
|
|
|
+ }))
|
|
|
+ };
|
|
|
+ if (style === 2) this.loadDeliveryBindStatus();
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ this.$message.error('加载失败,请重试');
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.loading = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ addReduceRule() {
|
|
|
+ this.form.reduceRules.push({
|
|
|
+ meetNum: 0,
|
|
|
+ meetAmount: 0,
|
|
|
+ freeKm: 0,
|
|
|
+ sort: this.form.reduceRules.length
|
|
|
+ });
|
|
|
+ },
|
|
|
+ clearReduceRuleInputOnFocus(rule, field) {
|
|
|
+ this.$set(rule, field, '');
|
|
|
+ },
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ this.$set(rule, field, field === 'meetNum' ? parseInt(num, 10) : parseFloat(String(num)));
|
|
|
+ },
|
|
|
+ removeReduceRule(index) {
|
|
|
+ this.$confirm('确认删除该满减规则吗?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ })
|
|
|
+ .then(() => this.form.reduceRules.splice(index, 1))
|
|
|
+ .catch(() => {});
|
|
|
+ },
|
|
|
+ addExplain() {
|
|
|
+ if (this.form.explains.length >= 5) {
|
|
|
+ this.$message.warning('最多只能添加5条说明');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.form.explains.push({
|
|
|
+ explain: '',
|
|
|
+ color: 1,
|
|
|
+ fontWeight: 1,
|
|
|
+ sort: this.form.explains.length
|
|
|
+ });
|
|
|
+ },
|
|
|
+ removeExplain(index) {
|
|
|
+ this.$confirm('确认删除该说明吗?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ })
|
|
|
+ .then(() => this.form.explains.splice(index, 1))
|
|
|
+ .catch(() => {});
|
|
|
+ },
|
|
|
+ setExplainColor(index, color) {
|
|
|
+ const exp = this.form.explains[index];
|
|
|
+ if (!exp) return;
|
|
|
+ this.$set(this.form.explains, index, { ...exp, color });
|
|
|
+ },
|
|
|
+ setExplainWeight(index, fontWeight) {
|
|
|
+ const exp = this.form.explains[index];
|
|
|
+ if (!exp) return;
|
|
|
+ this.$set(this.form.explains, index, { ...exp, fontWeight });
|
|
|
+ },
|
|
|
+ submitSave() {
|
|
|
+ if (!this.form.name) {
|
|
|
+ this.$message.warning('请填写别名');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (this.form.name.length > 4) {
|
|
|
+ this.$message.warning('名称最多4个字');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ for (let i = 0; i < this.form.explains.length; i++) {
|
|
|
+ if (!this.form.explains[i].explain) {
|
|
|
+ this.$message.warning(`请填写第 ${i + 1} 条说明内容`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const runnerErr = this.validateRunnerBeforeSave();
|
|
|
+ if (runnerErr) {
|
|
|
+ this.$message.warning(runnerErr);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.saving = true;
|
|
|
+ this.$service.psMethod
|
|
|
+ .saveConfig(this.form)
|
|
|
+ .then(() => {
|
|
|
+ this.$message.success('保存成功');
|
|
|
+ this.loadConfig(this.form.style);
|
|
|
+ this.loadSorts();
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.saving = false;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.ps-method-page {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ height: calc(100vh - 100px);
|
|
|
+ background: #f0f2f5;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+.ps-tabs {
|
|
|
+ background: #fff;
|
|
|
+ padding: 0 16px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ ::v-deep .el-tabs__header {
|
|
|
+ margin: 0;
|
|
|
+ }
|
|
|
+ ::v-deep .el-tabs__item {
|
|
|
+ height: 44px;
|
|
|
+ line-height: 44px;
|
|
|
+ }
|
|
|
+ ::v-deep .el-tabs__nav-wrap::after {
|
|
|
+ height: 1px;
|
|
|
+ }
|
|
|
+}
|
|
|
+.ps-body {
|
|
|
+ flex: 1;
|
|
|
+ overflow: auto;
|
|
|
+ padding: 12px 16px;
|
|
|
+}
|
|
|
+.base-panel {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #e8e8e8;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+.base-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ min-height: 38px;
|
|
|
+ padding: 4px 20px;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+ &.last {
|
|
|
+ border-bottom: none;
|
|
|
+ }
|
|
|
+}
|
|
|
+.base-lab {
|
|
|
+ width: 110px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ color: #595959;
|
|
|
+ font-size: 13px;
|
|
|
+}
|
|
|
+.base-val {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ flex-wrap: wrap;
|
|
|
+}
|
|
|
+.order-preview {
|
|
|
+ color: #262626;
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+.provider-text {
|
|
|
+ margin-right: 12px;
|
|
|
+ color: #595959;
|
|
|
+ min-width: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 分段按钮:选中蓝底白字 */
|
|
|
+.seg-btn {
|
|
|
+ display: inline-block;
|
|
|
+ height: 28px;
|
|
|
+ line-height: 26px;
|
|
|
+ padding: 0 14px;
|
|
|
+ margin-right: 8px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #595959;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #d9d9d9;
|
|
|
+ border-radius: 3px;
|
|
|
+ cursor: pointer;
|
|
|
+ outline: none;
|
|
|
+ &.active {
|
|
|
+ color: #fff;
|
|
|
+ background: #1890ff;
|
|
|
+ border-color: #1890ff;
|
|
|
+ }
|
|
|
+ &.mini {
|
|
|
+ height: 24px;
|
|
|
+ line-height: 22px;
|
|
|
+ padding: 0 10px;
|
|
|
+ margin-right: 6px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.rule-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 1fr 1fr;
|
|
|
+ grid-gap: 12px;
|
|
|
+ &.single {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ }
|
|
|
+}
|
|
|
+.rule-card {
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #e8e8e8;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 14px 16px;
|
|
|
+ min-height: 160px;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+.rule-head {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+.rule-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #262626;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+.rule-head .rule-title {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+.badge {
|
|
|
+ display: inline-block;
|
|
|
+ margin-left: 8px;
|
|
|
+ padding: 0 6px;
|
|
|
+ font-size: 11px;
|
|
|
+ font-weight: 400;
|
|
|
+ color: #52c41a;
|
|
|
+ background: #f6ffed;
|
|
|
+ border: 1px solid #b7eb8f;
|
|
|
+ border-radius: 2px;
|
|
|
+ &.gray {
|
|
|
+ color: #8c8c8c;
|
|
|
+ background: #fafafa;
|
|
|
+ border-color: #d9d9d9;
|
|
|
+ }
|
|
|
+}
|
|
|
+.rule-line {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #595959;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ line-height: 28px;
|
|
|
+}
|
|
|
+.fixed-lab {
|
|
|
+ width: 64px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ color: #8c8c8c;
|
|
|
+}
|
|
|
+.num-input {
|
|
|
+ width: 72px !important;
|
|
|
+ margin: 0 6px;
|
|
|
+}
|
|
|
+.num-input-sm {
|
|
|
+ width: 56px !important;
|
|
|
+ margin: 0 6px;
|
|
|
+}
|
|
|
+.rule-tip {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #52c41a;
|
|
|
+ line-height: 1.5;
|
|
|
+ margin-top: 4px;
|
|
|
+}
|
|
|
+.empty-tip {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #bfbfbf;
|
|
|
+ padding: 8px 0;
|
|
|
+}
|
|
|
+.explain-card-half {
|
|
|
+ /* 非跑腿时说明板块整体半屏 */
|
|
|
+ max-width: 50%;
|
|
|
+}
|
|
|
+.explain-item {
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+/* 说明输入框宽度约半屏,卡片内不超过父级 */
|
|
|
+.explain-textarea {
|
|
|
+ display: block;
|
|
|
+ width: 50vw !important;
|
|
|
+ max-width: 100%;
|
|
|
+}
|
|
|
+.explain-textarea ::v-deep .el-textarea__inner {
|
|
|
+ width: 100%;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+.explain-actions {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ margin-top: 8px;
|
|
|
+ width: 50vw;
|
|
|
+ max-width: 100%;
|
|
|
+}
|
|
|
+.link-del {
|
|
|
+ margin-left: 10px;
|
|
|
+ color: #1890ff;
|
|
|
+ cursor: pointer;
|
|
|
+ font-size: 12px;
|
|
|
+ &:hover {
|
|
|
+ opacity: 0.8;
|
|
|
+ }
|
|
|
+}
|
|
|
+.text-red {
|
|
|
+ color: #f5222d;
|
|
|
+}
|
|
|
+.text-blue {
|
|
|
+ color: #1890ff;
|
|
|
+}
|
|
|
+.text-bold {
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+.ps-footer {
|
|
|
+ flex-shrink: 0;
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-start;
|
|
|
+ align-items: center;
|
|
|
+ padding: 12px 16px;
|
|
|
+ background: #fff;
|
|
|
+ border-top: 1px solid #e8e8e8;
|
|
|
+ .el-button + .el-button {
|
|
|
+ margin-left: 10px;
|
|
|
+ }
|
|
|
+ .btn-save {
|
|
|
+ min-width: 110px;
|
|
|
+ height: 36px;
|
|
|
+ padding: 0 22px;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|