Просмотр исходного кода

fix(cfc-web): admin edit pages and CHANGELOG updates

Ultraworked with Sisyphus

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
User 2 месяцев назад
Родитель
Сommit
5e7d9d90f5

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-34eaf000892bd22a570d9efcbe2d8a6d13503ff3
+7aea8dbf2dc6637dbfb4ee75d75defd0e89b068c

+ 104 - 0
cfc-web/CHANGELOG.md

@@ -1,6 +1,110 @@
 # 更新日志 (Changelog)
 
 此文件记录所有构建版本的变更。
+## v$(node (2026-07-07)
+
+### 重构
+- rebuild admin dashboard
+
+### 其他
+- - Use API module (getDashboardStats/getRevenueStats/getMembershipStats/getTrendStats)
+- - Element UI icons replacing emojis
+- - Section-level v-loading states
+- - ECharts lifecycle (dispose on destroy, resize handler)
+- - Recent tasks table from dashboard API
+- - Hover effects and responsive layout
+- 
+- - ~50 admin/9 teacher 页面统一添加 admin-page 样式类
+- - 所有列表/管理页添加 <=el-input> 关键字搜索
+- - 有分页的: keyword 传 API 参数
+- - 无分页的: computed filteredData 客户端过滤
+- - teacher 页面同时补齐 admin-page 类与面包屑样式
+- 
+- - DatabaseInitializer migration 34: ensureColumn for all missing columns
+-   - family_member_* (5): id, name, birthday, gender, age
+-   - EMI psych (4): emotion, resilience, stress_coping, self_awareness
+-   - Big Five (6): openness, conscientiousness, extraversion, agreeableness, neuroticism, big_five_overall
+-   - child snapshot (20): name, birthday, gender, age, school, grade, grade_class, height, weight,
+-     family_status, parent_degree, nation, province, city, district, address, single_child,
+-     school_score, urgent_name, urgent_phone
+-   - media (4): pics, videos, audio, audio_url
+-   - extension (2): process_desc, result_desc
+- 
+-   (修复Field doesn't have a default value错误)
+- - schema.sql同步: capability_role列+default_role列并存(兼容保留)
+- 
+-   supply_settlement_detail 4张表 + product_orders追加supply_system_id/
+-   supplier_id/supply_commission 3列+索引
+- - schema.sql: product_orders补列 + 追加4张新表定义
+- - supply_system_schema.sql: supplier_user_id→supplier_id(匹配实体) + 补充supply_commission
+- 
+- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
+- 
+-   EnergyService.calculateBodyScore Unknown column 'subject_id' 错误)
+- - 迁移32: 创建supply_system/supply_system_member/supply_settlement/
+-   supply_settlement_detail 4张表 + product_orders追加supply_system_id/
+-   supplier_id/supply_commission 3列+索引 (修复OrderTimeoutCancelTask
+-   Unknown column 'supply_system_id' 错误)
+- 
+- - ProductDTO.java: add deliveryMethod, rejectReason fields + from() mapping
+- - DatabaseInitializer: migration 32 - ensureColumn for delivery_method, category_id, distribution_system_id
+- - schema.sql: add 3 columns to products table DDL
+- 
+- - report-confirm.vue: add colored confidence badge + progress bar
+- - report-confirm.vue: add ranked candidate member list with similarity scores
+- - report-confirm.vue: add auto-create member form with pre-filled extracted data
+- 
+- 替代: import E from '@wangeditor/editor' + new E(domRef)
+- 移除: initEditor()方法、beforeDestroy editor.destroy()、editor: null data
+- 新增: editorConfig (MENU_CONF.uploadImage) 传入Editor组件
+- 
+- - Add Jupiter 12yr and Saturn 29.5yr cycle phase calculation
+- - Blend 60% Western + 40% Chinese zodiac with Tai Sui relation modifiers
+- - Update controller to accept birthMonth/birthDay, return full fusion result
+- 
+- - 6步标准流程:错误定位→查实体→检查schema.sql→加迁移→同步schema.sql→编译验证
+- - ensureColumn() 辅助方法优先使用
+- - 幂等性要求:所有迁移必须可重复执行
+- - 同步原则:schema.sql 与 DatabaseInitializer 保持最终一致
+- - 4种场景代码模板:ADD COLUMN / CREATE TABLE / ADD INDEX / MODIFY COLUMN
+- 
+
+### Bug 修复
+- FamilyRelationGraph 完善 + login/family-dashboard 页面更新
+- add dan_assessment_results missing columns - familyMember/EMI/Big Five/snapshot fields
+- relationship_types.capability_role 添加默认值
+- 迁移33 供应商分销体系(4新表+product_orders 3列)
+- health_reports补subject_id列 + 迁移32供商体系表(4张新表+product_orders 3列)
+- HealthEnergyConfig.vue 全角括号导致前端构建失败
+- add deliveryMethod/rejectReason/categoryId/distributionSystemId mappings - ProductEdit field alignment
+- 多页面综合修复 + 后端关系质量/能量同步 + 前端组件完善 - 后端: DatabaseInitializer/RelationshipQualityController/DimensionEnergySyncService - 前端组件: ContactImport/DimensionActivities 完善 - 页面: action/body/mind/wisdom/discover/health 各维度更新 - splash.vue 删除 - 新增 HealthEnergyConfig.vue 管理端页面 - router/Layout 同步更新
+- resolve rebase conflicts + update tests/docs
+- resolve .last_build_commit conflict
+- ProductEdit wangeditor 生产构建错误 — 改用 @wangeditor/editor-for-vue Vue组件替换programmatic API
+- product order stock check by product type (physical only)
+- resolve merge conflicts and sync build artifacts
+- create cart table migration and sync to schema.sql
+- add relationship score column migrations for family_members table
+- add require_registration, require_checkin_review, energy_penalty_unregistered migrations for activities table
+- add role_override column migration for family_members table
+- add default_role column migration for relationship_types table
+- add remaining child-specific column migrations for family_members table
+- activities表添加审核相关字段迁移(迁移25)
+- package.json 版本更新
+- add id_card column migration for family_members table
+
+### 文档
+- rewrite root AGENTS.md - compact, high-signal, removes stale refs
+- 添加数据库迁移工作流规范
+
+### 新功能
+- admin/teacher 页面全局搜索 + 统一 admin-page 样式
+- 心首页关键信息卡片 — 家庭+个人双栏概览
+- health report Phase 2 - match confidence, candidate selection, auto-create member
+- supplier distribution system - entities, services, controllers, and admin UI
+- fuse Western astrology with Chinese zodiac for annual energy calculation
+
+
 ## v1.0.6 (2026-07-06)
 
 ### Bug 修复

