Bläddra i källkod

feat: 管理端虚拟支付道具映射CRUD页面

VirtualGoodsConfig.vue照KnowledgeTag模板,路由/菜单/权限注册
asus 1 månad sedan
förälder
incheckning
c33aba66fe

+ 13 - 0
cfc-web/src/api/virtualGoods.js

@@ -0,0 +1,13 @@
+import request from '@/utils/request'
+
+export function getVirtualGoodsList(data) {
+  return request({ url: '/api/admin/virtual-goods/list', method: 'post', data })
+}
+
+export function saveVirtualGoods(data) {
+  return request({ url: '/api/admin/virtual-goods/save', method: 'post', data })
+}
+
+export function deleteVirtualGoods(data) {
+  return request({ url: '/api/admin/virtual-goods/delete', method: 'post', data })
+}

+ 6 - 0
cfc-web/src/router/index.js

@@ -331,6 +331,12 @@ const routes = [
         component: () => import('@/views/admin/KnowledgeTag.vue'),
         meta: { title: '知识标签', perm: 'articles:categories' }
       },
+      {
+        path: 'virtual-goods-config',
+        name: 'VirtualGoodsConfig',
+        component: () => import('@/views/admin/VirtualGoodsConfig.vue'),
+        meta: { title: '虚拟支付道具', perm: 'system:config' }
+      },
       // ========== 佣金推荐系统 ==========
       {
         path: 'product-profit-rate',

+ 1 - 0
cfc-web/src/views/Layout.vue

@@ -272,6 +272,7 @@ export default {
                 { path: '/operation-logs', label: '操作日志', icon: 'el-icon-s-order', perm: 'system:logs' },
                 { path: '/membership-center', label: '会员中心', icon: 'el-icon-s-custom', perm: 'system:config' },
                 { path: '/badge-manage', label: '勋章管理', icon: 'el-icon-medal', perm: 'system:config' },
+                { path: '/virtual-goods-config', label: '虚拟支付道具', icon: 'el-icon-goods', perm: 'system:config' },
               ]},
             // --- 配置中心 ---
             { title: '配置中心', icon: 'el-icon-setting', perm: 'system:config',

+ 212 - 0
cfc-web/src/views/admin/VirtualGoodsConfig.vue

@@ -0,0 +1,212 @@
+<template>
+  <div class="virtual-goods-config admin-page">
+    <el-card>
+      <div slot="header">
+        <span>虚拟支付道具</span>
+        <div style="float: right; display: flex; gap: 10px;">
+          <el-select v-model="filterGoodsType" placeholder="道具类型" size="small" clearable style="width: 150px;" @change="loadData">
+            <el-option label="测评套餐" value="ASSESSMENT_PACKAGE" />
+            <el-option label="会员" value="MEMBERSHIP" />
+            <el-option label="订阅" value="SUBSCRIPTION" />
+          </el-select>
+          <el-select v-model="filterStatus" placeholder="状态" size="small" clearable style="width: 110px;">
+            <el-option label="启用" value="1" />
+            <el-option label="停用" value="0" />
+          </el-select>
+          <el-button type="primary" size="small" @click="handleCreate">+ 新增道具</el-button>
+        </div>
+      </div>
+      <el-table :max-height="tableHeight" :data="filteredList" v-loading="loading" border stripe>
+        <el-table-column prop="id" label="ID" width="70" />
+        <el-table-column label="道具类型" width="120">
+          <template slot-scope="{ row }">
+            <el-tag size="small" :type="row.goodsType === 'ASSESSMENT_PACKAGE' ? 'primary' : (row.goodsType === 'MEMBERSHIP' ? 'success' : 'warning')">
+                {{ goodsTypeText(row.goodsType) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="bizKey" label="业务SKU" min-width="120" />
+        <el-table-column prop="productId" label="道具ID" min-width="140" />
+        <el-table-column prop="goodsName" label="道具名称" min-width="140" />
+        <el-table-column label="状态" width="90">
+          <template slot-scope="{ row }">
+            <el-switch :value="row.status === 1" disabled />
+          </template>
+        </el-table-column>
+        <el-table-column prop="createTime" label="创建时间" width="170" />
+        <el-table-column prop="updateTime" label="更新时间" width="170" />
+        <el-table-column label="操作" width="180" fixed="right">
+          <template slot-scope="{ row }">
+            <el-button size="mini" type="primary" @click="handleEdit(row)">编辑</el-button>
+            <el-dropdown trigger="hover" @command="(cmd) => handleActionCmd(row, cmd)">
+              <el-button size="mini">
+                更多<i class="el-icon-arrow-down el-icon--right"></i>
+              </el-button>
+              <el-dropdown-menu slot="dropdown">
+                <el-dropdown-item command="delete" icon="el-icon-delete">删除</el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+
+    <el-dialog :visible.sync="dialogVisible" :title="isEdit ? '编辑道具' : '新增道具'" width="480px" :close-on-click-modal="false">
+      <el-form :model="form" label-width="90px">
+        <el-form-item label="道具类型">
+          <el-select v-model="form.goodsType" placeholder="选择道具类型" style="width: 220px;">
+            <el-option label="测评套餐" value="ASSESSMENT_PACKAGE" />
+            <el-option label="会员" value="MEMBERSHIP" />
+            <el-option label="订阅" value="SUBSCRIPTION" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="业务SKU">
+          <el-input v-model="form.bizKey" placeholder="请输入业务SKU标识" />
+        </el-form-item>
+        <el-form-item label="道具ID">
+          <el-input v-model="form.productId" placeholder="请输入道具ID" />
+          <div style="color:#909399;font-size:12px;line-height:1.5;margin-top:4px;">需与微信商户后台道具 ID 一致;上线前必须配置</div>
+        </el-form-item>
+        <el-form-item label="道具名称">
+          <el-input v-model="form.goodsName" placeholder="请输入道具名称" />
+        </el-form-item>
+        <el-form-item label="状态">
+          <el-switch v-model="form.status" :active-value="1" :inactive-value="0" active-text="启用" inactive-text="停用" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer">
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="handleSubmit" :loading="submitting">保存</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { getVirtualGoodsList, saveVirtualGoods, deleteVirtualGoods } from '@/api/virtualGoods.js'
+
+export default {
+  name: 'VirtualGoodsConfig',
+  data() {
+    return {
+      loading: false,
+      submitting: false,
+      list: [],
+      filterGoodsType: '',
+      filterStatus: '',
+      dialogVisible: false,
+      isEdit: false,
+      form: {
+        id: null,
+        goodsType: '',
+        bizKey: '',
+        productId: '',
+        goodsName: '',
+        status: 1
+      }
+    }
+  },
+  computed: {
+    tableHeight() {
+      return window.innerHeight - 300
+    },
+    filteredList() {
+      if (this.filterStatus === '' || this.filterStatus === null || this.filterStatus === undefined) return this.list
+      var target = Number(this.filterStatus)
+      return this.list.filter(function(item) { return item.status === target })
+    }
+  },
+  created() {
+    this.loadData()
+  },
+  methods: {
+    goodsTypeText(type) {
+      if (type === 'ASSESSMENT_PACKAGE') return '测评套餐'
+      if (type === 'MEMBERSHIP') return '会员'
+      if (type === 'SUBSCRIPTION') return '订阅'
+      return type || '-'
+    },
+    async loadData() {
+      this.loading = true
+      try {
+        var params = this.filterGoodsType ? { goodsType: this.filterGoodsType } : {}
+        var res = await getVirtualGoodsList(params)
+        if (res.data) {
+          this.list = res.data
+        }
+      } catch (e) {
+        this.$message.error('加载道具列表失败')
+      } finally {
+        this.loading = false
+      }
+    },
+    handleCreate() {
+      this.isEdit = false
+      this.form = { id: null, goodsType: '', bizKey: '', productId: '', goodsName: '', status: 1 }
+      this.dialogVisible = true
+    },
+    handleEdit(row) {
+      this.isEdit = true
+      this.form = { id: row.id, goodsType: row.goodsType, bizKey: row.bizKey, productId: row.productId, goodsName: row.goodsName, status: row.status }
+      this.dialogVisible = true
+    },
+    async handleSubmit() {
+      if (!this.form.goodsType) {
+        this.$message.warning('请选择道具类型')
+        return
+      }
+      if (!this.form.bizKey || !this.form.bizKey.trim()) {
+        this.$message.warning('请输入业务SKU')
+        return
+      }
+      if (!this.form.productId || !this.form.productId.trim()) {
+        this.$message.warning('请输入道具ID')
+        return
+      }
+      if (!this.form.goodsName || !this.form.goodsName.trim()) {
+        this.$message.warning('请输入道具名称')
+        return
+      }
+      this.submitting = true
+      try {
+        await saveVirtualGoods(this.form)
+        this.$message.success('保存成功')
+        this.dialogVisible = false
+        this.loadData()
+      } catch (e) {
+        this.$message.error(e.message || '保存失败')
+      } finally {
+        this.submitting = false
+      }
+    },
+    async handleDelete(row) {
+      this.$confirm('确定删除道具「' + row.goodsName + '」吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(async () => {
+        try {
+          await deleteVirtualGoods({ id: row.id })
+          this.$message.success('删除成功')
+          this.loadData()
+        } catch (e) {
+          this.$message.error(e.message || '删除失败')
+        }
+      }).catch(() => {})
+    },
+    handleActionCmd(row, cmd) {
+      switch (cmd) {
+        case 'delete':
+          this.handleDelete(row)
+          break
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.virtual-goods-config {
+  padding: 20px;
+}
+</style>