New file |
| | |
| | | <template> |
| | | <div class="points-settings"> |
| | | <div class="settings-content"> |
| | | <!-- 左侧:积分获取规则 --> |
| | | <div class="left-column"> |
| | | <el-card class="rule-section" shadow="never"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span class="section-title">积分获取规则</span> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- 资源贡献 --> |
| | | <div class="rule-category" v-for="rule in rules.getPointsRuleList"> |
| | | <h4>{{ rule.category}}</h4> |
| | | <div class="rule-subcategory"> |
| | | <div class="rule-items"> |
| | | <div class="rule-item" v-for="pointRuleDetial in rule.pointsRules"> |
| | | <span class="rule-desc">{{ pointRuleDetial.ruleName }} : {{ pointRuleDetial.ruleDescription}} </span> |
| | | <el-input |
| | | v-model="pointRuleDetial.pointsValue" |
| | | size="small" |
| | | style="width: 80px;" |
| | | placeholder="请输入" |
| | | readonly |
| | | /> |
| | | <span class="unit">积分</span> |
| | | <span class="unit" v-if="pointRuleDetial.isLimit === 1">,每日积分上限</span> |
| | | <el-input |
| | | v-model="pointRuleDetial.dailyLimit" |
| | | size="small" |
| | | style="width: 80px;" |
| | | placeholder="请输入" |
| | | readonly |
| | | v-if="pointRuleDetial.isLimit === 1" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- 右侧:积分消耗规则和积分转换规则 --> |
| | | <div class="right-column"> |
| | | <!-- 积分消耗规则 --> |
| | | <el-card class="rule-section" shadow="never"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span class="section-title">积分消耗规则</span> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- 资源传播 --> |
| | | <div class="rule-category" v-for="consumeRule in rules.consumePointsRuleList"> |
| | | <h4>{{ consumeRule.category }}</h4> |
| | | <div class="rule-subcategory"> |
| | | <div class="rule-items"> |
| | | <div class="rule-item" v-for="pointRuleDetials in consumeRule.pointsRules"> |
| | | <span class="rule-desc">{{ pointRuleDetials.ruleName }} : {{ pointRuleDetials.ruleDescription}}</span> |
| | | <el-input |
| | | v-model="pointRuleDetials.pointsValue" |
| | | size="small" |
| | | style="width: 60px;" |
| | | placeholder="请输入" |
| | | readonly |
| | | /> |
| | | <span class="unit">积分</span> |
| | | <span class="unit" v-if="pointRuleDetials.isLimit === 1">,每日积分上限</span> |
| | | <el-input |
| | | v-model="pointRuleDetials.dailyLimit" |
| | | size="small" |
| | | style="width: 80px;" |
| | | placeholder="请输入" |
| | | readonly |
| | | v-if="pointRuleDetials.isLimit === 1" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | </el-card> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 底部按钮 --> |
| | | <div class="bottom-actions"> |
| | | <el-button @click="goBack" size="large">返回</el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref, onMounted, watch } from 'vue' |
| | | import { useRoute } from 'vue-router' |
| | | import { ElMessage } from 'element-plus' |
| | | import pointsApi from '../../../api/pointsApi' |
| | | |
| | | // 定义props |
| | | interface Props { |
| | | ruleId?: string |
| | | } |
| | | |
| | | const props = withDefaults(defineProps<Props>(), { |
| | | ruleId: '' |
| | | }) |
| | | |
| | | // 定义emits |
| | | const emit = defineEmits<{ |
| | | goBack: [] |
| | | }>() |
| | | |
| | | // 获取路由实例 |
| | | const route = useRoute() |
| | | // 获取路由参数中的规则id,优先使用props中的ruleId |
| | | const ruleId = props.ruleId || (route.query.ruleId as string) |
| | | |
| | | interface PointRule { |
| | | category: string, |
| | | pointsRules: PointRuleDetial[] |
| | | } |
| | | interface PointRuleDetial { |
| | | category: string, |
| | | id: number, |
| | | ruleDescription: string, |
| | | ruleName: string, |
| | | createdAt: string, |
| | | ruleType: number, |
| | | pointsWinner: number, |
| | | isLimit: number, |
| | | pointsValue: number, |
| | | dailyLimit: number |
| | | } |
| | | |
| | | // 积分规则配置 |
| | | interface PointsRules { |
| | | getPointsRuleList: PointRule[], |
| | | consumePointsRuleList: PointRule[] |
| | | } |
| | | |
| | | const rules = ref<PointsRules>({ |
| | | consumePointsRuleList: [], |
| | | getPointsRuleList: [] |
| | | }) |
| | | |
| | | // 获取积分规则配置 |
| | | const getPointsRules = async () => { |
| | | try { |
| | | if (ruleId) { |
| | | // 如果有ruleId参数,则获取特定规则 |
| | | const formData = new URLSearchParams(); |
| | | formData.append('ruleId', ruleId); |
| | | |
| | | const res = await pointsApi.getPointsRuleById(formData) |
| | | if (res.code === 200 && res.data) { |
| | | // 将后端数据映射到前端规则对象 |
| | | Object.assign(rules.value, res.data) |
| | | console.log(rules.value) |
| | | } |
| | | } else { |
| | | // 如果没有ruleId参数,则获取所有规则 |
| | | const res = await pointsApi.getPointsRules({}) |
| | | if (res.code === 200 && res.data) { |
| | | // 将后端数据映射到前端规则对象 |
| | | Object.assign(rules.value, res.data) |
| | | } |
| | | } |
| | | } catch (error) { |
| | | console.error('获取积分规则失败:', error) |
| | | } |
| | | } |
| | | |
| | | // 返回上一页 |
| | | const goBack = () => { |
| | | // 如果是从组件调用,发出goBack事件 |
| | | if (props.ruleId) { |
| | | emit('goBack') |
| | | } else { |
| | | // 否则使用原有的返回逻辑 |
| | | history.back() |
| | | } |
| | | } |
| | | |
| | | // 监听ruleId变化 |
| | | watch(() => props.ruleId, (newRuleId) => { |
| | | if (newRuleId) { |
| | | getPointsRules() |
| | | } |
| | | }, { immediate: true }) |
| | | |
| | | onMounted(() => { |
| | | // 只有在没有props.ruleId时才执行原有的逻辑 |
| | | if (!props.ruleId) { |
| | | getPointsRules() |
| | | } |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .points-settings { |
| | | padding: 20px; |
| | | background-color: #f5f5f5; |
| | | min-height: 100vh; |
| | | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; |
| | | |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | padding: 16px 20px; |
| | | background: white; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | |
| | | .breadcrumb { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | |
| | | .back-arrow { |
| | | cursor: pointer; |
| | | color: #409eff; |
| | | font-size: 18px; |
| | | |
| | | &:hover { |
| | | color: #66b1ff; |
| | | } |
| | | } |
| | | |
| | | .title { |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | } |
| | | } |
| | | |
| | | .actions { |
| | | display: flex; |
| | | gap: 12px; |
| | | } |
| | | } |
| | | |
| | | .settings-content { |
| | | display: flex; |
| | | gap: 20px; |
| | | max-width: 1665px; |
| | | margin: 0 auto; |
| | | margin-bottom: 40px; |
| | | |
| | | @media (max-width: 768px) { |
| | | flex-direction: column; |
| | | gap: 16px; |
| | | } |
| | | } |
| | | |
| | | .bottom-actions { |
| | | display: flex; |
| | | justify-content: center; |
| | | gap: 20px; |
| | | padding: 20px 0; |
| | | |
| | | .el-button { |
| | | min-width: 120px; |
| | | height: 40px; |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | |
| | | .left-column { |
| | | flex: 1; |
| | | max-width: 50%; |
| | | |
| | | @media (max-width: 768px) { |
| | | max-width: 100%; |
| | | } |
| | | } |
| | | |
| | | .right-column { |
| | | flex: 1; |
| | | max-width: 50%; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | |
| | | @media (max-width: 768px) { |
| | | max-width: 100%; |
| | | gap: 16px; |
| | | } |
| | | } |
| | | |
| | | .rule-section { |
| | | border-radius: 8px; |
| | | border: 1px solid #e4e7ed; |
| | | |
| | | .card-header { |
| | | .section-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #656D9A; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .rule-category { |
| | | margin-bottom: 24px; |
| | | |
| | | h4 { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin: 0 0 16px 0; |
| | | padding-bottom: 8px; |
| | | border-bottom: 1px solid #ebeef5; |
| | | background-color: #f8f9fa; |
| | | padding: 8px 12px; |
| | | border-radius: 4px; |
| | | border-left: 3px solid #409eff; |
| | | } |
| | | |
| | | .rule-subcategory { |
| | | margin-left: 16px; |
| | | margin-bottom: 16px; |
| | | |
| | | h5 { |
| | | font-size: 13px; |
| | | font-weight: 500; |
| | | color: #606266; |
| | | margin: 0 0 12px 0; |
| | | padding-left: 8px; |
| | | } |
| | | } |
| | | |
| | | .rule-items { |
| | | display: flex; |
| | | flex-direction: column; |
| | | margin-left: 16px; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .rule-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 6px; |
| | | padding: 6px 8px; |
| | | border-radius: 4px; |
| | | transition: background-color 0.2s; |
| | | |
| | | &:hover { |
| | | background-color: #f5f7fa; |
| | | } |
| | | |
| | | .rule-desc { |
| | | font-size: 13px; |
| | | color: #606266; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | .unit { |
| | | font-size: 13px; |
| | | color: #606266; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | :deep(.el-input) { |
| | | .el-input__wrapper { |
| | | box-shadow: 0 0 0 1px #dcdfe6 inset; |
| | | background-color: #f5f7fa; |
| | | |
| | | &:hover { |
| | | box-shadow: 0 0 0 1px #c0c4cc inset; |
| | | } |
| | | |
| | | &.is-focus { |
| | | box-shadow: 0 0 0 1px #409eff inset; |
| | | } |
| | | } |
| | | |
| | | .el-input__inner { |
| | | color: #606266; |
| | | cursor: not-allowed; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |