Prechádzať zdrojové kódy

admin-web 新增充值配置管理页面(支持站点关联)

- 新增平台配置 > 充值配置菜单页面
- 列表页支持按站点筛选,区分平台默认配置和站点专属配置
- 弹窗支持选择关联站点,留空即为平台默认配置
- 充值金额和赠款金额以元为单位输入,自动转换为分存储

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
skyline 4 dní pred
rodič
commit
ccd1b12221

+ 15 - 0
admin-web/src/router/route.ts

@@ -346,6 +346,21 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                             perm: "platformFeeRate.list",
                             icon: 'ele-Wallet',
                         },
+                    },
+                    {
+                        path: '/platform/rechargeConfig',
+                        name: 'adminPlatformRechargeConfig',
+                        component: () => import('/@/views/admin/platform/rechargeConfig/index.vue'),
+                        meta: {
+                            title: '充值配置',
+                            isLink: '',
+                            isHide: false,
+                            isKeepAlive: true,
+                            isAffix: false,
+                            isIframe: false,
+                            perm: "rechargeConfig.list",
+                            icon: 'ele-Money',
+                        },
                     }
                 ]
             },

+ 162 - 0
admin-web/src/views/admin/platform/rechargeConfig/dialog.vue

@@ -0,0 +1,162 @@
+<style scoped lang="scss">
+
+</style>
+<template>
+  <div class="system-dialog-container">
+    <el-dialog
+        :title="state.dialog.title"
+        v-model="state.dialog.isShowDialog"
+        width="600px"
+        draggable
+        destroy-on-close
+        :close-on-click-modal="false"
+        align-center>
+      <el-form
+          :model="state.ruleForm"
+          :rules="state.rules"
+          label-position="top"
+          ref="formRef"
+          size="default"
+          label-width="100px"
+          class="mt5">
+        <el-form-item label="关联站点" prop="stationId">
+          <el-select-v2
+              v-model="state.ruleForm.stationId"
+              :options="state.stationOptions"
+              placeholder="留空则为平台默认配置"
+              clearable
+              style="width: 100%"
+          />
+        </el-form-item>
+        <el-form-item label="充值金额(元)" prop="rechargeAmount">
+          <el-input-number
+              v-model="state.ruleForm.rechargeAmountYuan"
+              placeholder="充值金额"
+              :min="0"
+              :step="10"
+              style="width: 100%">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="赠款金额(元)" prop="grantsAmount">
+          <el-input-number
+              v-model="state.ruleForm.grantsAmountYuan"
+              placeholder="赠款金额(0表示不赠送)"
+              :min="0"
+              :step="5"
+              style="width: 100%">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="标签" prop="label">
+          <el-input
+              v-model="state.ruleForm.label"
+              placeholder="如:推荐、限时"
+              clearable>
+          </el-input>
+        </el-form-item>
+      </el-form>
+
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onCancel" size="default">取 消</el-button>
+          <el-button :loading="state.btnLoading" type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts" name="RechargeConfigDialog">
+import { reactive, onMounted, ref } from 'vue';
+import { Msg } from "/@/utils/message";
+import { $body, $get } from "/@/utils/request";
+import u from '/@/utils/u'
+
+const emit = defineEmits(['refresh']);
+const formRef = ref();
+
+const initState = () => ({
+  ruleForm: {
+    id: null as number | null,
+    stationId: null as string | null,
+    rechargeAmountYuan: 0,
+    grantsAmountYuan: 0,
+    label: '',
+  },
+  btnLoading: false,
+  dialog: {
+    isShowDialog: false,
+    type: '',
+    title: '',
+    submitTxt: '',
+  },
+  rules: {
+    rechargeAmountYuan: [u.validator.required],
+  },
+  stationOptions: [] as Array<{ value: string; label: string }>,
+})
+
+const state = reactive(initState());
+
+onMounted(() => {
+  // 加载站点列表供选择
+  $body('/washStation/list', { pageNum: 1, pageSize: 200 }).then((res: any) => {
+    state.stationOptions = (res.list || []).map((s: any) => ({
+      value: s.stationId,
+      label: s.stationName + ' (' + s.stationId + ')',
+    }));
+  });
+});
+
+const open = (action: string = 'add', row: any) => {
+  state.dialog.title = u.dialog.actions[action].title + "『充值配置』"
+  state.dialog.submitTxt = u.dialog.actions[action].btn + "『充值配置』"
+  state.dialog.isShowDialog = true;
+  if (action !== 'add' && row) {
+    state.ruleForm = {
+      id: row.id,
+      stationId: row.stationId || null,
+      rechargeAmountYuan: (row.rechargeAmount || 0) / 100,
+      grantsAmountYuan: (row.grantsAmount || 0) / 100,
+      label: row.label || '',
+    };
+  }
+};
+
+const onClose = () => {
+  state.dialog.isShowDialog = false;
+  Object.assign(state, initState());
+};
+
+const onCancel = () => {
+  onClose();
+};
+
+const onSubmit = () => {
+  formRef.value.validate((v: boolean) => {
+    if (v) {
+      state.btnLoading = true;
+      const payload = {
+        id: state.ruleForm.id,
+        stationId: state.ruleForm.stationId || null,
+        rechargeAmount: Math.round(state.ruleForm.rechargeAmountYuan * 100),
+        grantsAmount: Math.round(state.ruleForm.grantsAmountYuan * 100),
+        label: state.ruleForm.label || null,
+      };
+      const url = !!state.ruleForm.id ? "/rechargeConfig/modify" : "/rechargeConfig/add";
+      $body(url, payload).then(() => {
+        state.btnLoading = false;
+        Msg.message('操作成功');
+        onClose();
+        emit('refresh');
+      }).catch(() => {
+        state.btnLoading = false;
+        Msg.message('操作失败', 'error');
+      });
+    } else {
+      Msg.message('请先完整填写表单', 'error');
+    }
+  });
+};
+
+defineExpose({ open });
+</script>

