diff --git a/mock-server/data/orgs.js b/mock-server/data/orgs.js
new file mode 100644
index 0000000..4a7d2e2
--- /dev/null
+++ b/mock-server/data/orgs.js
@@ -0,0 +1,38 @@
+// 机构数据以扁平列表存储,前端/mock均通过 parentId 自行组装树
+// org001 为一级机构(内置根机构),不可删除;admin 用户归属于此
+export const orgs = [
+ { id: 'org001', name: '总行', parentId: null, level: 1, remark: '内置根机构,不可删除', builtin: true, createdAt: '2026-01-01 00:00:00' },
+ { id: 'org002', name: '华东分行', parentId: 'org001', level: 2, remark: '', builtin: false, createdAt: '2026-01-02 00:00:00' },
+ { id: 'org003', name: '华南分行', parentId: 'org001', level: 2, remark: '', builtin: false, createdAt: '2026-01-02 00:00:00' },
+ { id: 'org004', name: '上海分部', parentId: 'org002', level: 3, remark: '', builtin: false, createdAt: '2026-01-03 00:00:00' }
+]
+
+let seq = orgs.length
+
+export function nextOrgId() {
+ seq += 1
+ return `org${String(seq).padStart(3, '0')}`
+}
+
+export function buildOrgTree(list) {
+ const map = new Map(list.map((o) => [o.id, { ...o, children: [] }]))
+ const roots = []
+ map.forEach((node) => {
+ if (node.parentId && map.has(node.parentId)) {
+ map.get(node.parentId).children.push(node)
+ } else {
+ roots.push(node)
+ }
+ })
+ return roots
+}
+
+export function collectDescendantIds(list, id) {
+ const result = []
+ const children = list.filter((o) => o.parentId === id)
+ children.forEach((c) => {
+ result.push(c.id)
+ result.push(...collectDescendantIds(list, c.id))
+ })
+ return result
+}
diff --git a/mock-server/index.js b/mock-server/index.js
index efc263e..1e1390d 100644
--- a/mock-server/index.js
+++ b/mock-server/index.js
@@ -2,6 +2,7 @@ import express from 'express'
import cors from 'cors'
import cookieParser from 'cookie-parser'
import authRoutes from './routes/auth.js'
+import systemRoutes from './routes/system.js'
const app = express()
app.use(cors({ origin: 'http://localhost:5173', credentials: true }))
@@ -9,6 +10,7 @@ app.use(express.json({ limit: '10mb' }))
app.use(cookieParser())
app.use('/auth', authRoutes)
+app.use('/system', systemRoutes)
const PORT = 8888
app.listen(PORT, () => {
diff --git a/mock-server/routes/system.js b/mock-server/routes/system.js
new file mode 100644
index 0000000..e3fc281
--- /dev/null
+++ b/mock-server/routes/system.js
@@ -0,0 +1,99 @@
+import express from 'express'
+import { orgs, nextOrgId, buildOrgTree, collectDescendantIds } from '../data/orgs.js'
+import { users } from '../data/users.js'
+import { requireAuth } from '../middleware/requireAuth.js'
+
+const router = express.Router()
+router.use(requireAuth)
+
+// ---------------- 机构管理 ----------------
+
+router.get('/org/list', (req, res) => {
+ const { name, id } = req.query || {}
+ let list = orgs
+ if (name || id) {
+ const matched = orgs.filter(
+ (o) => (name && o.name.includes(name)) || (id && o.id.includes(id))
+ )
+ const idSet = new Set()
+ matched.forEach((m) => {
+ let cur = m
+ while (cur) {
+ idSet.add(cur.id)
+ cur = orgs.find((o) => o.id === cur.parentId)
+ }
+ })
+ list = orgs.filter((o) => idSet.has(o.id))
+ }
+ res.json({ code: 200, msg: 'ok', data: buildOrgTree(list) })
+})
+
+router.post('/org', (req, res) => {
+ const { name, parentId, remark } = req.body || {}
+ if (!name) return res.json({ code: 40004, msg: '机构名称不能为空', data: null })
+ if (!parentId) return res.json({ code: 40004, msg: '请选择上级机构', data: null })
+ const parent = orgs.find((o) => o.id === parentId)
+ if (!parent) return res.json({ code: 40004, msg: '上级机构不存在', data: null })
+ const duplicate = orgs.some((o) => o.parentId === parentId && o.name === name)
+ if (duplicate) return res.json({ code: 40005, msg: '同一上级机构下机构名称不可重复', data: null })
+
+ const org = {
+ id: nextOrgId(),
+ name,
+ parentId,
+ level: parent.level + 1,
+ remark: remark || '',
+ builtin: false,
+ createdAt: new Date().toISOString().slice(0, 19).replace('T', ' ')
+ }
+ orgs.push(org)
+ res.json({ code: 200, msg: 'ok', data: org })
+})
+
+router.put('/org/:id', (req, res) => {
+ const { id } = req.params
+ const { name, parentId, remark } = req.body || {}
+ const org = orgs.find((o) => o.id === id)
+ if (!org) return res.json({ code: 40404, msg: '机构不存在', data: null })
+ if (!name) return res.json({ code: 40004, msg: '机构名称不能为空', data: null })
+
+ let nextParentId = org.parentId
+ let level = org.level
+ if (org.parentId !== null) {
+ if (!parentId) return res.json({ code: 40004, msg: '请选择上级机构', data: null })
+ if (parentId === id) return res.json({ code: 40006, msg: '上级机构不可选择自身', data: null })
+ const descendantIds = collectDescendantIds(orgs, id)
+ if (descendantIds.includes(parentId)) {
+ return res.json({ code: 40006, msg: '上级机构不可选择自身下级', data: null })
+ }
+ const parent = orgs.find((o) => o.id === parentId)
+ if (!parent) return res.json({ code: 40004, msg: '上级机构不存在', data: null })
+ nextParentId = parentId
+ level = parent.level + 1
+ }
+
+ const duplicate = orgs.some((o) => o.id !== id && o.parentId === nextParentId && o.name === name)
+ if (duplicate) return res.json({ code: 40005, msg: '同一上级机构下机构名称不可重复', data: null })
+
+ org.name = name
+ org.parentId = nextParentId
+ org.level = level
+ org.remark = remark || ''
+ res.json({ code: 200, msg: 'ok', data: org })
+})
+
+router.delete('/org/:id', (req, res) => {
+ const { id } = req.params
+ const index = orgs.findIndex((o) => o.id === id)
+ if (index === -1) return res.json({ code: 40404, msg: '机构不存在', data: null })
+ const org = orgs[index]
+ if (org.builtin) return res.json({ code: 40007, msg: '内置一级机构不可删除', data: null })
+ const hasChildren = orgs.some((o) => o.parentId === id)
+ if (hasChildren) return res.json({ code: 40008, msg: '该机构存在下级机构,不可删除', data: null })
+ const hasUsers = users.some((u) => u.orgId === id)
+ if (hasUsers) return res.json({ code: 40009, msg: '该机构已关联用户,不可删除', data: null })
+ orgs.splice(index, 1)
+ res.json({ code: 200, msg: 'ok', data: null })
+})
+
+export default router
diff --git a/src/api/system.js b/src/api/system.js
new file mode 100644
index 0000000..452368d
--- /dev/null
+++ b/src/api/system.js
@@ -0,0 +1,7 @@
+import request from './request'
+
+// ---------------- 机构管理 ----------------
+export const fetchOrgListApi = (params) => request.get('/system/org/list', { params })
+export const createOrgApi = (data) => request.post('/system/org', data)
+export const updateOrgApi = (id, data) => request.put(`/system/org/${id}`, data)
+export const deleteOrgApi = (id) => request.delete(`/system/org/${id}`)
diff --git a/src/components/OrgTreeSelect.vue b/src/components/OrgTreeSelect.vue
new file mode 100644
index 0000000..d88974d
--- /dev/null
+++ b/src/components/OrgTreeSelect.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
diff --git a/src/components/ProTable.vue b/src/components/ProTable.vue
new file mode 100644
index 0000000..4346d4b
--- /dev/null
+++ b/src/components/ProTable.vue
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+ 查询
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/StatusTag.vue b/src/components/StatusTag.vue
new file mode 100644
index 0000000..06b8328
--- /dev/null
+++ b/src/components/StatusTag.vue
@@ -0,0 +1,23 @@
+
+ {{ meta.text }}
+
+
+
diff --git a/src/views/system/org/OrgList.vue b/src/views/system/org/OrgList.vue
new file mode 100644
index 0000000..1a99c3f
--- /dev/null
+++ b/src/views/system/org/OrgList.vue
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+
+
+
+ 查询
+ 重置
+
+
+
+
+
+
+
+
+
+
+ 修改
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+