|
|
@@ -0,0 +1,347 @@
|
|
|
+<template>
|
|
|
+ <div class="badge-manage-page">
|
|
|
+ <el-card>
|
|
|
+ <div slot="header" class="clearfix">
|
|
|
+ <span>勋章管理</span>
|
|
|
+ <div style="float:right; display:flex; gap:10px;">
|
|
|
+ <el-button type="primary" size="small" @click="showAddDialog">新增勋章</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 搜索 -->
|
|
|
+ <el-form :model="filters" inline size="small" style="margin-bottom:16px;">
|
|
|
+ <el-form-item label="名称">
|
|
|
+ <el-input v-model="filters.name" placeholder="搜索勋章名称" clearable @input="onSearchInput" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="分类">
|
|
|
+ <el-select v-model="filters.category" placeholder="全部分类" clearable @change="fetchList" style="width:140px;">
|
|
|
+ <el-option v-for="c in categoryOptions" :key="c.value" :label="c.label" :value="c.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="等级">
|
|
|
+ <el-select v-model="filters.level" placeholder="全部等级" clearable @change="fetchList" style="width:140px;">
|
|
|
+ <el-option v-for="c in levelOptions" :key="c.value" :label="c.label" :value="c.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-table :data="list" border stripe v-loading="loading" empty-text="暂无数据" style="width:100%" :max-height="tableHeight">
|
|
|
+ <el-table-column prop="id" label="ID" width="60" />
|
|
|
+ <el-table-column prop="name" label="名称" min-width="120" />
|
|
|
+ <el-table-column prop="category" label="分类" width="90" />
|
|
|
+ <el-table-column label="等级" width="80">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ {{ levelMap[row.level] || row.level || '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="稀有度" width="80">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ {{ rarityMap[row.rarity] || row.rarity || '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="启用" width="80" align="center">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-switch
|
|
|
+ :value="row.isActive === 1"
|
|
|
+ active-color="#67C23A"
|
|
|
+ @change="onToggleActive(row)"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="sortOrder" label="排序" width="60" align="center" />
|
|
|
+ <el-table-column prop="createdAt" label="创建时间" width="160" />
|
|
|
+ <el-table-column label="操作" width="160" fixed="right">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-button type="text" @click="editRow(row)">编辑</el-button>
|
|
|
+ <el-popconfirm title="确定删除此勋章吗?" @confirm="deleteRow(row)">
|
|
|
+ <el-button type="text" style="color:#f56c6c" slot="reference">删除</el-button>
|
|
|
+ </el-popconfirm>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 新增/编辑弹窗 -->
|
|
|
+ <el-dialog :visible.sync="dialog.visible" :title="dialog.isEdit ? '编辑勋章' : '新增勋章'" width="600px" :close-on-click-modal="false">
|
|
|
+ <el-form :model="dialog.form" label-width="110px" size="small">
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="名称" required>
|
|
|
+ <el-input v-model="dialog.form.name" placeholder="勋章名称" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="分类" required>
|
|
|
+ <el-select v-model="dialog.form.category" placeholder="请选择" style="width:100%">
|
|
|
+ <el-option v-for="c in categoryOptions" :key="c.value" :label="c.label" :value="c.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="等级">
|
|
|
+ <el-select v-model="dialog.form.level" placeholder="请选择" style="width:100%">
|
|
|
+ <el-option v-for="c in levelOptions" :key="c.value" :label="c.label" :value="c.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="稀有度">
|
|
|
+ <el-select v-model="dialog.form.rarity" placeholder="请选择" style="width:100%">
|
|
|
+ <el-option v-for="c in rarityOptions" :key="c.value" :label="c.label" :value="c.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-form-item label="图标URL">
|
|
|
+ <el-input v-model="dialog.form.icon" placeholder="勋章图标地址" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="描述">
|
|
|
+ <el-input type="textarea" :rows="3" v-model="dialog.form.description" placeholder="勋章描述..." />
|
|
|
+ </el-form-item>
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="触发类型">
|
|
|
+ <el-input v-model="dialog.form.triggerType" placeholder="如: micro_action_count" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="触发阈值">
|
|
|
+ <el-input-number v-model="dialog.form.threshold" :min="0" style="width:100%" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="排序">
|
|
|
+ <el-input-number v-model="dialog.form.sortOrder" :min="0" style="width:100%" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="过期天数">
|
|
|
+ <el-input-number v-model="dialog.form.expireDays" :min="0" style="width:100%" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="是否启用">
|
|
|
+ <el-switch v-model="dialog.form._active" active-color="#67C23A" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="需要审批">
|
|
|
+ <el-switch v-model="dialog.form._needApproval" active-color="#67C23A" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer">
|
|
|
+ <el-button @click="dialog.visible = false" size="small">取消</el-button>
|
|
|
+ <el-button type="primary" @click="saveDialog" size="small" :loading="dialog.saving">保存</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { getBadgeList, createBadge, updateBadge, deleteBadge } from '@/api/badge'
|
|
|
+
|
|
|
+export default {
|
|
|
+ computed: {
|
|
|
+ tableHeight() {
|
|
|
+ return window.innerHeight - 300
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ loading: false,
|
|
|
+ list: [],
|
|
|
+ searchTimer: null,
|
|
|
+ filters: { name: '', category: '', level: '' },
|
|
|
+ categoryOptions: [
|
|
|
+ { value: '学习', label: '学习' },
|
|
|
+ { value: '运动', label: '运动' },
|
|
|
+ { value: '家务', label: '家务' },
|
|
|
+ { value: '阅读', label: '阅读' },
|
|
|
+ { value: '打卡', label: '打卡' },
|
|
|
+ { value: '综合', label: '综合' }
|
|
|
+ ],
|
|
|
+ levelOptions: [
|
|
|
+ { value: 'bronze', label: '铜牌' },
|
|
|
+ { value: 'silver', label: '银牌' },
|
|
|
+ { value: 'gold', label: '金牌' },
|
|
|
+ { value: 'diamond', label: '钻石' }
|
|
|
+ ],
|
|
|
+ rarityOptions: [
|
|
|
+ { value: 'common', label: '普通' },
|
|
|
+ { value: 'rare', label: '稀有' },
|
|
|
+ { value: 'epic', label: '史诗' },
|
|
|
+ { value: 'legendary', label: '传说' }
|
|
|
+ ],
|
|
|
+ levelMap: { bronze: '铜牌', silver: '银牌', gold: '金牌', diamond: '钻石' },
|
|
|
+ rarityMap: { common: '普通', rare: '稀有', epic: '史诗', legendary: '传说' },
|
|
|
+ dialog: {
|
|
|
+ visible: false,
|
|
|
+ isEdit: false,
|
|
|
+ saving: false,
|
|
|
+ form: {
|
|
|
+ id: null,
|
|
|
+ name: '',
|
|
|
+ description: '',
|
|
|
+ icon: '',
|
|
|
+ category: '',
|
|
|
+ level: '',
|
|
|
+ rarity: '',
|
|
|
+ triggerType: '',
|
|
|
+ threshold: 0,
|
|
|
+ sortOrder: 0,
|
|
|
+ expireDays: 0,
|
|
|
+ _active: true,
|
|
|
+ _needApproval: false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() { this.fetchList() },
|
|
|
+ methods: {
|
|
|
+ onSearchInput() {
|
|
|
+ if (this.searchTimer) clearTimeout(this.searchTimer)
|
|
|
+ this.searchTimer = setTimeout(() => { this.fetchList() }, 300)
|
|
|
+ },
|
|
|
+ async fetchList() {
|
|
|
+ this.loading = true
|
|
|
+ try {
|
|
|
+ const res = await getBadgeList()
|
|
|
+ var data = res.data || []
|
|
|
+ // Apply client-side filters
|
|
|
+ if (this.filters.name) {
|
|
|
+ var q = this.filters.name.toLowerCase()
|
|
|
+ data = data.filter(function(d) { return d.name && d.name.toLowerCase().indexOf(q) !== -1 })
|
|
|
+ }
|
|
|
+ if (this.filters.category) {
|
|
|
+ data = data.filter(function(d) { return d.category === this.filters.category }.bind(this))
|
|
|
+ }
|
|
|
+ if (this.filters.level) {
|
|
|
+ data = data.filter(function(d) { return d.level === this.filters.level }.bind(this))
|
|
|
+ }
|
|
|
+ this.list = data
|
|
|
+ } catch (e) {
|
|
|
+ console.error('加载勋章列表失败', e)
|
|
|
+ this.$message.error('加载失败')
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ showAddDialog() {
|
|
|
+ this.dialog.isEdit = false
|
|
|
+ this.dialog.form = {
|
|
|
+ id: null,
|
|
|
+ name: '',
|
|
|
+ description: '',
|
|
|
+ icon: '',
|
|
|
+ category: '',
|
|
|
+ level: '',
|
|
|
+ rarity: '',
|
|
|
+ triggerType: '',
|
|
|
+ threshold: 0,
|
|
|
+ sortOrder: 0,
|
|
|
+ expireDays: 0,
|
|
|
+ _active: true,
|
|
|
+ _needApproval: false
|
|
|
+ }
|
|
|
+ this.dialog.visible = true
|
|
|
+ },
|
|
|
+ editRow(row) {
|
|
|
+ this.dialog.isEdit = true
|
|
|
+ this.dialog.form = {
|
|
|
+ id: row.id,
|
|
|
+ name: row.name || '',
|
|
|
+ description: row.description || '',
|
|
|
+ icon: row.icon || '',
|
|
|
+ category: row.category || '',
|
|
|
+ level: row.level || '',
|
|
|
+ rarity: row.rarity || '',
|
|
|
+ triggerType: row.triggerType || '',
|
|
|
+ threshold: row.threshold != null ? row.threshold : 0,
|
|
|
+ sortOrder: row.sortOrder != null ? row.sortOrder : 0,
|
|
|
+ expireDays: row.expireDays != null ? row.expireDays : 0,
|
|
|
+ _active: row.isActive === 1,
|
|
|
+ _needApproval: !!row.needApproval
|
|
|
+ }
|
|
|
+ this.dialog.visible = true
|
|
|
+ },
|
|
|
+ async saveDialog() {
|
|
|
+ var form = this.dialog.form
|
|
|
+ if (!form.name) {
|
|
|
+ this.$message.warning('请填写勋章名称')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!form.category) {
|
|
|
+ this.$message.warning('请选择分类')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.dialog.saving = true
|
|
|
+ try {
|
|
|
+ var payload = {
|
|
|
+ id: form.id,
|
|
|
+ name: form.name,
|
|
|
+ description: form.description,
|
|
|
+ icon: form.icon,
|
|
|
+ category: form.category,
|
|
|
+ level: form.level,
|
|
|
+ rarity: form.rarity,
|
|
|
+ triggerType: form.triggerType,
|
|
|
+ threshold: form.threshold,
|
|
|
+ sortOrder: form.sortOrder,
|
|
|
+ expireDays: form.expireDays,
|
|
|
+ isActive: form._active ? 1 : 0,
|
|
|
+ needApproval: form._needApproval
|
|
|
+ }
|
|
|
+ if (form.id) {
|
|
|
+ await updateBadge(payload)
|
|
|
+ this.$message.success('更新成功')
|
|
|
+ } else {
|
|
|
+ await createBadge(payload)
|
|
|
+ this.$message.success('创建成功')
|
|
|
+ }
|
|
|
+ this.dialog.visible = false
|
|
|
+ this.fetchList()
|
|
|
+ } catch (e) {
|
|
|
+ console.error('保存失败', e)
|
|
|
+ this.$message.error('保存失败')
|
|
|
+ } finally {
|
|
|
+ this.dialog.saving = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async deleteRow(row) {
|
|
|
+ try {
|
|
|
+ await deleteBadge(row.id)
|
|
|
+ this.$message.success('删除成功')
|
|
|
+ this.fetchList()
|
|
|
+ } catch (e) {
|
|
|
+ console.error('删除失败', e)
|
|
|
+ this.$message.error('删除失败')
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async onToggleActive(row) {
|
|
|
+ try {
|
|
|
+ var newVal = row.isActive === 1 ? 0 : 1
|
|
|
+ await updateBadge({ id: row.id, isActive: newVal })
|
|
|
+ this.$message.success(newVal === 1 ? '已启用' : '已禁用')
|
|
|
+ row.isActive = newVal
|
|
|
+ } catch (e) {
|
|
|
+ console.error('操作失败', e)
|
|
|
+ this.$message.error('操作失败')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.badge-manage-page {
|
|
|
+ padding: 16px;
|
|
|
+}
|
|
|
+</style>
|