+ 151 - 0
admin-web/src/views/admin/platform/rechargeConfig/index.vue

@@ -0,0 +1,151 @@
+<style scoped lang="scss">
+.system-container {
+
+  :deep(.el-card__body) {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    flex: 1;
+    overflow: auto;
+
+    .el-table {
+      flex: 1;
+    }
+
+  }
+}
+
+.page-content {
+  margin-bottom: 20px;
+}
+</style>
+<template>
+  <div class="system-container layout-padding">
+    <el-card shadow="hover" class="layout-padding-auto">
+      <div class="page-search mb10">
+        <el-select-v2
+            v-model="state.filterStationId"
+            :options="state.stationOptions"
+            placeholder="按站点筛选"
+            clearable
+            style="width: 240px"
+            @change="loadData"
+        />
+        <el-button v-auth="'rechargeConfig.add'" size="default" plain type="success" class="ml10" @click="handleRowClick('add', null)">
+          <SvgIcon name="ele-FolderAdd"/>
+          新增
+        </el-button>
+      </div>
+
+      <el-table
+          border
+          stripe
+          :height="state.tableData.height"
+          highlight-current-row
+          row-key="id"
+          :data="state.tableData.data"
+          v-loading="state.tableData.loading">
+        <template #empty>
+          <el-empty></el-empty>
+        </template>
+
+        <el-table-column label="站点" prop="stationId" width="160">
+          <template #default="{ row }">
+            <el-tag v-if="row.stationId" type="success">{{ row.stationId }}</el-tag>
+            <el-tag v-else type="info">平台默认</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="充值金额" prop="rechargeAmount" width="140">
+          <template #default="{ row }">
+            {{ u.fmt.fmtMoney(row.rechargeAmount) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="赠款金额" prop="grantsAmount" width="140">
+          <template #default="{ row }">
+            {{ u.fmt.fmtMoney(row.grantsAmount) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="标签" prop="label" min-width="120">
+          <template #default="{ row }">
+            {{ row.label || '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column label="创建时间" prop="createTime" width="180">
+          <template #default="{ row }">
+            {{ u.fmt.fmtDateTime(row.createTime) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" prop="action" width="160" align="center" fixed="right">
+          <template #default="{ row }">
+            <el-button v-auth="'rechargeConfig.modify'" type="warning" size="small" text @click="handleRowClick('edit', row)">编辑</el-button>
+            <el-button v-auth="'rechargeConfig.remove'" type="danger" size="small" text @click="handleRowDelete(row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+  </div>
+  <RechargeConfigDialog ref="rechargeConfigDialogRef" @refresh="loadData"/>
+</template>
+
+<script setup lang="ts" name="AdminRechargeConfig">
+import { defineAsyncComponent, reactive, onMounted, ref, nextTick } from 'vue';
+import { $body, $get } from "/@/utils/request";
+import u from '/@/utils/u'
+import { Msg } from "/@/utils/message";
+
+const RechargeConfigDialog = defineAsyncComponent(() => import("/@/views/admin/platform/rechargeConfig/dialog.vue"));
+
+const rechargeConfigDialogRef = ref();
+
+const state = reactive({
+  filterStationId: null as string | null,
+  stationOptions: [] as Array<{ value: string; label: string }>,
+  tableData: {
+    height: 500,
+    data: [] as Array<any>,
+    loading: false,
+  },
+});
+
+onMounted(() => {
+  loadStations();
+  loadData();
+});
+
+const loadStations = () => {
+  $body('/washStation/list', { pageNum: 1, pageSize: 200 }).then((res: any) => {
+    state.stationOptions = [
+      { value: '__all__', label: '全部' },
+      ...(res.list || []).map((s: any) => ({ value: s.stationId, label: s.stationName + ' (' + s.stationId + ')' })),
+    ];
+  });
+};
+
+const loadData = () => {
+  state.tableData.loading = true;
+  const url = state.filterStationId && state.filterStationId !== '__all__'
+    ? `/rechargeConfig/list?stationId=${state.filterStationId}`
+    : '/rechargeConfig/listAll';
+  $get(url).then((res: any) => {
+    state.tableData.data = res || [];
+    state.tableData.loading = false;
+  }).catch(() => {
+    state.tableData.loading = false;
+  });
+};
+
+const handleRowClick = (type: string, row: any) => {
+  rechargeConfigDialogRef.value.open(type, row);
+};
+
+const handleRowDelete = (row: any) => {
+  Msg.confirm(`此操作将永久删除该充值配置,是否继续?`).then(() => {
+    $get(`/rechargeConfig/remove/${row.id}`).then(() => {
+      Msg.message("删除成功", 'success');
+      loadData();
+    }).catch(() => {
+      Msg.message("删除失败", 'error');
+    });
+  });
+};
+</script>