|
|
@@ -0,0 +1,419 @@
|
|
|
+<!--
|
|
|
+ 分成比例管理 — ghs Web 门店邀请分佣
|
|
|
+ Tab1 基础分佣只读展示 dict;Tab2 批发商独立比例 CRUD
|
|
|
+-->
|
|
|
+<template>
|
|
|
+ <div class="app-list-content shop-invite-ratio">
|
|
|
+ <el-tabs v-model="activeTab" type="border-card" class="ratio-tabs" @tab-click="handleTabClick">
|
|
|
+ <el-tab-pane label="基础分佣设置" name="basic">
|
|
|
+ <div class="basic-panel" v-loading="basicLoading">
|
|
|
+ <div class="panel-title">基础分佣设置</div>
|
|
|
+ <el-form label-width="140px" class="basic-form">
|
|
|
+ <el-form-item label="分佣功能">
|
|
|
+ <el-switch
|
|
|
+ :value="basicConfig.enabled === 1"
|
|
|
+ disabled
|
|
|
+ active-text="已开启"
|
|
|
+ inactive-text="已关闭"
|
|
|
+ ></el-switch>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="佣金计算基数">
|
|
|
+ <span class="readonly-text">{{ basicConfig.commissionBaseLabel || '会员实际支付金额' }}</span>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="会员开通原价">
|
|
|
+ <span class="readonly-text">¥ {{ moneyText(basicConfig.price) }}</span>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="默认分佣金额">
|
|
|
+ <span class="readonly-text">¥ {{ moneyText(basicConfig.commissionAmount) }}</span>
|
|
|
+ <span class="ratio-tip">(等效 {{ ratioText(basicConfig.commissionRatio) }})</span>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="默认花店优惠">
|
|
|
+ <span class="readonly-text">¥ {{ moneyText(basicConfig.hdDiscountAmount) }}</span>
|
|
|
+ <span class="ratio-tip">(等效 {{ ratioText(basicConfig.discountRatio) }})</span>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div class="info-banner">
|
|
|
+ 未单独配置批发商时,统一按默认分佣金额计算佣金。全局配置修改请联系运维更新 dict.php 中的 hdRegisterConfig。
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ <el-tab-pane label="批发商独立比例设置" name="wholesaler"></el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+
|
|
|
+ <!-- 仅在批发商 Tab 激活时挂载 x-crud,避免隐藏状态下表格高度计算为 0 -->
|
|
|
+ <div v-if="activeTab === 'wholesaler'" class="wholesaler-crud-panel">
|
|
|
+ <x-crud class="liu-crud-wrap" @load="onRatioLoad">
|
|
|
+ <template #slot-ratio-search>
|
|
|
+ <el-input
|
|
|
+ v-model="ratioKeyword"
|
|
|
+ placeholder="请输入批发商名称或手机号"
|
|
|
+ size="mini"
|
|
|
+ clearable
|
|
|
+ style="width: 240px;"
|
|
|
+ @keyup.enter.native="refreshRatioList"
|
|
|
+ ></el-input>
|
|
|
+ <el-button size="mini" type="primary" style="margin-left: 10px;" @click="refreshRatioList">查询</el-button>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #slot-add-ratio>
|
|
|
+ <el-button type="primary" size="mini" @click="openRatioDialog()">新增比例</el-button>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #table-column-mobile="{ scope }">
|
|
|
+ <span>{{ scope.row.mobile || '-' }}</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #table-column-inviteCode="{ scope }">
|
|
|
+ <span>{{ scope.row.inviteCode || '-' }}</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #table-column-commissionAmount="{ scope }">
|
|
|
+ <span>¥ {{ moneyText(scope.row.commissionAmount) }}</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #table-column-hdDiscountAmount="{ scope }">
|
|
|
+ <span>¥ {{ moneyText(scope.row.hdDiscountAmount) }}</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #table-column-effectiveTime="{ scope }">
|
|
|
+ <span>{{ formatDate(scope.row.effectiveTime) }}</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #table-column-updateTime="{ scope }">
|
|
|
+ <span>{{ formatDateTime(scope.row.updateTime) }}</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #slot-column-option="{ scope }">
|
|
|
+ <el-button type="text" @click="openRatioDialog(scope.row)">编辑</el-button>
|
|
|
+ </template>
|
|
|
+ </x-crud>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-dialog
|
|
|
+ :title="ratioForm.id ? '编辑分成比例' : '新增分成比例'"
|
|
|
+ :visible.sync="ratioDialogVisible"
|
|
|
+ width="520px"
|
|
|
+ @closed="resetRatioForm"
|
|
|
+ >
|
|
|
+ <el-form ref="ratioFormRef" :model="ratioForm" :rules="ratioRules" label-width="120px">
|
|
|
+ <el-form-item label="批发商" prop="mainId">
|
|
|
+ <el-select
|
|
|
+ v-model="ratioForm.mainId"
|
|
|
+ filterable
|
|
|
+ remote
|
|
|
+ clearable
|
|
|
+ placeholder="请选择批发商"
|
|
|
+ :remote-method="searchWholesaler"
|
|
|
+ :loading="wholesalerLoading"
|
|
|
+ style="width: 100%;"
|
|
|
+ :disabled="!!ratioForm.id"
|
|
|
+ @focus="loadWholesalerOptions()"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in wholesalerOptions"
|
|
|
+ :key="item.mainId"
|
|
|
+ :label="`${item.shopName} (${item.mobile || '-'})`"
|
|
|
+ :value="item.mainId"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="分佣金额" prop="commissionAmount">
|
|
|
+ <el-input-number
|
|
|
+ v-model="ratioForm.commissionAmount"
|
|
|
+ :min="0"
|
|
|
+ :precision="2"
|
|
|
+ :step="1"
|
|
|
+ style="width: 200px;"
|
|
|
+ ></el-input-number>
|
|
|
+ <span class="ratio-tip">元</span>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="花店优惠" prop="hdDiscountAmount">
|
|
|
+ <el-input-number
|
|
|
+ v-model="ratioForm.hdDiscountAmount"
|
|
|
+ :min="0"
|
|
|
+ :precision="2"
|
|
|
+ :step="1"
|
|
|
+ style="width: 200px;"
|
|
|
+ ></el-input-number>
|
|
|
+ <span class="ratio-tip">元</span>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer">
|
|
|
+ <el-button @click="ratioDialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" :loading="ratioSaving" @click="submitRatio">确定</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+export default {
|
|
|
+ name: 'shop-invite-ratio-manage',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ activeTab: 'basic',
|
|
|
+ basicLoading: false,
|
|
|
+ basicConfig: {
|
|
|
+ enabled: 0,
|
|
|
+ price: 0,
|
|
|
+ commissionAmount: 0,
|
|
|
+ hdDiscountAmount: 0,
|
|
|
+ commissionRatio: 0,
|
|
|
+ discountRatio: 0,
|
|
|
+ commissionBaseLabel: '会员实际支付金额'
|
|
|
+ },
|
|
|
+ ratioApp: null,
|
|
|
+ ratioKeyword: '',
|
|
|
+ ratioDialogVisible: false,
|
|
|
+ ratioSaving: false,
|
|
|
+ wholesalerLoading: false,
|
|
|
+ wholesalerOptions: [],
|
|
|
+ ratioForm: {
|
|
|
+ id: 0,
|
|
|
+ mainId: '',
|
|
|
+ commissionAmount: 0,
|
|
|
+ hdDiscountAmount: 0
|
|
|
+ },
|
|
|
+ ratioRules: {
|
|
|
+ mainId: [{ required: true, message: '请选择批发商', trigger: 'change' }],
|
|
|
+ commissionAmount: [{ required: true, message: '请输入分佣金额', trigger: 'blur' }],
|
|
|
+ hdDiscountAmount: [{ required: true, message: '请输入花店优惠金额', trigger: 'blur' }]
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.loadBasicConfig();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ moneyText(val) {
|
|
|
+ const n = parseFloat(val);
|
|
|
+ return isNaN(n) ? '0.00' : n.toFixed(2);
|
|
|
+ },
|
|
|
+ formatDate(val) {
|
|
|
+ if (!val) {
|
|
|
+ return '-';
|
|
|
+ }
|
|
|
+ return String(val).slice(0, 10);
|
|
|
+ },
|
|
|
+ formatDateTime(val) {
|
|
|
+ if (!val) {
|
|
|
+ return '-';
|
|
|
+ }
|
|
|
+ const text = String(val).replace('T', ' ');
|
|
|
+ return text.length > 16 ? text.slice(0, 16) : text;
|
|
|
+ },
|
|
|
+ ratioText(val) {
|
|
|
+ const n = parseFloat(val);
|
|
|
+ return isNaN(n) ? '0.00%' : `${n.toFixed(2)}%`;
|
|
|
+ },
|
|
|
+ loadBasicConfig() {
|
|
|
+ this.basicLoading = true;
|
|
|
+ this.$service.shopInvite.registerConfig().then(res => {
|
|
|
+ if (res) {
|
|
|
+ this.basicConfig = { ...this.basicConfig, ...res };
|
|
|
+ }
|
|
|
+ }).finally(() => {
|
|
|
+ this.basicLoading = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ handleTabClick(tab) {
|
|
|
+ // Tab 切换后重算表格高度(x-crud 内部 calcHeight 依赖可见容器)
|
|
|
+ if (tab.name === 'wholesaler') {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.layoutRatioTable();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /** 批发商独立比例分页:合并搜索关键词并映射 x-crud 所需 pagination */
|
|
|
+ fetchRatioPage(params) {
|
|
|
+ return this.$service.shopInvite.wholesalerRatioList({
|
|
|
+ ...params,
|
|
|
+ keyword: this.ratioKeyword
|
|
|
+ }).then(res => {
|
|
|
+ return {
|
|
|
+ list: Array.isArray(res.list) ? res.list : [],
|
|
|
+ pagination: {
|
|
|
+ page: parseInt(params.page || 1, 10),
|
|
|
+ size: parseInt(params.size || 20, 10),
|
|
|
+ total: parseInt(res.totalNum || 0, 10),
|
|
|
+ totalPage: parseInt(res.totalPage || 0, 10)
|
|
|
+ }
|
|
|
+ };
|
|
|
+ });
|
|
|
+ },
|
|
|
+ onRatioLoad({ ctx, app }) {
|
|
|
+ this.ratioApp = app;
|
|
|
+ ctx.service({ page: this.fetchRatioPage.bind(this) })
|
|
|
+ .set('table', {
|
|
|
+ // 固定表格可视高度,避免 calcHeight 在布局未稳定时算出 0
|
|
|
+ props: {
|
|
|
+ height: 520
|
|
|
+ },
|
|
|
+ columns: [
|
|
|
+ { prop: 'shopName', label: '批发商', align: 'center', 'min-width': 140 },
|
|
|
+ { prop: 'mobile', label: '联系手机号', align: 'center', 'min-width': 120 },
|
|
|
+ { prop: 'inviteCode', label: '邀请码', align: 'center', 'min-width': 100 },
|
|
|
+ { prop: 'commissionAmount', label: '分成金额', align: 'center', width: 110 },
|
|
|
+ { prop: 'hdDiscountAmount', label: '花店优惠金额', align: 'center', width: 120 },
|
|
|
+ { prop: 'effectiveTime', label: '生效时间', align: 'center', width: 110 },
|
|
|
+ { prop: 'updateTime', label: '更新时间', align: 'center', 'min-width': 140 }
|
|
|
+ ],
|
|
|
+ op: {
|
|
|
+ visible: true,
|
|
|
+ props: { width: 80, align: 'center', fixed: 'right', label: '操作' },
|
|
|
+ layout: ['slot-column-option']
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .set('layout', [
|
|
|
+ ['slot-ratio-search', 'flex1', 'slot-add-ratio', 'refresh-btn'],
|
|
|
+ ['data-table'],
|
|
|
+ ['flex1', 'pagination']
|
|
|
+ ])
|
|
|
+ .on('refresh', async (params, { next, render }) => {
|
|
|
+ const res = await next({
|
|
|
+ ...params,
|
|
|
+ keyword: this.ratioKeyword
|
|
|
+ });
|
|
|
+ render(Array.isArray(res.list) ? res.list : []);
|
|
|
+ })
|
|
|
+ .done();
|
|
|
+
|
|
|
+ // 仅在此处触发首次加载,避免与 Tab 切换重复 refresh 造成 refreshRd 竞态
|
|
|
+ this.$nextTick(() => {
|
|
|
+ app.refresh({ keyword: this.ratioKeyword });
|
|
|
+ this.layoutRatioTable();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ refreshRatioList() {
|
|
|
+ if (!this.ratioApp) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.ratioApp.refresh({ keyword: this.ratioKeyword });
|
|
|
+ },
|
|
|
+ /** 重算 x-crud 表格高度,解决 Tab/容器切换后 tbody 不显示 */
|
|
|
+ layoutRatioTable() {
|
|
|
+ const crud = this.ratioApp;
|
|
|
+ if (!crud || !crud.$refs || !crud.$refs.table) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const dataTable = crud.$refs.table;
|
|
|
+ if (dataTable && typeof dataTable.resize === 'function') {
|
|
|
+ dataTable.resize();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ searchWholesaler(keyword) {
|
|
|
+ this.loadWholesalerOptions(keyword);
|
|
|
+ },
|
|
|
+ /** 加载批发商下拉:xhShop ptStyle=2,无关键词时返回默认列表 */
|
|
|
+ loadWholesalerOptions(keyword = '') {
|
|
|
+ const searchKey = typeof keyword === 'string' ? keyword.trim() : '';
|
|
|
+ this.wholesalerLoading = true;
|
|
|
+ this.$service.shopInvite.wholesalerSearch({ keyword: searchKey }).then(res => {
|
|
|
+ this.wholesalerOptions = (res && res.list) || [];
|
|
|
+ }).finally(() => {
|
|
|
+ this.wholesalerLoading = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ openRatioDialog(row) {
|
|
|
+ if (row && row.mainId) {
|
|
|
+ this.ratioForm = {
|
|
|
+ id: row.id,
|
|
|
+ mainId: row.mainId,
|
|
|
+ commissionAmount: parseFloat(row.commissionAmount) || 0,
|
|
|
+ hdDiscountAmount: parseFloat(row.hdDiscountAmount) || 0
|
|
|
+ };
|
|
|
+ this.wholesalerOptions = [{
|
|
|
+ mainId: row.mainId,
|
|
|
+ shopName: row.shopName,
|
|
|
+ mobile: row.mobile
|
|
|
+ }];
|
|
|
+ } else {
|
|
|
+ this.resetRatioForm();
|
|
|
+ this.loadWholesalerOptions();
|
|
|
+ }
|
|
|
+ this.ratioDialogVisible = true;
|
|
|
+ },
|
|
|
+ resetRatioForm() {
|
|
|
+ this.ratioForm = {
|
|
|
+ id: 0,
|
|
|
+ mainId: '',
|
|
|
+ commissionAmount: parseFloat(this.basicConfig.commissionAmount) || 0,
|
|
|
+ hdDiscountAmount: parseFloat(this.basicConfig.hdDiscountAmount) || 0
|
|
|
+ };
|
|
|
+ this.wholesalerOptions = [];
|
|
|
+ if (this.$refs.ratioFormRef) {
|
|
|
+ this.$refs.ratioFormRef.clearValidate();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ submitRatio() {
|
|
|
+ this.$refs.ratioFormRef.validate(valid => {
|
|
|
+ if (!valid) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.ratioSaving = true;
|
|
|
+ this.$service.shopInvite.saveWholesalerRatio({
|
|
|
+ mainId: this.ratioForm.mainId,
|
|
|
+ commissionAmount: this.ratioForm.commissionAmount,
|
|
|
+ hdDiscountAmount: this.ratioForm.hdDiscountAmount
|
|
|
+ }).then(() => {
|
|
|
+ this.$message.success('保存成功');
|
|
|
+ this.ratioDialogVisible = false;
|
|
|
+ this.refreshRatioList();
|
|
|
+ }).finally(() => {
|
|
|
+ this.ratioSaving = false;
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.shop-invite-ratio {
|
|
|
+ min-height: calc(100vh - 120px);
|
|
|
+
|
|
|
+ .ratio-tabs {
|
|
|
+ background: #fff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .wholesaler-crud-panel {
|
|
|
+ min-height: 520px;
|
|
|
+ margin-top: 12px;
|
|
|
+ background: #fff;
|
|
|
+ padding: 12px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+
|
|
|
+ .basic-panel {
|
|
|
+ padding: 20px 24px 30px;
|
|
|
+ max-width: 720px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .panel-title {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .readonly-text {
|
|
|
+ color: #303133;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ratio-tip {
|
|
|
+ margin-left: 8px;
|
|
|
+ color: #909399;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .info-banner {
|
|
|
+ margin-top: 24px;
|
|
|
+ padding: 12px 16px;
|
|
|
+ background: #f0f9eb;
|
|
|
+ border: 1px solid #e1f3d8;
|
|
|
+ color: #67c23a;
|
|
|
+ border-radius: 4px;
|
|
|
+ line-height: 1.6;
|
|
|
+ font-size: 13px;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|