|
|
@@ -0,0 +1,237 @@
|
|
|
+<template>
|
|
|
+ <div class="energy-behavior-config admin-page">
|
|
|
+ <div class="header">
|
|
|
+ <h2>能量行为配置</h2>
|
|
|
+ <div class="filters">
|
|
|
+ <el-input v-model="keyword" placeholder="搜索行为名称/编码..." prefix-icon="el-icon-search" style="width:260px" clearable @keyup.enter.native="loadList" @clear="loadList" />
|
|
|
+ <el-select v-model="enabledFilter" placeholder="状态筛选" @change="loadList" style="width: 140px;" clearable>
|
|
|
+ <el-option label="全部" value="" />
|
|
|
+ <el-option label="已启用" value="1" />
|
|
|
+ <el-option label="已禁用" value="0" />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-table :data="list" v-loading="loading" border stripe highlight-current-row>
|
|
|
+ <el-table-column prop="behaviorCode" label="行为编码" width="160" />
|
|
|
+ <el-table-column prop="behaviorName" label="行为名称" width="130" />
|
|
|
+ <el-table-column prop="description" label="描述" min-width="160" show-overflow-tooltip />
|
|
|
+ <el-table-column label="默认值" width="80">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-tag size="mini" type="info">{{ row.defaultAmount }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="amountSource" label="值来源" width="120">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-tag size="mini" :type="sourceTypeTag(row.amountSource)">{{ sourceTypeLabel(row.amountSource) }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="维度分配" min-width="200">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <span v-if="!row.dimensionAssign" style="color:#999">未配置</span>
|
|
|
+ <el-tag v-for="d in parseDimAssign(row.dimensionAssign)" :key="d.dim" size="mini" :style="{ background: dimColor(d.dim), color: '#fff', marginRight: '4px' }">
|
|
|
+ {{ dimLabel(d.dim) }} {{ d.ratio }}%
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="每日上限" width="80">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <span>{{ row.dailyLimit || '不限' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="过期天数" width="80">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <span>{{ row.expireDays || '永久' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="状态" width="80">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-switch :value="row.enabled === 1" @change="toggleStatus(row)" />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="120" fixed="right">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-button size="mini" type="primary" @click="showEdit(row)">编辑</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <el-dialog title="编辑能量行为配置" :visible.sync="dialogVisible" width="700px" :close-on-click-modal="false">
|
|
|
+ <el-form :model="form" label-width="110px" size="small">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="行为编码">
|
|
|
+ <el-input v-model="form.behaviorCode" disabled />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="行为名称">
|
|
|
+ <el-input v-model="form.behaviorName" disabled />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-form-item label="描述">
|
|
|
+ <el-input v-model="form.description" disabled />
|
|
|
+ </el-form-item>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="默认能量值">
|
|
|
+ <el-input-number v-model="form.defaultAmount" :min="0" :max="9999" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="值来源">
|
|
|
+ <el-select v-model="form.amountSource" style="width:100%">
|
|
|
+ <el-option label="固定值" value="fixed" />
|
|
|
+ <el-option label="跟随积分" value="points" />
|
|
|
+ <el-option label="跟随金额" value="order_amount" />
|
|
|
+ <el-option label="从配置表读取" value="config" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="过期天数">
|
|
|
+ <el-input-number v-model="form.expireDays" :min="0" :max="3650" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="每日上限">
|
|
|
+ <el-input-number v-model="form.dailyLimit" :min="0" :max="9999" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="冷却时间(秒)">
|
|
|
+ <el-input-number v-model="form.cooldownSeconds" :min="0" :max="86400" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-form-item label="维度分配">
|
|
|
+ <div v-for="(d, i) in dimAssignList" :key="i" style="display:flex;align-items:center;margin-bottom:8px;gap:8px">
|
|
|
+ <el-select v-model="d.dim" style="width:120px" @change="normalizeDimRatio">
|
|
|
+ <el-option v-for="dim in dimensions" :key="dim.code" :label="dim.name" :value="dim.code" />
|
|
|
+ </el-select>
|
|
|
+ <el-slider v-model="d.ratio" :min="0" :max="100" style="flex:1" />
|
|
|
+ <span style="width:40px;text-align:right">{{ d.ratio }}%</span>
|
|
|
+ <el-button type="danger" size="mini" icon="el-icon-delete" circle @click="removeDim(i)" v-if="dimAssignList.length > 1" />
|
|
|
+ </div>
|
|
|
+ <el-button size="mini" @click="addDim" v-if="dimAssignList.length < 5">+ 添加维度</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <span slot="footer">
|
|
|
+ <el-button @click="dialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="saveConfig">保存</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { getEnergyBehaviorList, updateEnergyBehavior, toggleEnergyBehavior } from '@/api/energy'
|
|
|
+
|
|
|
+const DIMENSION_MAP = {
|
|
|
+ body: { name: '身', color: '#FF8C42' },
|
|
|
+ mind: { name: '心', color: '#FF6B9D' },
|
|
|
+ wisdom: { name: '智', color: '#6366F1' },
|
|
|
+ action: { name: '行', color: '#10B981' },
|
|
|
+ wealth: { name: '富', color: '#F59E0B' }
|
|
|
+}
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'EnergyBehaviorConfig',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ keyword: '',
|
|
|
+ enabledFilter: '',
|
|
|
+ loading: false,
|
|
|
+ list: [],
|
|
|
+ dialogVisible: false,
|
|
|
+ form: {},
|
|
|
+ dimAssignList: [],
|
|
|
+ dimensions: Object.entries(DIMENSION_MAP).map(([code, v]) => ({ code, name: v.name }))
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.loadList()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ async loadList() {
|
|
|
+ this.loading = true
|
|
|
+ try {
|
|
|
+ const res = await getEnergyBehaviorList()
|
|
|
+ if (res.code === 200) {
|
|
|
+ this.list = (res.data || []).filter(r => {
|
|
|
+ if (this.keyword) {
|
|
|
+ const kw = this.keyword.toLowerCase()
|
|
|
+ if (!r.behaviorCode.toLowerCase().includes(kw) && !r.behaviorName.toLowerCase().includes(kw)) return false
|
|
|
+ }
|
|
|
+ if (this.enabledFilter !== '') {
|
|
|
+ if (r.enabled !== parseInt(this.enabledFilter)) return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ showEdit(row) {
|
|
|
+ this.form = { ...row }
|
|
|
+ this.dimAssignList = this.parseDimAssign(row.dimensionAssign)
|
|
|
+ if (this.dimAssignList.length === 0) {
|
|
|
+ this.dimAssignList = [{ dim: 'action', ratio: 100 }]
|
|
|
+ }
|
|
|
+ this.dialogVisible = true
|
|
|
+ },
|
|
|
+ parseDimAssign(json) {
|
|
|
+ if (!json) return []
|
|
|
+ try { return JSON.parse(json) } catch { return [] }
|
|
|
+ },
|
|
|
+ async saveConfig() {
|
|
|
+ const dimensionAssign = JSON.stringify(this.dimAssignList.filter(d => d.ratio > 0))
|
|
|
+ const payload = { ...this.form, dimensionAssign }
|
|
|
+ const res = await updateEnergyBehavior(payload)
|
|
|
+ if (res.code === 200) {
|
|
|
+ this.$message.success('保存成功')
|
|
|
+ this.dialogVisible = false
|
|
|
+ this.loadList()
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.message || '保存失败')
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async toggleStatus(row) {
|
|
|
+ const res = await toggleEnergyBehavior(row.id)
|
|
|
+ if (res.code === 200) {
|
|
|
+ this.$message.success(res.message)
|
|
|
+ this.loadList()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ addDim() {
|
|
|
+ const used = this.dimAssignList.map(d => d.dim)
|
|
|
+ const next = this.dimensions.find(d => !used.includes(d.code))
|
|
|
+ if (next) this.dimAssignList.push({ dim: next.code, ratio: 0 })
|
|
|
+ },
|
|
|
+ removeDim(i) {
|
|
|
+ this.dimAssignList.splice(i, 1)
|
|
|
+ this.normalizeDimRatio()
|
|
|
+ },
|
|
|
+ normalizeDimRatio() {
|
|
|
+ // 自动调整,确保总和为100
|
|
|
+ },
|
|
|
+ sourceTypeLabel(v) {
|
|
|
+ return { fixed: '固定值', points: '跟随积分', order_amount: '跟随金额', config: '配置表读取' }[v] || v
|
|
|
+ },
|
|
|
+ sourceTypeTag(v) {
|
|
|
+ return { fixed: '', points: 'warning', order_amount: 'danger', config: 'info' }[v] || ''
|
|
|
+ },
|
|
|
+ dimLabel(code) { return (DIMENSION_MAP[code] || {}).name || code },
|
|
|
+ dimColor(code) { return (DIMENSION_MAP[code] || {}).color || '#999' }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
|
|
+.filters { display: flex; gap: 10px; align-items: center; }
|
|
|
+</style>
|