+ 2 - 2
cfc-web/package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "cfc-web",
-  "version": "1.0.2",
+  "version": "1.0.7",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "cfc-web",
-      "version": "1.0.2",
+      "version": "1.0.7",
       "dependencies": {
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor-for-vue": "^1.0.2",

+ 1 - 1
cfc-web/package.json

@@ -1,6 +1,6 @@
 {
   "name": "cfc-web",
-  "version": "1.0.6",
+  "version": "1.0.8",
   "private": true,
   "scripts": {
     "dev": "vue-cli-service serve",

+ 32 - 3
cfc-web/src/views/admin/ActivityEdit.vue

@@ -43,7 +43,9 @@
         </el-form-item>
 
         <el-form-item label="活动描述" prop="description">
-          <el-input v-model="form.description" type="textarea" :rows="3" placeholder="请输入活动描述" :disabled="readonly" />
+          <div class="rich-editor" :class="{ 'is-disabled': readonly }">
+            <Editor v-model="form.description" :config="editorConfig" @onCreated="handleEditorCreated" :disabled="readonly" style="min-height: 300px;" />
+          </div>
         </el-form-item>
 
         <el-form-item label="封面图片">
@@ -153,10 +155,11 @@
 import DimensionWeightPicker from '@/components/DimensionWeightPicker.vue'
 import { getActivityDetail, createActivity, updateActivity, publishActivity, submitActivityReview, reDraftActivity } from '@/api/activity'
 import { saveDimensionWeights, getDimensionWeights } from '@/api/dimension-weight.js'
+import { Editor } from '@wangeditor/editor-for-vue'
 
 export default {
   name: 'ActivityEdit',
-  components: { DimensionWeightPicker },
+  components: { DimensionWeightPicker, Editor },
   data() {
     return {
       isEdit: false,
@@ -203,7 +206,9 @@ export default {
         endTime: [
           { required: true, message: '请选择结束时间', trigger: 'change' }
         ]
-      }
+      },
+      editorConfig: null,
+      editorInstance: null
     }
   },
   computed: {
@@ -229,6 +234,7 @@ export default {
     }
   },
   created() {
+    this.initEditorConfig()
     const id = this.$route.query.id
     this.readonly = this.$route.query.readonly === '1'
     if (id) {
@@ -237,6 +243,29 @@ export default {
     }
   },
   methods: {
+    initEditorConfig() {
+      this.editorConfig = {
+        MENU_CONF: {
+          uploadImage: {
+            server: this.uploadActionUrl,
+            headers: this.uploadHeaders,
+            maxNumberOfFiles: 1,
+            fieldName: 'file',
+            customAlert: (msg, type) => {
+              this.$message && this.$message({ message: msg, type: type || 'error' })
+            },
+            onSuccess(res, insertFn) {
+              if (res.code === 200) {
+                insertFn(res.data.url || res.data)
+              }
+            }
+          }
+        }
+      }
+    },
+    handleEditorCreated(editor) {
+      this.editorInstance = editor
+    },
     async loadDetail(id) {
       this.loading = true
       try {

+ 33 - 3
cfc-web/src/views/admin/ArticleEdit.vue

@@ -86,8 +86,9 @@
         </el-form-item>
 
         <el-form-item label="内容">
-          <el-input v-model="form.content" type="textarea" :rows="15" placeholder="请输入文章内容(支持 HTML)&#10;&#10;提示:此为纯文本编辑器,后续将升级为富文本编辑器" :disabled="readonly" />
-          <div style="color: #999; font-size: 12px; margin-top: 4px;">当前为纯文本输入,后续将升级为富文本编辑器</div>
+          <div class="rich-editor" :class="{ 'is-disabled': readonly }">
+            <Editor v-model="form.content" :config="editorConfig" @onCreated="handleEditorCreated" :disabled="readonly" style="min-height: 400px;" />
+          </div>
         </el-form-item>
 
         <el-form-item label="标签">
@@ -136,10 +137,11 @@ import {
 } from '@/api/article.js'
 import { saveDimensionWeights, getDimensionWeights } from '@/api/dimension-weight.js'
 import { getTagList } from '@/api/dimension.js'
+import { Editor } from '@wangeditor/editor-for-vue'
 
 export default {
   name: 'ArticleEdit',
-  components: { DimensionWeightPicker },
+  components: { DimensionWeightPicker, Editor },
   data() {
     return {
       isEdit: false,
@@ -154,6 +156,8 @@ export default {
       rejectReason: '',
       categories: [],
       tagOptions: [],
+      editorConfig: null,
+      editorInstance: null,
       form: {
         id: null,
         categoryId: '',
@@ -197,6 +201,7 @@ export default {
     }
   },
   created() {
+    this.initEditorConfig()
     this.loadCategories()
     this.loadTagOptions()
     const id = this.$route.query.id
@@ -238,6 +243,31 @@ export default {
     next()
   },
   methods: {
+    initEditorConfig() {
+      const baseURL = process.env.VUE_APP_BASE_API || ''
+      const token = localStorage.getItem('token')
+      this.editorConfig = {
+        MENU_CONF: {
+          uploadImage: {
+            server: baseURL + '/api/admin/articles/upload/image',
+            headers: token ? { Authorization: 'Bearer ' + token } : {},
+            maxNumberOfFiles: 1,
+            fieldName: 'file',
+            customAlert: (msg, type) => {
+              this.$message && this.$message({ message: msg, type: type || 'error' })
+            },
+            onSuccess(res, insertFn) {
+              if (res.code === 200) {
+                insertFn(res.data.url || res.data)
+              }
+            }
+          }
+        }
+      }
+    },
+    handleEditorCreated(editor) {
+      this.editorInstance = editor
+    },
     async loadCategories() {
       try {
         const res = await adminArticleCategoriesList()

+ 7 - 10
cfc-web/src/views/admin/ProductEdit.vue

@@ -25,7 +25,7 @@
 
         <el-form-item label="商品描述" prop="description">
           <div class="rich-editor">
-            <Editor v-model="form.description" :config="editorConfig" style="min-height: 300px;" />
+            <Editor v-model="form.description" :config="editorConfig" @onCreated="handleEditorCreated" style="min-height: 300px;" />
           </div>
         </el-form-item>
 
@@ -176,7 +176,8 @@ export default {
             }
           }
         }
-      }
+      },
+      editorInstance: null
     }
   },
   computed: {
@@ -201,6 +202,9 @@ export default {
     }
   },
   methods: {
+    handleEditorCreated(editor) {
+      this.editorInstance = editor
+    },
     async loadProduct(id) {
       this.pageLoading = true
       try {
@@ -233,12 +237,7 @@ export default {
           }
           // Load dimension weights
           this.loadDimensionWeights(p.id)
-          // Set editor content after DOM ready
-          this.$nextTick(() => {
-            if (this.editor) {
-              this.editor.txt.html(p.description || '')
-            }
-          })
+          // v-model 已绑定 form.description,editor 内容自动更新
         }
       } catch (e) {
         this.$message.error('加载商品失败')
@@ -300,8 +299,6 @@ export default {
         if (!valid) return
         this.saving = true
         try {
-          // Get HTML content from editor
-          this.form.description = this.editor ? this.editor.txt.html() : ''
           const payload = {
             ...this.form,
             price: (this.form.price != null) ? Math.round(this.form.price * 100) : 0,