HealthEnergyConfig.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. <template>
  2. <div class="health-energy-config admin-page">
  3. <el-card>
  4. <div slot="header">
  5. <span>七维健康能量配置</span>
  6. <el-button style="float: right" type="primary" size="small" @click="handleSave" :loading="submitting">保存配置</el-button>
  7. </div>
  8. <el-form ref="form" :model="form" label-width="140px" v-loading="loading">
  9. <!-- 维度权重配置 -->
  10. <el-divider content-position="left">各维度权重配置</el-divider>
  11. <el-row :gutter="20">
  12. <el-col :span="12" v-for="dim in dimensions" :key="dim.code">
  13. <el-form-item :label="dim.label + ' 权重'">
  14. <el-slider
  15. v-model="form.weights[dim.code]"
  16. :min="0"
  17. :max="100"
  18. :step="1"
  19. show-input
  20. :format-tooltip="val => val + '%'"
  21. @change="onWeightChange(dim.code)"
  22. />
  23. </el-form-item>
  24. </el-col>
  25. </el-row>
  26. <el-alert
  27. v-if="weightSum !== 100"
  28. :title="'权重总和不等于 100%(当前: ' + weightSum + '%)'"
  29. type="warning"
  30. :closable="false"
  31. style="margin-bottom: 20px;"
  32. />
  33. <el-alert
  34. v-else
  35. title="权重总和 = 100%,配置有效"
  36. type="success"
  37. :closable="false"
  38. style="margin-bottom: 20px;"
  39. />
  40. <el-divider content-position="left">同步系数配置</el-divider>
  41. <el-form-item label="能量同步系数">
  42. <el-slider
  43. v-model="form.syncFactor"
  44. :min="0"
  45. :max="100"
  46. :step="1"
  47. show-input
  48. :format-tooltip="val => (val / 100).toFixed(2)"
  49. style="max-width: 400px;"
  50. />
  51. <div style="color: #909399; font-size: 12px; margin-top: 4px;">
  52. 系数决定七维评分转换为身能量的比例(当前: {{ (form.syncFactor / 100).toFixed(2) }})
  53. 公式: 身能量 = 加权评分 × 系数
  54. </div>
  55. </el-form-item>
  56. <el-divider content-position="left">配置说明</el-divider>
  57. <el-alert
  58. title="计算逻辑"
  59. type="info"
  60. :closable="false"
  61. style="margin-bottom: 10px;"
  62. >
  63. <template slot>
  64. <ul style="margin: 8px 0 0 0; padding-left: 20px; font-size: 13px; color: #606266; line-height: 1.8;">
  65. <li>每次录入七维健康评分后,系统计算加权评分:<code>加权评分 = Σ(各维度评分 × 权重) / 权重总和</code></li>
  66. <li>然后转换为身能量值:<code>身能量 = 加权评分 × 同步系数</code></li>
  67. <li>权重越高,该维度评分对身能量的影响越大</li>
  68. <li>同步系数控制能量发放速度(系数越大,每次同步获得的能量越多)</li>
  69. </ul>
  70. </template>
  71. </el-alert>
  72. </el-form>
  73. </el-card>
  74. </div>
  75. </template>
  76. <script>
  77. export default {
  78. name: 'HealthEnergyConfig',
  79. data() {
  80. return {
  81. loading: false,
  82. submitting: false,
  83. form: {
  84. weights: {
  85. growth: 20,
  86. sleep: 15,
  87. vision: 10,
  88. immunity: 15,
  89. nutrition: 15,
  90. gut: 10,
  91. exercise: 15
  92. },
  93. syncFactor: 50
  94. },
  95. dimensions: [
  96. { code: 'growth', label: '生长发育' },
  97. { code: 'sleep', label: '睡眠质量' },
  98. { code: 'vision', label: '视力健康' },
  99. { code: 'immunity', label: '免疫力' },
  100. { code: 'nutrition', label: '营养均衡' },
  101. { code: 'gut', label: '肠胃健康' },
  102. { code: 'exercise', label: '运动活力' }
  103. ]
  104. }
  105. },
  106. computed: {
  107. weightSum() {
  108. return Object.values(this.form.weights).reduce((sum, v) => sum + v, 0)
  109. }
  110. },
  111. mounted() {
  112. this.loadConfig()
  113. },
  114. methods: {
  115. loadConfig() {
  116. this.loading = true
  117. this.$axios.post('/api/admin/config/all').then(res => {
  118. if (res.data.code === 200) {
  119. const config = res.data.data || {}
  120. // 加载权重配置
  121. const weightsJson = config['health_dimension_weights']
  122. if (weightsJson) {
  123. try {
  124. const weights = JSON.parse(weightsJson)
  125. if (weights && typeof weights === 'object') {
  126. for (const key of Object.keys(this.form.weights)) {
  127. if (weights[key] !== undefined) {
  128. this.form.weights[key] = Math.round(weights[key] * 100)
  129. }
  130. }
  131. }
  132. } catch (e) {
  133. this.$message.warning('权重配置解析失败,使用默认值')
  134. }
  135. }
  136. // 加载同步系数
  137. const factorStr = config['health_dimension_sync_factor']
  138. if (factorStr) {
  139. const factor = parseFloat(factorStr)
  140. if (!isNaN(factor)) {
  141. this.form.syncFactor = Math.round(factor * 100)
  142. }
  143. }
  144. }
  145. }).catch(() => {
  146. this.$message.error('加载配置失败')
  147. }).finally(() => {
  148. this.loading = false
  149. })
  150. },
  151. onWeightChange() {
  152. // 归一化处理:如果总和超过100,等比例压缩
  153. const sum = this.weightSum
  154. if (sum > 0 && sum !== 100) {
  155. const ratio = 100 / sum
  156. for (const key of Object.keys(this.form.weights)) {
  157. this.form.weights[key] = Math.round(this.form.weights[key] * ratio)
  158. }
  159. }
  160. },
  161. handleSave() {
  162. if (this.weightSum !== 100) {
  163. this.$message.warning('权重总和必须等于 100%')
  164. return
  165. }
  166. this.submitting = true
  167. // 将 0-100 的权重值转换为 0-1 的小数
  168. const normalizedWeights = {}
  169. for (const key of Object.keys(this.form.weights)) {
  170. normalizedWeights[key] = this.form.weights[key] / 100
  171. }
  172. const promises = [
  173. this.$axios.post('/api/admin/config/update', {
  174. configKey: 'health_dimension_weights',
  175. configValue: JSON.stringify(normalizedWeights)
  176. }),
  177. this.$axios.post('/api/admin/config/update', {
  178. configKey: 'health_dimension_sync_factor',
  179. configValue: (this.form.syncFactor / 100).toFixed(2)
  180. })
  181. ]
  182. Promise.all(promises).then(res => {
  183. const allSuccess = res.every(r => r.data.code === 200)
  184. if (allSuccess) {
  185. this.$message.success('保存成功')
  186. } else {
  187. this.$message.error('部分配置保存失败')
  188. }
  189. }).catch(() => {
  190. this.$message.error('保存失败')
  191. }).finally(() => {
  192. this.submitting = false
  193. })
  194. }
  195. }
  196. }
  197. </script>
  198. <style scoped>
  199. .health-energy-config {
  200. padding: 20px;
  201. }
  202. .health-energy-config .el-col {
  203. margin-bottom: 0;
  204. }
  205. </style>