Przeglądaj źródła

chore: auto bump version and changelog [skip ci]

iwt 3 tygodni temu
rodzic
commit
7731dc7412

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-888d60724e4a1fb2ee71bb9540b06a9a61da7cef
+7859cbb171622968f49cd60d2d09dd2752850675

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

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

+ 248 - 0
cfc-web/src/views/admin/org/Detail.vue

@@ -0,0 +1,248 @@
+<template>
+  <div class="org-detail">
+    <el-card>
+      <PageHeader slot="header" :title="'组织详情 — ' + (org.name || '')">
+        <el-button size="mini" @click="$router.push('/org')">返回列表</el-button>
+      </PageHeader>
+
+      <el-descriptions :column="3" border v-if="org.id">
+        <el-descriptions-item label="ID">{{ org.id }}</el-descriptions-item>
+        <el-descriptions-item label="组织编号">{{ org.code }}</el-descriptions-item>
+        <el-descriptions-item label="类型">{{ typeLabel(org.type) }}</el-descriptions-item>
+        <el-descriptions-item label="联系人">{{ org.contactName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="联系电话">{{ org.contactPhone || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="状态">
+          <StatusTag :type="org.status === 'active' ? 'success' : 'danger'"
+                     :text="org.status === 'active' ? '启用' : '停用'" />
+        </el-descriptions-item>
+        <el-descriptions-item label="简介" :span="3">{{ org.description || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="创建时间">{{ formatTime(org.createdAt) }}</el-descriptions-item>
+        <el-descriptions-item label="查看大屏">
+          <el-button size="mini" type="text" @click="$router.push('/org/' + org.id + '/dashboard')">统计大屏</el-button>
+        </el-descriptions-item>
+      </el-descriptions>
+    </el-card>
+
+    <el-card style="margin-top: 15px">
+      <PageHeader slot="header" title="组织成员">
+        <el-button type="primary" size="small" @click="showAddDialog">+ 添加成员</el-button>
+      </PageHeader>
+
+      <div class="adm-table-wrap">
+        <el-table :data="members" stripe v-loading="memberLoading" height="100%">
+          <el-table-column prop="userId" label="用户ID" width="100"></el-table-column>
+          <el-table-column prop="nickname" label="昵称" min-width="140"></el-table-column>
+          <el-table-column prop="phone" label="手机号" width="140"></el-table-column>
+          <el-table-column label="类型" width="100">
+            <template slot-scope="scope">
+              <StatusTag :type="scope.row.memberType === 'planner' ? 'primary' : 'info'"
+                         :text="scope.row.memberType === 'planner' ? '规划师' : '普通用户'" />
+            </template>
+          </el-table-column>
+          <el-table-column prop="familyCount" label="家庭数" width="90"></el-table-column>
+          <el-table-column prop="totalOrders" label="订单数" width="90"></el-table-column>
+          <el-table-column prop="totalConsumption" label="消费总额(分)" width="130"></el-table-column>
+          <el-table-column prop="joinedAt" label="加入时间" width="170">
+            <template slot-scope="scope">
+              {{ formatTime(scope.row.joinedAt) }}
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" width="170" fixed="right">
+            <template slot-scope="scope">
+              <el-button size="mini" type="primary" @click="openChangeType(scope.row)">改类型</el-button>
+              <el-button size="mini" type="danger" @click="removeMember(scope.row)">移除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </el-card>
+
+    <!-- 添加成员对话框 -->
+    <el-dialog title="添加成员" :visible.sync="addDialogVisible" width="460px" :close-on-click-modal="false">
+      <el-form label-width="100px">
+        <el-form-item label="搜索用户">
+          <el-select v-model="addForm.userId" filterable remote placeholder="按手机号/昵称搜索平台用户"
+                     :remote-method="searchUsers" :loading="searchLoading" style="width: 100%">
+            <el-option v-for="item in searchResults" :key="item.id"
+                       :label="item.nickname + ' (' + item.phone + ')'" :value="item.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="成员类型">
+          <el-radio-group v-model="addForm.memberType">
+            <el-radio label="planner">规划师</el-radio>
+            <el-radio label="user">普通用户</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <div slot="footer">
+        <el-button @click="addDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="submitAddMember" :loading="adding">确定</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 变更类型对话框 -->
+    <el-dialog title="变更成员类型" :visible.sync="changeTypeVisible" width="400px" :close-on-click-modal="false">
+      <el-form label-width="100px">
+        <el-form-item label="成员类型">
+          <el-radio-group v-model="changeTypeForm.memberType">
+            <el-radio label="planner">规划师</el-radio>
+            <el-radio label="user">普通用户</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <div slot="footer">
+        <el-button @click="changeTypeVisible = false">取消</el-button>
+        <el-button type="primary" @click="submitChangeType" :loading="changing">确定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { orgDetail, orgMemberList, orgMemberAdd, orgMemberRemove, orgMemberChangeType, orgUserSearch } from '@/api/org'
+
+export default {
+  name: 'OrgDetail',
+  data() {
+    return {
+      org: {},
+      members: [],
+      memberLoading: false,
+      addDialogVisible: false,
+      searchLoading: false,
+      searchResults: [],
+      addForm: { userId: null, memberType: 'user' },
+      adding: false,
+      changeTypeVisible: false,
+      changeTypeForm: { userId: null, memberType: 'user' },
+      changing: false
+    }
+  },
+  computed: {
+    orgId() {
+      return this.$route.params.id
+    }
+  },
+  created() {
+    this.loadDetail()
+    this.loadMembers()
+  },
+  methods: {
+    formatTime(t) {
+      return t ? t.replace('T', ' ').substring(0, 19) : '-'
+    },
+    typeLabel(t) {
+      const map = { enterprise: '企业', institution: '机构', other: '其他' }
+      return t ? (map[t] || t) : '-'
+    },
+    async loadDetail() {
+      try {
+        const res = await orgDetail({ id: this.orgId })
+        if (res.data) {
+          this.org = res.data
+        }
+      } catch (e) {
+        this.$message.error('加载失败')
+      }
+    },
+    async loadMembers() {
+      this.memberLoading = true
+      try {
+        const res = await orgMemberList({ orgId: this.orgId })
+        if (res.data) {
+          this.members = res.data
+        }
+      } catch (e) {
+        this.$message.error('加载失败')
+      } finally {
+        this.memberLoading = false
+      }
+    },
+    showAddDialog() {
+      this.addDialogVisible = true
+      this.addForm = { userId: null, memberType: 'user' }
+      this.searchResults = []
+    },
+    async searchUsers(keyword) {
+      if (!keyword) {
+        this.searchResults = []
+        return
+      }
+      this.searchLoading = true
+      try {
+        const res = await orgUserSearch({ keyword })
+        if (res.data) {
+          this.searchResults = res.data
+        }
+      } catch (e) {
+        this.searchResults = []
+      } finally {
+        this.searchLoading = false
+      }
+    },
+    async submitAddMember() {
+      if (!this.addForm.userId) {
+        this.$message.warning('请选择用户')
+        return
+      }
+      this.adding = true
+      try {
+        await orgMemberAdd({
+          orgId: this.orgId,
+          userId: this.addForm.userId,
+          memberType: this.addForm.memberType
+        })
+        this.$message.success('添加成功')
+        this.addDialogVisible = false
+        this.loadMembers()
+      } catch (e) {
+        this.$message.error('操作失败')
+      } finally {
+        this.adding = false
+      }
+    },
+    removeMember(row) {
+      this.$confirm('确认移除该成员?(软删,不再计入统计)', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(async () => {
+        try {
+          await orgMemberRemove({ orgId: this.orgId, userId: row.userId })
+          this.$message.success('已移除')
+          this.loadMembers()
+        } catch (e) {
+          this.$message.error('移除失败')
+        }
+      }).catch(() => {})
+    },
+    openChangeType(row) {
+      this.changeTypeForm = { userId: row.userId, memberType: row.memberType || 'user' }
+      this.changeTypeVisible = true
+    },
+    async submitChangeType() {
+      this.changing = true
+      try {
+        await orgMemberChangeType({
+          orgId: this.orgId,
+          userId: this.changeTypeForm.userId,
+          memberType: this.changeTypeForm.memberType
+        })
+        this.$message.success('已变更')
+        this.changeTypeVisible = false
+        this.loadMembers()
+      } catch (e) {
+        this.$message.error('变更失败')
+      } finally {
+        this.changing = false
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.org-detail {
+  padding: 20px;
+}
+</style>