You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1344 lines
73 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 账号管理
> 模块编码account
> 端侧Web专属仅超级管理员
> 关联文档01-模块划分 §1.1~1.4 / 02-功能清单-超级管理员 §1 / 03-业务流转逻辑-超级管理员 §1~3 / 05-接口规范 §9.2 / 06-项目技术要求 §4.1~4.3
> 强制规范遵循 `07-前端界面开发规范.md`
## 功能概览
| 项目 | 说明 |
|------|------|
| 菜单名称 | 账号管理 |
| 子菜单 | 医院信息管理、物业公司信息管理、医院账号管理、物业公司管理员账号管理、到期账号管理、到期提醒规则配置 |
| 功能编号 | SA-A-01 ~ SA-A-08 |
| 权限编码前缀 | permission:user:* / permission:config:* |
---
## 页面1医院信息管理列表页
**页面编号**SA-A-01-P01
**端侧归属**Web专属
**页面路径**/account/hospitals
### 界面布局
```
┌──────────────────────────────────────────────────────────────────┐
│ [面包屑] 账号管理 > 医院信息管理 │
├──────────────────────────────────────────────────────────────────┤
│ [查询条件区] │
│ 医院名称[____] 状态[▼] 联系人[____] [查询] [重置] │
├──────────────────────────────────────────────────────────────────┤
│ [操作栏] [新增医院] │
├──────────────────────────────────────────────────────────────────┤
│ [列表区] │
│ 序号 | 医院名称 | 院区数 | 联系人 | 联系电话 | 状态 | 操作 │
│ 1 | XX医院 | 3 | 张三 | 138****1234| 启用 | 编辑 停用 │
│ 2 | YY医院 | 1 | 李四 | 139****5678| 停用 | 编辑 启用 │
├──────────────────────────────────────────────────────────────────┤
│ [分页] 共50条 每页[20▼] < 1 2 3 > │
└──────────────────────────────────────────────────────────────────┘
```
### 查询条件
| 字段名 | 控件类型 | 必填 | 默认值 | 说明 |
|--------|----------|------|--------|------|
| 医院名称 | 文本输入 | 否 | — | 模糊匹配 |
| 状态 | 下拉单选 | 否 | 全部 | 启用/停用 |
| 联系人 | 文本输入 | 否 | — | 模糊匹配 |
### 列表字段
| 序号 | 字段名 | 列宽 | 支持排序 | 说明 |
|------|--------|------|----------|------|
| 1 | 序号 | 60px | — | 自增序号 |
| 2 | 医院名称 | 180px | 是 | — |
| 3 | 院区数 | 80px | 否 | 显示关联院区数量,点击展开院区列表 |
| 4 | 联系人 | 100px | 否 | — |
| 5 | 联系电话 | 130px | 否 | 脱敏显示 |
| 6 | 创建时间 | 150px | 是 | 默认倒序 |
| 7 | 状态 | 80px | 是 | 启用(绿色)/停用(红色)标签 |
| 8 | 操作 | 140px | — | 编辑/启停 |
### 操作按钮
| 按钮 | 权限编码 | 位置 | 显示条件 | 说明 |
|------|----------|------|----------|------|
| 新增医院 | permission:user:create | 操作栏 | 始终 | 跳转新增页 |
| 编辑 | permission:user:update | 行操作 | 始终 | 跳转编辑页 |
| 启用/停用 | permission:user:update | 行操作 | 始终 | 二次确认弹窗 |
### 角色差异化视图
| 角色 | 可见按钮 | 数据范围 | 备注 |
|------|----------|----------|------|
| 超级管理员 | 全部按钮 | 全部医院 | — |
### 交互流程要求
1. **页面加载流程**
- 进入页面 → 调用 `GET /api/v1/hospitals` 加载列表数据默认第1页每页20条创建时间倒序
- 并行加载下拉选项:状态下拉(启用/停用)
- 列表为空时显示空状态插图 + "暂无医院信息" 提示文字
- 加载中显示 `el-table` 骨架屏skeleton效果
2. **查询交互流程**
- 用户填写查询条件 → 点击"查询"按钮 → 前端校验无特殊限制 → 调用API携带筛选参数 → 重新渲染列表重置到第1页
- 点击"重置"→ 清空所有查询条件 → 重新加载默认列表
- 支持回车键触发查询
3. **行内操作流程**
- **编辑**:点击"编辑"→ 路由跳转至 `/account/hospitals/:id/edit`
- **启用/停用**:点击"启用"或"停用"→ 弹出 `el-message-box` 二次确认("确认停用XX医院停用后该医院下所有账号将无法登录")→ 确认后调用 `PUT /api/v1/hospitals/{id}/toggle-status` → 成功后 `el-message` 提示"操作成功" → 刷新当前列表
4. **异常处理**
- API请求失败 → `el-message.error` 提示错误信息,列表保持原数据
- 网络超时 → 提示"网络异常,请稍后重试"
- 启停操作失败 → 提示具体错误原因(如"该医院下存在进行中的工单,无法停用"
5. **权限控制交互**
-`permission:user:create` 权限 → "新增医院"按钮不渲染
-`permission:user:update` 权限 → 行操作列不渲染"编辑"和"启用/停用"按钮
6. **[H1]防重复请求**
- 查询/筛选:点击"查询"按钮后立即 disabled=true + 显示loading态API返回后恢复查询期间再次点击无效
- 行内操作(编辑/启停):点击操作按钮后该行所有操作按钮禁用 + 按钮显示loading旋转图标操作完成后恢复
- 分页切换切换页码时取消上一页未完成请求abortController后再发起新请求
- 页面初始化并行请求(列表+下拉数据)之间互不阻塞
7. **[H2]超时与加载反馈**
- 列表查询APItimeout=15秒加载中表格区显示v-loading骨架屏遮罩
- 启停等写操作APItimeout=30秒操作按钮:loading态
- 超时处理:自动中断 → ElMessage.error("请求超时,请检查网络后重试") → 按钮恢复可用
- 列表加载超过2秒时显示全局ElLoading进度提示
8. **[H3]操作确认机制**
- 启用/停用操作ElMessageBox.confirm("确定要{启用/停用}「{对象名称}」吗?停用后该账号将无法登录", "操作确认", { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
- 确认弹窗期间底层页面不可点击modal遮罩
9. **[H8]操作结果反馈**
- 成功操作ElMessage.success("{操作}成功"duration=2000ms) → silent方式刷新列表数据不带loading闪烁
- 失败ElMessage.error(后端message或"操作失败,请稍后重试"duration=0手动关闭)
- 网络异常:提示"网络连接异常,请检查网络后重试"+ 提供"重试"文字按钮
### 组件规范
| 元素 | 组件 | 配置参数 |
|------|------|----------|
| 查询条件区 | el-form | inline, label-width="auto" |
| 医院名称/联系人 | el-input | clearable, maxlength=50, placeholder="请输入" |
| 状态下拉 | el-select | clearable, placeholder="请选择" |
| 查询按钮 | el-button | type="primary", icon="Search" |
| 重置按钮 | el-button | icon="Refresh" |
| 列表 | el-table | stripe, border, size="default", v-loading |
| 状态标签 | el-tag | 启用:type="success", 停用:type="danger" |
| 院区数(可点击) | el-link | type="primary", underline=false |
| 操作按钮 | el-button | type="primary", link, size="small" |
| 分页 | el-pagination | layout="total, sizes, prev, pager, next", :page-sizes="[10,20,50]" |
| 新增医院 | el-button | type="primary", icon="Plus" |
### 校验规则
| 字段 | 规则 | 错误提示 |
|------|------|----------|
| 医院名称(查询) | 最大50字符 | — |
| 联系人(查询) | 最大20字符 | — |
### 响应式布局
| 断点 | 布局调整 |
|------|----------|
| ≥1280px桌面端 | 查询条件区一行排列;列表完整展示所有列 |
| 1024-1279pxPad横屏 | 查询条件区一行排列;"联系人"列隐藏,"创建时间"列隐藏 |
| 768-1023pxPad竖屏 | 查询条件区两行排列;列表仅显示:序号、医院名称、状态、操作;其余列折叠,点击行展开详情 |
---
## 页面2医院信息新增/编辑页
**页面编号**SA-A-01-P02
**端侧归属**Web专属
**页面路径**/account/hospitals/create 或 /account/hospitals/:id/edit
### 界面布局
```
┌──────────────────────────────────────────────────────────────────┐
│ [面包屑] 账号管理 > 医院信息管理 > 新增医院 │
├──────────────────────────────────────────────────────────────────┤
│ ── 基本信息 ────────────────────────────────────────────────── │
│ 医院名称:[____________] 状态:[●启用 ○停用] │
│ 医院地址:[____________________________] │
│ 联系人: [__________] 联系电话:[____________] │
├──────────────────────────────────────────────────────────────────┤
│ ── 院区信息 ────────────────────────────────────────────────── │
│ [ + 添加院区 ] │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 院区1 名称:[主院区] 地址:[____] 联系人:[____] [删除] │ │
│ │ 院区2 名称:[东院区] 地址:[____] 联系人:[____] [删除] │ │
│ └────────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────────┤
│ [取消] [保存] │
└──────────────────────────────────────────────────────────────────┘
```
### 表单字段(基本信息)
| 字段名 | 控件类型 | 必填 | 默认值 | 数据来源 | 校验规则 |
|--------|----------|------|--------|----------|----------|
| 医院名称 | 文本输入 | 是 | — | 自填 | 最大50字全局唯一 |
| 状态 | 单选按钮 | 是 | 启用 | 固定选项 | — |
| 医院地址 | 文本输入 | 否 | — | 自填 | 最大200字 |
| 联系人 | 文本输入 | 否 | — | 自填 | 最大20字 |
| 联系电话 | 文本输入 | 否 | — | 自填 | 手机号格式 |
### 表单字段(院区信息,支持多条)
| 字段名 | 控件类型 | 必填 | 默认值 | 数据来源 | 校验规则 |
|--------|----------|------|--------|----------|----------|
| 院区名称 | 文本输入 | 是 | — | 自填 | 最大30字同医院内唯一 |
| 院区地址 | 文本输入 | 是 | — | 自填 | 最大200字 |
| 联系人 | 文本输入 | 否 | — | 自填 | 最大20字 |
### 通知触发
| 触发操作 | 通知对象 | 通知方式 | 消息模板 | 文档来源 |
|----------|----------|----------|----------|----------|
| 创建医院 | — | — | — | 仅记录操作日志 |
### API端点
| 页面操作 | API路径 | 方法 | 说明 |
|----------|---------|------|------|
| 新增医院 | /api/v1/hospitals | POST | 含院区列表 |
| 编辑医院 | /api/v1/hospitals/{id} | PUT | 含院区列表 |
| 查询详情 | /api/v1/hospitals/{id} | GET | 编辑时回填 |
### 交互流程要求
1. **页面加载流程**
- 新增模式:页面空白表单,状态默认"启用"
- 编辑模式:根据路由参数 `:id` 调用 `GET /api/v1/hospitals/{id}` 回填表单数据
- 加载中显示表单骨架屏
2. **表单填写与提交流程**
- 用户逐项填写基本信息 → 动态添加/删除院区行 → 点击"保存"→ 前端校验 → 调用API → 成功后 `el-message.success` → 路由跳转回列表页
- 编辑模式下保存 → 调用 `PUT /api/v1/hospitals/{id}` → 成功后返回列表页
3. **院区动态表单交互**
- 点击"+添加院区"→ 在院区列表末尾新增一行空表单
- 点击"删除"→ 二次确认后移除该院区行(至少保留一个院区)
- 院区行内字段实时校验
4. **联动交互**
- 无特殊联动逻辑
5. **异常处理**
- 唯一性校验失败(医院名称重复)→ 对应字段下方显示红色错误提示"该医院名称已存在"
- API失败 → `el-message.error` 提示错误信息,表单保持当前数据不丢失
- 编辑模式下详情加载失败 → 提示"数据加载失败"并提供"返回列表"按钮
6. **权限控制交互**
- 无保存权限时 → "保存"按钮禁用
7. **[H1]防重复请求**
- 点击"保存"按钮后:按钮 :loading=true + 文案变为"保存中..." + 按钮disabledAPI返回成功/失败/超时)后恢复
- 保存期间不允许再次点击保存或关闭页面
- 院区行动态增删操作删除院区确认弹窗pending期间禁用其他删除按钮
8. **[H2]超时与加载反馈**
- 新增/编辑提交APIPOST/PUTtimeout=30秒
- 编辑模式详情回填APIGETtimeout=15秒
- 提交中保存按钮保持:loading态 + 表单区域不可编辑(半透明遮罩可选)
- 超时处理:中断请求 → 提示"保存超时,请检查网络后重试" → 按钮恢复
9. **[H3]操作确认机制**
- 删除院区行前 ElMessageBox.confirm("确定要删除该院区吗?删除后数据无法恢复", "删除确认", { type: 'warning' })
10. **[H4]脏数据检测**
- 编辑模式进入时deep clone表单初始数据作为快照
- 用户修改任意字段后标记isDirty=true
- 点击"取消"按钮或浏览器后退时:
* isDirty=true → ElMessageBox.confirm("当前修改尚未保存,离开后将丢失未保存的内容,是否确认离开?") → 确认则离开/取消则停留
* isDirty=false → 直接执行离开操作
- 使用vue-router的beforeRouteLeave导航守卫拦截路由切换
- 保存成功后将当前数据设为新快照重置isDirty=false
11. **[H8]操作结果反馈**
- 保存成功ElMessage.success("保存成功", duration=2000) → 延迟300ms后router.back()返回列表页
- 保存失败ElMessage.error(错误信息, duration=0);表单数据保持不丢失
- 唯一性校验失败对应字段下方红色文字提示不弹message
### 组件规范
| 元素 | 组件 | 配置参数 |
|------|------|----------|
| 基本信息表单 | el-form | label-width="100px", :model, :rules |
| 医院名称 | el-input | maxlength=50, show-word-limit, clearable |
| 状态 | el-radio-group | el-radio label="启用"/"停用" |
| 医院地址 | el-input | maxlength=200, show-word-limit |
| 联系人 | el-input | maxlength=20 |
| 联系电话 | el-input | maxlength=11, type="tel" |
| 院区列表 | 动态表单行 | v-for 渲染, 动态增删 |
| 院区名称 | el-input | maxlength=30, show-word-limit |
| 院区地址 | el-input | maxlength=200 |
| 保存按钮 | el-button | type="primary", :loading |
| 取消按钮 | el-button | @click="router.back()" |
| 添加院区 | el-button | type="primary", plain, icon="Plus" |
| 删除院区 | el-button | type="danger", link, icon="Delete" |
### 校验规则
| 字段 | 规则 | 错误提示 |
|------|------|----------|
| 医院名称 | 必填, 2-50字符, 全局唯一 | "请输入医院名称" / "医院名称长度2-50字符" / "该医院名称已存在" |
| 状态 | 必填 | "请选择状态" |
| 医院地址 | 最大200字符 | "医院地址不能超过200字符" |
| 联系人 | 最大20字符 | "联系人不能超过20字符" |
| 联系电话 | 手机号格式(/^1[3-9]\d{9}$/) | "请输入正确的手机号" |
| 院区名称 | 必填, 2-30字符, 同医院内唯一 | "请输入院区名称" / "院区名称长度2-30字符" / "该院区名称已存在" |
| 院区地址 | 必填, 最大200字符 | "请输入院区地址" / "院区地址不能超过200字符" |
| 院区列表 | 至少1个院区 | "请至少添加一个院区" |
### 响应式布局
| 断点 | 布局调整 |
|------|----------|
| ≥1280px桌面端 | 基本信息两列排列(医院名称+状态一行,地址独占一行,联系人+电话一行院区信息卡片宽度100% |
| 1024-1279pxPad横屏 | 基本信息两列排列院区信息卡片宽度100% |
| 768-1023pxPad竖屏 | 基本信息单列排列每行一个字段院区信息卡片宽度100%,院内行单列 |
---
## 页面3物业公司信息管理列表页
**页面编号**SA-A-02-P01
**端侧归属**Web专属
**页面路径**/account/property-companies
### 界面布局
```
┌──────────────────────────────────────────────────────────────────┐
│ [面包屑] 账号管理 > 物业公司信息管理 │
├──────────────────────────────────────────────────────────────────┤
│ [查询条件区] │
│ 公司名称[____] 状态[▼] 联系人[____] [查询] [重置] │
├──────────────────────────────────────────────────────────────────┤
│ [操作栏] [新增物业公司] │
├──────────────────────────────────────────────────────────────────┤
│ [列表区] │
│ 序号 | 公司名称 | 服务医院 | 联系人 | 联系电话 | 状态 | 操作 │
│ 1 | XX物业 | A医院 | 王五 | 137****9012| 启用 | 编辑 停用 │
├──────────────────────────────────────────────────────────────────┤
│ [分页] 共30条 每页[20▼] < 1 2 > │
└──────────────────────────────────────────────────────────────────┘
```
### 查询条件
| 字段名 | 控件类型 | 必填 | 默认值 | 说明 |
|--------|----------|------|--------|------|
| 公司名称 | 文本输入 | 否 | — | 模糊匹配 |
| 状态 | 下拉单选 | 否 | 全部 | 启用/停用 |
| 联系人 | 文本输入 | 否 | — | 模糊匹配 |
### 列表字段
| 序号 | 字段名 | 列宽 | 支持排序 | 说明 |
|------|--------|------|----------|------|
| 1 | 序号 | 60px | — | 自增序号 |
| 2 | 公司名称 | 180px | 是 | — |
| 3 | 服务医院 | 150px | 否 | 显示关联医院名称列表 |
| 4 | 联系人 | 100px | 否 | — |
| 5 | 联系电话 | 130px | 否 | 脱敏显示 |
| 6 | 创建时间 | 150px | 是 | 默认倒序 |
| 7 | 状态 | 80px | 是 | 启用/停用标签 |
| 8 | 操作 | 140px | — | 编辑/启停 |
### 操作按钮
| 按钮 | 权限编码 | 位置 | 显示条件 | 说明 |
|------|----------|------|----------|------|
| 新增物业公司 | permission:user:create | 操作栏 | 始终 | 跳转新增页 |
| 编辑 | permission:user:update | 行操作 | 始终 | — |
| 启用/停用 | permission:user:update | 行操作 | 始终 | 二次确认 |
### API端点
| 页面操作 | API路径 | 方法 | 说明 |
|----------|---------|------|------|
| 列表查询 | /api/v1/property-companies | GET | 分页查询 |
| 启用/停用 | /api/v1/property-companies/{id}/toggle-status | PUT | — |
### 交互流程要求
1. **页面加载流程**
- 进入页面 → 调用 `GET /api/v1/property-companies` 加载列表默认第1页每页20条创建时间倒序
- 并行加载下拉选项:状态下拉
- 列表为空时显示空状态插图 + "暂无物业公司信息"
2. **查询交互流程**
- 填写查询条件 → 点击"查询"→ 调用API → 重置到第1页渲染
- 点击"重置"→ 清空条件 → 重新加载
3. **行内操作流程**
- **编辑**:点击"编辑"→ 路由跳转至 `/account/property-companies/:id/edit`
- **启用/停用**:二次确认弹窗("确认停用XX物业停用后该物业下所有账号将无法登录")→ 确认后调用API → 成功提示 → 刷新列表
4. **异常处理**
- 同页面1通用异常处理规则
5. **权限控制交互**
-`permission:user:create` → "新增物业公司"按钮不渲染
-`permission:user:update` → 行操作列不渲染
6. **[H1]防重复请求**
- 查询/筛选:点击"查询"按钮后立即 disabled=true + 显示loading态API返回后恢复查询期间再次点击无效
- 行内操作(编辑/启停):点击操作按钮后该行所有操作按钮禁用 + 按钮显示loading旋转图标操作完成后恢复
- 分页切换切换页码时取消上一页未完成请求abortController后再发起新请求
- 页面初始化并行请求(列表+下拉数据)之间互不阻塞
7. **[H2]超时与加载反馈**
- 列表查询APItimeout=15秒加载中表格区显示v-loading骨架屏遮罩
- 启停等写操作APItimeout=30秒操作按钮:loading态
- 超时处理:自动中断 → ElMessage.error("请求超时,请检查网络后重试") → 按钮恢复可用
- 列表加载超过2秒时显示全局ElLoading进度提示
8. **[H3]操作确认机制**
- 启用/停用操作ElMessageBox.confirm("确定要{启用/停用}「{对象名称}」吗?停用后该账号将无法登录", "操作确认", { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
- 确认弹窗期间底层页面不可点击modal遮罩
9. **[H8]操作结果反馈**
- 成功操作ElMessage.success("{操作}成功"duration=2000ms) → silent方式刷新列表数据不带loading闪烁
- 失败ElMessage.error(后端message或"操作失败,请稍后重试"duration=0手动关闭)
- 网络异常:提示"网络连接异常,请检查网络后重试"+ 提供"重试"文字按钮
### 组件规范
| 元素 | 组件 | 配置参数 |
|------|------|----------|
| 列表 | el-table | stripe, border, v-loading |
| 服务医院(可点击) | el-link | type="primary" |
| 状态标签 | el-tag | 启用:type="success", 停用:type="danger" |
| 操作按钮 | el-button | type="primary", link, size="small" |
| 分页 | el-pagination | layout="total, sizes, prev, pager, next" |
### 校验规则
| 字段 | 规则 | 错误提示 |
|------|------|----------|
| 公司名称(查询) | 最大50字符 | — |
| 联系人(查询) | 最大20字符 | — |
### 响应式布局
| 断点 | 布局调整 |
|------|----------|
| ≥1280px桌面端 | 查询条件一行排列;列表完整展示所有列 |
| 1024-1279pxPad横屏 | 查询条件一行排列;"联系人""联系电话"列隐藏 |
| 768-1023pxPad竖屏 | 查询条件两行排列;列表仅显示:序号、公司名称、状态、操作 |
---
## 页面4物业公司信息新增/编辑页
**页面编号**SA-A-02-P02
**端侧归属**Web专属
**页面路径**/account/property-companies/create 或 /account/property-companies/:id/edit
### 表单字段
| 字段名 | 控件类型 | 必填 | 默认值 | 数据来源 | 校验规则 |
|--------|----------|------|--------|----------|----------|
| 公司名称 | 文本输入 | 是 | — | 自填 | 最大50字全局唯一 |
| 公司地址 | 文本输入 | 否 | — | 自填 | 最大200字 |
| 联系人 | 文本输入 | 是 | — | 自填 | 最大20字 |
| 联系电话 | 文本输入 | 是 | — | 自填 | 手机号格式 |
### API端点
| 页面操作 | API路径 | 方法 | 说明 |
|----------|---------|------|------|
| 新增 | /api/v1/property-companies | POST | — |
| 编辑 | /api/v1/property-companies/{id} | PUT | — |
### 交互流程要求
1. **页面加载流程**
- 新增模式:空白表单
- 编辑模式:根据路由参数 `:id` 调用 `GET /api/v1/property-companies/{id}` 回填数据
2. **表单填写与提交流程**
- 填写表单 → 点击"保存"→ 前端校验 → 调用API → 成功提示 → 返回列表页
- 校验失败 → 定位到第一个错误字段,滚动到可见区域
3. **异常处理**
- 唯一性校验失败 → 字段下方红色提示"该公司名称已存在"
- API失败 → 提示错误信息,表单数据不丢失
4. **[H1]防重复请求**
- 点击"保存"按钮后:按钮 :loading=true + 文案变为"保存中..." + 按钮disabledAPI返回成功/失败/超时)后恢复
- 保存期间不允许再次点击保存或关闭页面
5. **[H2]超时与加载反馈**
- 新增/编辑提交APIPOST/PUTtimeout=30秒
- 编辑模式详情回填APIGETtimeout=15秒
- 提交中保存按钮保持:loading态 + 表单区域不可编辑(半透明遮罩可选)
- 超时处理:中断请求 → 提示"保存超时,请检查网络后重试" → 按钮恢复
6. **[H4]脏数据检测**
- 编辑模式进入时deep clone表单初始数据作为快照
- 用户修改任意字段后标记isDirty=true
- 点击"取消"按钮或浏览器后退时:
* isDirty=true → ElMessageBox.confirm("当前修改尚未保存,离开后将丢失未保存的内容,是否确认离开?") → 确认则离开/取消则停留
* isDirty=false → 直接执行离开操作
- 使用vue-router的beforeRouteLeave导航守卫拦截路由切换
- 保存成功后将当前数据设为新快照重置isDirty=false
7. **[H8]操作结果反馈**
- 保存成功ElMessage.success("保存成功", duration=2000) → 延迟300ms后router.back()返回列表页
- 保存失败ElMessage.error(错误信息, duration=0);表单数据保持不丢失
- 唯一性校验失败对应字段下方红色文字提示不弹message
### 组件规范
| 元素 | 组件 | 配置参数 |
|------|------|----------|
| 表单 | el-form | label-width="100px", :model, :rules |
| 公司名称 | el-input | maxlength=50, show-word-limit |
| 公司地址 | el-input | maxlength=200 |
| 联系人 | el-input | maxlength=20 |
| 联系电话 | el-input | maxlength=11, type="tel" |
| 保存按钮 | el-button | type="primary", :loading |
### 校验规则
| 字段 | 规则 | 错误提示 |
|------|------|----------|
| 公司名称 | 必填, 2-50字符, 全局唯一 | "请输入公司名称" / "公司名称长度2-50字符" / "该公司名称已存在" |
| 公司地址 | 最大200字符 | "公司地址不能超过200字符" |
| 联系人 | 必填, 最大20字符 | "请输入联系人" / "联系人不能超过20字符" |
| 联系电话 | 必填, 手机号格式 | "请输入联系电话" / "请输入正确的手机号" |
### 响应式布局
| 断点 | 布局调整 |
|------|----------|
| ≥1280px桌面端 | 表单两列排列(公司名称+联系人一行,地址独占一行) |
| 1024-1279pxPad横屏 | 表单两列排列 |
| 768-1023pxPad竖屏 | 表单单列排列 |
---
## 页面5医院账号管理列表页
**页面编号**SA-A-03-P01
**端侧归属**Web专属
**页面路径**/account/hospital-accounts
### 界面布局
```
┌──────────────────────────────────────────────────────────────────┐
│ [面包屑] 账号管理 > 医院账号管理 │
├──────────────────────────────────────────────────────────────────┤
│ [查询条件区] │
│ 登录账号[____] 绑定医院[▼] 状态[▼] 有效期[▼] [查询] [重置] │
├──────────────────────────────────────────────────────────────────┤
│ [操作栏] [新增医院账号] │
├──────────────────────────────────────────────────────────────────┤
│ [列表区] │
│ 序号 | 登录账号 | 绑定医院 | 角色 | 有效期至 | 状态 | 操作 │
│ 1 | hospital01| XX医院 | 医院查看 | 2027-04-16| 正常 | 编辑 续期│
│ 2 | hospital02| YY医院 | 医院查看 | 2026-03-01| 即将到期| 续期 │
│ 3 | hospital03| ZZ医院 | 医院查看 | 2026-01-01| 已过期| 续期 │
├──────────────────────────────────────────────────────────────────┤
│ [分页] 共20条 每页[20▼] < 1 > │
└──────────────────────────────────────────────────────────────────┘
```
### 查询条件
| 字段名 | 控件类型 | 必填 | 默认值 | 说明 |
|--------|----------|------|--------|------|
| 登录账号 | 文本输入 | 否 | — | 精确匹配 |
| 绑定医院 | 下拉单选 | 否 | 全部 | 数据来源:医院信息管理 |
| 状态 | 下拉单选 | 否 | 全部 | 正常/即将到期/已过期/已停用 |
| 有效期 | 下拉单选 | 否 | 全部 | 已过期/7天内到期/30天内到期/正常 |
### 列表字段
| 序号 | 字段名 | 列宽 | 支持排序 | 说明 |
|------|--------|------|----------|------|
| 1 | 序号 | 60px | — | 自增 |
| 2 | 登录账号 | 130px | 否 | — |
| 3 | 绑定医院 | 150px | 是 | — |
| 4 | 角色 | 120px | 否 | 显示分配的角色名称 |
| 5 | 有效期至 | 120px | 是 | 即将到期=橙色,已过期=红色 |
| 6 | 状态 | 100px | 是 | 正常(绿)/即将到期(橙)/已过期(红)/已停用(灰) |
| 7 | 操作 | 200px | — | 编辑/续期/启停/重置密码 |
### 操作按钮
| 按钮 | 权限编码 | 位置 | 显示条件 | 说明 |
|------|----------|------|----------|------|
| 新增医院账号 | permission:user:create | 操作栏 | 始终 | — |
| 编辑 | permission:user:update | 行操作 | 始终 | — |
| 续期 | permission:user:update | 行操作 | 始终 | 修改有效期 |
| 启用/禁用 | permission:user:update | 行操作 | 始终 | 二次确认,禁用立即生效 |
| 重置密码 | permission:user:update | 行操作 | 始终 | 重置为默认密码 |
### 通知触发
| 触发操作 | 通知对象 | 通知方式 | 消息模板 | 文档来源 |
|----------|----------|----------|----------|----------|
| 账号禁用 | 被禁用账号 | — | 已登录session立即失效 | 03-超级管理员 §2 |
| 重置密码 | 账号持有人 | — | 下次登录使用新密码 | — |
### API端点
| 页面操作 | API路径 | 方法 | 说明 |
|----------|---------|------|------|
| 列表查询 | /api/v1/accounts/hospital | GET | 分页查询 |
| 启用/禁用 | /api/v1/accounts/{id}/toggle-status | PUT | 禁用立即失效session |
| 续期 | /api/v1/accounts/{id}/renew | PUT | 修改有效期 |
| 重置密码 | /api/v1/accounts/{id}/reset-password | PUT | — |
### 交互流程要求
1. **页面加载流程**
- 进入页面 → 并行调用列表查询API + 绑定医院下拉数据 + 状态下拉 + 有效期下拉
- 加载中骨架屏 → 数据渲染列表
2. **查询交互流程**
- 填写查询条件 → 点击"查询"→ 调用API → 重置到第1页
- "绑定医院"下拉支持搜索过滤filterable
3. **行内操作流程**
- **编辑**:点击"编辑"→ 路由跳转编辑页(如存在独立编辑页)
- **续期**:点击"续期"→ 弹出续期弹窗(`el-dialog`,选择新有效期日期)→ 确认后调用 `PUT /api/v1/accounts/{id}/renew` → 成功提示 → 刷新列表
- **启用/禁用**:二次确认("确认禁用该账号?禁用后该账号将立即无法登录")→ 确认后调用API → 成功提示 → 刷新列表
- **重置密码**:二次确认("确认重置密码?重置后密码将恢复为默认密码")→ 确认后调用API → 成功后弹窗展示新密码,支持一键复制
4. **联动交互**
- 绑定医院下拉数据来源于医院信息管理中的启用状态医院
5. **异常处理**
- 禁用失败(账号已禁用等)→ 提示具体原因
- 续期时新有效期早于当前日期 → 前端校验拦截,提示"有效期不能早于当前日期"
- 重置密码失败 → 提示错误信息
6. **权限控制交互**
- 无对应权限 → 行操作按钮不渲染
7. **[H1]防重复请求**
- 查询/筛选:点击"查询"按钮后立即 disabled=true + 显示loading态API返回后恢复查询期间再次点击无效
- 行内操作(编辑/续期/启停/重置密码):点击操作按钮后该行所有操作按钮禁用 + 按钮显示loading旋转图标操作完成后恢复
- 分页切换切换页码时取消上一页未完成请求abortController后再发起新请求
- 页面初始化并行请求(列表+下拉数据)之间互不阻塞
8. **[H2]超时与加载反馈**
- 列表查询APItimeout=15秒加载中表格区显示v-loading骨架屏遮罩
- 启停/续期/重置密码等写操作APItimeout=30秒操作按钮:loading态
- 超时处理:自动中断 → ElMessage.error("请求超时,请检查网络后重试") → 按钮恢复可用
- 列表加载超过2秒时显示全局ElLoading进度提示
9. **[H3]操作确认机制**
- 启用/禁用操作ElMessageBox.confirm("确定要{启用/禁用}「{对象名称}」吗?禁用后该账号将无法登录", "操作确认", { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
- 重置密码ElMessageBox.confirm("确定要重置「{账号}」的密码吗?重置后将恢复为默认密码", "操作确认", { type: 'warning' })
- 确认弹窗期间底层页面不可点击modal遮罩
10. **[H8]操作结果反馈**
- 成功操作ElMessage.success("{操作}成功"duration=2000ms) → silent方式刷新列表数据不带loading闪烁
- 失败ElMessage.error(后端message或"操作失败,请稍后重试"duration=0手动关闭)
- 网络异常:提示"网络连接异常,请检查网络后重试"+ 提供"重试"文字按钮
### 组件规范
| 元素 | 组件 | 配置参数 |
|------|------|----------|
| 绑定医院下拉 | el-select | filterable, clearable, remote, placeholder="请选择医院" |
| 有效期至 | el-tag | 正常:type="success", 即将到期:type="warning", 已过期:type="danger" |
| 状态标签 | el-tag | 正常:type="success", 即将到期:type="warning", 已过期:type="danger", 已停用:type="info" |
| 续期弹窗 | el-dialog | width="400px", :close-on-click-modal=false |
| 续期日期选择 | el-date-picker | type="date", :disabled-date="禁用过去日期" |
| 重置密码结果弹窗 | el-dialog | 展示新密码 + 复制按钮 |
### 校验规则
| 字段 | 规则 | 错误提示 |
|------|------|----------|
| 续期日期 | 必填, 不早于当前日期 | "请选择有效期" / "有效期不能早于当前日期" |
### 响应式布局
| 断点 | 布局调整 |
|------|----------|
| ≥1280px桌面端 | 查询条件一行排列;列表完整展示所有列 |
| 1024-1279pxPad横屏 | 查询条件一行排列;"角色"列隐藏 |
| 768-1023pxPad竖屏 | 查询条件两行排列;列表仅显示:序号、登录账号、绑定医院、状态、操作 |
---
## 页面6新增医院账号页
**页面编号**SA-A-03-P02
**端侧归属**Web专属
**页面路径**/account/hospital-accounts/create
### 界面布局
```
┌──────────────────────────────────────────────────────────────────┐
│ [面包屑] 账号管理 > 医院账号管理 > 新增医院账号 │
├──────────────────────────────────────────────────────────────────┤
│ 登录账号:[____________] 初始密码:[________](自动生成) │
│ 绑定医院:[▼必选] │
│ 有效期至:[日期选择] (必填) │
│ 分配角色:[▼多选] □ 医院查看模板 □ 自定义... │
├──────────────────────────────────────────────────────────────────┤
│ [取消] [保存] │
└──────────────────────────────────────────────────────────────────┘
```
### 表单字段
| 字段名 | 控件类型 | 必填 | 默认值 | 数据来源 | 校验规则 |
|--------|----------|------|--------|----------|----------|
| 登录账号 | 文本输入 | 是 | — | 自填 | 4~20位字母数字全局唯一 |
| 初始密码 | 文本输入 | 是 | 系统生成 | 自动生成 | 可手动修改6~20位 |
| 绑定医院 | 下拉单选 | 是 | — | 医院信息列表 | — |
| 有效期至 | 日期选择 | 是 | — | 自填 | 不早于当前日期 |
| 分配角色 | 下拉多选 | 是 | — | 角色管理-医院适用角色 | 至少选一个 |
### API端点
| 页面操作 | API路径 | 方法 | 说明 |
|----------|---------|------|------|
| 新增 | /api/v1/accounts/hospital | POST | — |
### 交互流程要求
1. **页面加载流程**
- 进入页面 → 并行加载:绑定医院下拉(仅启用状态医院)+ 角色下拉(仅医院适用范围角色)
- 自动生成初始密码8位随机字母数字显示在密码输入框中
- 有效期默认为当前日期+1年
2. **表单填写与提交流程**
- 填写登录账号 → 实时校验唯一性(失焦时调用后端校验接口)
- 选择绑定医院 → 选择有效期 → 分配角色 → 点击"保存"→ 前端校验全部通过 → 调用API → 成功提示 → 返回列表页
3. **联动交互**
- 绑定医院下拉:数据来源于医院信息管理中启用状态的医院
- 分配角色下拉:数据来源于权限管理中"适用范围=医院账号"的启用角色
4. **异常处理**
- 登录账号重复 → 失焦校验后即时提示"该登录账号已存在"
- 保存失败 → 提示错误信息,表单数据不丢失
5. **权限控制交互**
- 无保存权限时 → "保存"按钮禁用
6. **[H1]防重复请求**
- 点击"保存"按钮后:按钮 :loading=true + 文案变为"保存中..." + 按钮disabledAPI返回成功/失败/超时)后恢复
- 保存期间不允许再次点击保存或关闭页面
7. **[H2]超时与加载反馈**
- 新增提交APIPOSTtimeout=30秒
- 提交中保存按钮保持:loading态 + 表单区域不可编辑(半透明遮罩可选)
- 超时处理:中断请求 → 提示"保存超时,请检查网络后重试" → 按钮恢复
8. **[H4]脏数据检测**
- 页面进入时deep clone表单初始数据作为快照
- 用户修改任意字段后标记isDirty=true
- 点击"取消"按钮或浏览器后退时:
* isDirty=true → ElMessageBox.confirm("当前修改尚未保存,离开后将丢失未保存的内容,是否确认离开?") → 确认则离开/取消则停留
* isDirty=false → 直接执行离开操作
- 使用vue-router的beforeRouteLeave导航守卫拦截路由切换
- 保存成功后将当前数据设为新快照重置isDirty=false
9. **[H8]操作结果反馈**
- 保存成功ElMessage.success("保存成功", duration=2000) → 延迟300ms后router.back()返回列表页
- 保存失败ElMessage.error(错误信息, duration=0);表单数据保持不丢失
- 唯一性校验失败对应字段下方红色文字提示不弹message
### 组件规范
| 元素 | 组件 | 配置参数 |
|------|------|----------|
| 表单 | el-form | label-width="100px", :model, :rules |
| 登录账号 | el-input | maxlength=20, clearable, placeholder="4-20位字母数字" |
| 初始密码 | el-input | maxlength=20, show-password, readonly=false |
| 绑定医院 | el-select | filterable, clearable, placeholder="请选择医院" |
| 有效期至 | el-date-picker | type="date", :disabled-date="禁用过去日期", value-format="YYYY-MM-DD" |
| 分配角色 | el-select | multiple, filterable, collapse-tags, placeholder="请选择角色" |
| 保存按钮 | el-button | type="primary", :loading |
| 取消按钮 | el-button | @click="router.back()" |
| 刷新密码按钮 | el-button | icon="Refresh", circle, @click="generatePassword" |
### 校验规则
| 字段 | 规则 | 错误提示 |
|------|------|----------|
| 登录账号 | 必填, 4-20位字母数字(/^[a-zA-Z0-9]{4,20}$/), 全局唯一 | "请输入登录账号" / "登录账号为4-20位字母数字" / "该登录账号已存在" |
| 初始密码 | 必填, 6-20位 | "请输入初始密码" / "密码长度6-20位" |
| 绑定医院 | 必填 | "请选择绑定医院" |
| 有效期至 | 必填, 不早于当前日期 | "请选择有效期" / "有效期不能早于当前日期" |
| 分配角色 | 必填, 至少选一个 | "请选择至少一个角色" |
### 响应式布局
| 断点 | 布局调整 |
|------|----------|
| ≥1280px桌面端 | 表单两列排列(登录账号+初始密码一行,绑定医院+有效期一行,角色独占一行) |
| 1024-1279pxPad横屏 | 表单两列排列 |
| 768-1023pxPad竖屏 | 表单单列排列,每个字段独占一行 |
---
## 页面7物业公司管理员账号管理列表页
**页面编号**SA-A-04-P01
**端侧归属**Web专属
**页面路径**/account/property-accounts
### 界面布局
```
┌──────────────────────────────────────────────────────────────────┐
│ [面包屑] 账号管理 > 物业管理员账号管理 │
├──────────────────────────────────────────────────────────────────┤
│ [查询条件区] │
│ 登录账号[____] 绑定物业公司[▼] 服务医院[▼] 状态[▼] [查询] [重置] │
├──────────────────────────────────────────────────────────────────┤
│ [操作栏] [新增物业管理员账号] │
├──────────────────────────────────────────────────────────────────┤
│ [列表区] │
│ 序号 | 登录账号 | 绑定物业 | 服务医院 | 角色 | 有效期至 | 状态 | 操作│
│ 1 | prop01 | XX物业 | A医院 | 管理员| 2027-04| 正常 | ...│
├──────────────────────────────────────────────────────────────────┤
│ [分页] 共40条 每页[20▼] < 1 2 > │
└──────────────────────────────────────────────────────────────────┘
```
### 查询条件
| 字段名 | 控件类型 | 必填 | 默认值 | 说明 |
|--------|----------|------|--------|------|
| 登录账号 | 文本输入 | 否 | — | 精确匹配 |
| 绑定物业公司 | 下拉单选 | 否 | 全部 | 数据来源:物业公司信息管理 |
| 服务医院 | 下拉单选 | 否 | 全部 | 数据来源:医院信息管理 |
| 状态 | 下拉单选 | 否 | 全部 | 正常/即将到期/已过期/已停用 |
### 列表字段
| 序号 | 字段名 | 列宽 | 支持排序 | 说明 |
|------|--------|------|----------|------|
| 1 | 序号 | 60px | — | 自增 |
| 2 | 登录账号 | 120px | 否 | — |
| 3 | 绑定物业公司 | 120px | 是 | — |
| 4 | 服务医院 | 120px | 是 | — |
| 5 | 角色 | 120px | 否 | — |
| 6 | 有效期至 | 110px | 是 | 颜色标记同医院账号 |
| 7 | 状态 | 90px | 是 | 同医院账号 |
| 8 | 操作 | 200px | — | 编辑/续期/启停/重置密码 |
### 操作按钮
| 按钮 | 权限编码 | 位置 | 显示条件 | 说明 |
|------|----------|------|----------|------|
| 新增物业管理员账号 | permission:user:create | 操作栏 | 始终 | — |
| 编辑 | permission:user:update | 行操作 | 始终 | — |
| 续期 | permission:user:update | 行操作 | 始终 | — |
| 启用/禁用 | permission:user:update | 行操作 | 始终 | 禁用时下属账号同步失效 |
| 重置密码 | permission:user:update | 行操作 | 始终 | — |
### 通知触发
| 触发操作 | 通知对象 | 通知方式 | 消息模板 | 文档来源 |
|----------|----------|----------|----------|----------|
| 账号禁用 | 物业管理员 | — | session失效+小程序同步下线 | 03-超级管理员 §2 |
| 账号禁用 | 下属人员 | 小程序推送 | 关联下属账号同步失效 | 01 §1.4 |
### API端点
| 页面操作 | API路径 | 方法 | 说明 |
|----------|---------|------|------|
| 列表查询 | /api/v1/accounts/property-admin | GET | 分页查询 |
| 启用/禁用 | /api/v1/accounts/{id}/toggle-status | PUT | 禁用同步下线下属 |
| 续期 | /api/v1/accounts/{id}/renew | PUT | — |
### 交互流程要求
1. **页面加载流程**
- 进入页面 → 并行加载:列表数据 + 绑定物业公司下拉 + 服务医院下拉 + 状态下拉
- 加载中骨架屏
2. **查询交互流程**
- 填写条件 → 点击"查询"→ 调用API → 重置到第1页
- "绑定物业公司"和"服务医院"下拉均支持搜索过滤
3. **行内操作流程**
- **编辑**:跳转编辑页
- **续期**:弹出续期弹窗 → 选择新有效期 → 确认 → API调用 → 刷新列表
- **启用/禁用**:二次确认("确认禁用?禁用后该管理员及所有下属账号将同步失效")→ 确认 → API调用 → 刷新列表
- **重置密码**:二次确认 → API调用 → 弹窗展示新密码
4. **联动交互**
- 物业公司下拉数据来源于物业公司信息管理中的启用状态公司
- 选择物业公司后,服务医院下拉可联动过滤该物业关联的医院
5. **异常处理**
- 禁用操作提示级联影响范围:"该操作将同步禁用X个下属账号"
- 其他同页面5
6. **权限控制交互**
- 同页面5
7. **[H1]防重复请求**
- 查询/筛选:点击"查询"按钮后立即 disabled=true + 显示loading态API返回后恢复查询期间再次点击无效
- 行内操作(编辑/续期/启停/重置密码):点击操作按钮后该行所有操作按钮禁用 + 按钮显示loading旋转图标操作完成后恢复
- 分页切换切换页码时取消上一页未完成请求abortController后再发起新请求
- 页面初始化并行请求(列表+下拉数据)之间互不阻塞
8. **[H2]超时与加载反馈**
- 列表查询APItimeout=15秒加载中表格区显示v-loading骨架屏遮罩
- 启停/续期/重置密码等写操作APItimeout=30秒操作按钮:loading态
- 超时处理:自动中断 → ElMessage.error("请求超时,请检查网络后重试") → 按钮恢复可用
- 列表加载超过2秒时显示全局ElLoading进度提示
9. **[H3]操作确认机制**
- 启用/禁用操作ElMessageBox.confirm("确定要{启用/禁用}「{对象名称}」吗?禁用后将同步禁用所有下属账号", "操作确认", { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
- 重置密码ElMessageBox.confirm("确定要重置「{账号}」的密码吗?重置后将恢复为默认密码", "操作确认", { type: 'warning' })
- 确认弹窗期间底层页面不可点击modal遮罩
10. **[H8]操作结果反馈**
- 成功操作ElMessage.success("{操作}成功"duration=2000ms) → silent方式刷新列表数据不带loading闪烁
- 失败ElMessage.error(后端message或"操作失败,请稍后重试"duration=0手动关闭)
- 网络异常:提示"网络连接异常,请检查网络后重试"+ 提供"重试"文字按钮
### 组件规范
| 元素 | 组件 | 配置参数 |
|------|------|----------|
| 绑定物业公司下拉 | el-select | filterable, clearable |
| 服务医院下拉 | el-select | filterable, clearable |
| 状态标签 | el-tag | 同页面5颜色规则 |
| 操作按钮 | el-button | type="primary", link, size="small" |
| 续期弹窗 | el-dialog | width="400px" |
| 续期日期选择 | el-date-picker | type="date", :disabled-date |
| 重置密码结果弹窗 | el-dialog | 展示新密码 + 复制按钮 |
### 校验规则
| 字段 | 规则 | 错误提示 |
|------|------|----------|
| 续期日期 | 必填, 不早于当前日期 | "请选择有效期" / "有效期不能早于当前日期" |
### 响应式布局
| 断点 | 布局调整 |
|------|----------|
| ≥1280px桌面端 | 查询条件一行排列;列表完整展示所有列 |
| 1024-1279pxPad横屏 | 查询条件两行排列;"角色"列隐藏 |
| 768-1023pxPad竖屏 | 查询条件两行排列;列表仅显示:序号、登录账号、绑定物业、状态、操作 |
---
## 页面8新增物业管理员账号页
**页面编号**SA-A-04-P02
**端侧归属**Web专属
**页面路径**/account/property-accounts/create
### 表单字段
| 字段名 | 控件类型 | 必填 | 默认值 | 数据来源 | 校验规则 |
|--------|----------|------|--------|----------|----------|
| 登录账号 | 文本输入 | 是 | — | 自填 | 4~20位字母数字全局唯一 |
| 初始密码 | 文本输入 | 是 | 系统生成 | 自动生成 | 可修改 |
| 绑定物业公司 | 下拉单选 | 是 | — | 物业公司信息列表 | — |
| 服务医院 | 下拉单选 | 是 | — | 医院信息列表 | — |
| 有效期至 | 日期选择 | 是 | — | 自填 | 不早于当前日期 |
| 分配角色 | 下拉多选 | 是 | — | 角色管理-物业适用角色 | 至少选一个 |
### API端点
| 页面操作 | API路径 | 方法 | 说明 |
|----------|---------|------|------|
| 新增 | /api/v1/accounts/property-admin | POST | — |
### 交互流程要求
1. **页面加载流程**
- 进入页面 → 并行加载:绑定物业公司下拉 + 服务医院下拉 + 角色下拉(物业适用范围)
- 自动生成初始密码,有效期默认当前日期+1年
2. **表单填写与提交流程**
- 填写登录账号 → 失焦时校验唯一性
- 选择物业公司 → 选择服务医院 → 选择有效期 → 分配角色 → 点击"保存"→ 校验 → API → 成功返回列表页
3. **联动交互**
- 绑定物业公司下拉:来源于物业公司信息管理中启用状态的公司
- 选择物业公司后,服务医院下拉联动过滤该物业关联的医院
- 分配角色下拉:仅显示"适用范围=物业管理员"的启用角色
4. **异常处理**
- 登录账号重复 → 失焦时即时提示
- API失败 → 提示错误,表单不丢失
5. **权限控制交互**
- 无保存权限时 → "保存"按钮禁用
6. **[H1]防重复请求**
- 点击"保存"按钮后:按钮 :loading=true + 文案变为"保存中..." + 按钮disabledAPI返回成功/失败/超时)后恢复
- 保存期间不允许再次点击保存或关闭页面
7. **[H2]超时与加载反馈**
- 新增提交APIPOSTtimeout=30秒
- 提交中保存按钮保持:loading态 + 表单区域不可编辑(半透明遮罩可选)
- 超时处理:中断请求 → 提示"保存超时,请检查网络后重试" → 按钮恢复
8. **[H4]脏数据检测**
- 页面进入时deep clone表单初始数据作为快照
- 用户修改任意字段后标记isDirty=true
- 点击"取消"按钮或浏览器后退时:
* isDirty=true → ElMessageBox.confirm("当前修改尚未保存,离开后将丢失未保存的内容,是否确认离开?") → 确认则离开/取消则停留
* isDirty=false → 直接执行离开操作
- 使用vue-router的beforeRouteLeave导航守卫拦截路由切换
- 保存成功后将当前数据设为新快照重置isDirty=false
9. **[H8]操作结果反馈**
- 保存成功ElMessage.success("保存成功", duration=2000) → 延迟300ms后router.back()返回列表页
- 保存失败ElMessage.error(错误信息, duration=0);表单数据保持不丢失
- 唯一性校验失败对应字段下方红色文字提示不弹message
### 组件规范
| 元素 | 组件 | 配置参数 |
|------|------|----------|
| 表单 | el-form | label-width="120px", :model, :rules |
| 登录账号 | el-input | maxlength=20, clearable |
| 初始密码 | el-input | maxlength=20, show-password |
| 绑定物业公司 | el-select | filterable, clearable |
| 服务医院 | el-select | filterable, clearable |
| 有效期至 | el-date-picker | type="date", :disabled-date, value-format="YYYY-MM-DD" |
| 分配角色 | el-select | multiple, filterable, collapse-tags |
| 保存按钮 | el-button | type="primary", :loading |
| 刷新密码按钮 | el-button | icon="Refresh", circle |
### 校验规则
| 字段 | 规则 | 错误提示 |
|------|------|----------|
| 登录账号 | 必填, 4-20位字母数字, 全局唯一 | "请输入登录账号" / "登录账号为4-20位字母数字" / "该登录账号已存在" |
| 初始密码 | 必填, 6-20位 | "请输入初始密码" / "密码长度6-20位" |
| 绑定物业公司 | 必填 | "请选择绑定物业公司" |
| 服务医院 | 必填 | "请选择服务医院" |
| 有效期至 | 必填, 不早于当前日期 | "请选择有效期" / "有效期不能早于当前日期" |
| 分配角色 | 必填, 至少选一个 | "请选择至少一个角色" |
### 响应式布局
| 断点 | 布局调整 |
|------|----------|
| ≥1280px桌面端 | 表单两列排列 |
| 1024-1279pxPad横屏 | 表单两列排列 |
| 768-1023pxPad竖屏 | 表单单列排列 |
---
## 页面9到期账号管理页
**页面编号**SA-A-07-P01
**端侧归属**Web专属
**页面路径**/account/expiring
### 界面布局
```
┌──────────────────────────────────────────────────────────────────┐
│ [面包屑] 账号管理 > 到期账号管理 │
├──────────────────────────────────────────────────────────────────┤
│ [统计卡片区] │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │已过期 │ │7天内到期 │ │30天内到期 │ │
│ │ 3 │ │ 5 │ │ 12 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
├──────────────────────────────────────────────────────────────────┤
│ [查询条件区] │
│ 账号类型[▼] 到期状态[▼] [查询] [重置] │
├──────────────────────────────────────────────────────────────────┤
│ [列表区] │
│ 序号 | 登录账号 | 账号类型 | 绑定单位 | 有效期至 | 剩余天数 | 操作 │
│ 1 | hospital01| 医院 | XX医院 | 2026-04-20| 4天 | 续期 │
│ 2 | prop01 | 物业 | XX物业 | 2026-03-01| -46天 | 续期 │
├──────────────────────────────────────────────────────────────────┤
│ [分页] 共20条 每页[20▼] < 1 > │
└──────────────────────────────────────────────────────────────────┘
```
### 查询条件
| 字段名 | 控件类型 | 必填 | 默认值 | 说明 |
|--------|----------|------|--------|------|
| 账号类型 | 下拉单选 | 否 | 全部 | 医院/物业管理员 |
| 到期状态 | 下拉单选 | 否 | 全部 | 已过期/7天内到期/30天内到期 |
### 列表字段
| 序号 | 字段名 | 列宽 | 支持排序 | 说明 |
|------|--------|------|----------|------|
| 1 | 序号 | 60px | — | 自增 |
| 2 | 登录账号 | 130px | 否 | — |
| 3 | 账号类型 | 100px | 否 | 医院/物业管理员 |
| 4 | 绑定单位 | 150px | 否 | 医院名称或物业公司名称 |
| 5 | 有效期至 | 120px | 是 | — |
| 6 | 剩余天数 | 90px | 是 | 已过期显示负数,红色标记 |
| 7 | 操作 | 100px | — | 续期 |
### 操作按钮
| 按钮 | 权限编码 | 位置 | 显示条件 | 说明 |
|------|----------|------|----------|------|
| 续期 | permission:user:update | 行操作 | 始终 | 弹窗修改有效期 |
### 通知触发
| 触发操作 | 通知对象 | 通知方式 | 消息模板 | 文档来源 |
|----------|----------|----------|----------|----------|
| 续期成功 | 被续期账号 | — | 账号恢复可用,记录操作日志 | 03-超级管理员 §3 |
### API端点
| 页面操作 | API路径 | 方法 | 说明 |
|----------|---------|------|------|
| 列表查询 | /api/v1/accounts/expiring | GET | 筛选到期账号 |
| 续期 | /api/v1/accounts/{id}/renew | PUT | — |
### 交互流程要求
1. **页面加载流程**
- 进入页面 → 并行调用:统计卡片数据 + 列表数据 + 下拉选项
- 统计卡片数据优先渲染,列表加载中显示骨架屏
- 点击统计卡片可快速筛选(点击"已过期"卡片 → 自动填充到期状态=已过期 → 触发查询)
2. **查询交互流程**
- 填写条件 → 点击"查询"→ 调用API → 重置到第1页
- 统计卡片数据随查询条件联动刷新
3. **行内操作流程**
- **续期**:点击"续期"→ 弹出续期弹窗(选择新有效期)→ 确认 → API调用 → 成功提示"续期成功"→ 刷新列表和统计卡片
4. **联动交互**
- 统计卡片点击 → 联动查询条件 → 触发筛选
5. **异常处理**
- 续期失败 → 提示错误信息
- 统计数据加载失败 → 卡片显示"--"占位,不影响列表操作
6. **权限控制交互**
-`permission:user:update` → 行"续期"按钮不渲染
7. **[H1]防重复请求**
- 查询/筛选:点击"查询"按钮后立即 disabled=true + 显示loading态API返回后恢复查询期间再次点击无效
- 行内操作(续期):点击操作按钮后该行所有操作按钮禁用 + 按钮显示loading旋转图标操作完成后恢复
- 分页切换切换页码时取消上一页未完成请求abortController后再发起新请求
- 页面初始化并行请求(统计卡片+列表+下拉数据)之间互不阻塞
8. **[H2]超时与加载反馈**
- 列表查询APItimeout=15秒加载中表格区显示v-loading骨架屏遮罩
- 续期写操作APItimeout=30秒操作按钮:loading态
- 统计卡片数据加载APItimeout=15秒
- 超时处理:自动中断 → ElMessage.error("请求超时,请检查网络后重试") → 按钮恢复可用
- 列表加载超过2秒时显示全局ElLoading进度提示
9. **[H3]操作确认机制**
- 续期弹窗本身即为操作确认机制:选择新有效期后需用户点击"确定"才提交
- 确认弹窗期间底层页面不可点击modal遮罩
10. **[H8]操作结果反馈**
- 成功操作ElMessage.success("{操作}成功"duration=2000ms) → silent方式刷新列表数据和统计卡片不带loading闪烁
- 失败ElMessage.error(后端message或"操作失败,请稍后重试"duration=0手动关闭)
- 网络异常:提示"网络连接异常,请检查网络后重试"+ 提供"重试"文字按钮
### 组件规范
| 元素 | 组件 | 配置参数 |
|------|------|----------|
| 统计卡片 | el-card | shadow="hover", :body-style="{padding:'20px',cursor:'pointer'}" |
| 卡片数字 | div | font-size=28px, font-weight=bold |
| 已过期数字 | — | color=var(--el-color-danger) |
| 7天内到期数字 | — | color=var(--el-color-warning) |
| 30天内到期数字 | — | color=var(--el-color-primary) |
| 剩余天数 | el-tag | 已过期:type="danger", ≤7天:type="warning", 其他:type="info" |
| 续期弹窗 | el-dialog | width="400px", :close-on-click-modal=false |
| 续期日期选择 | el-date-picker | type="date", :disabled-date |
### 校验规则
| 字段 | 规则 | 错误提示 |
|------|------|----------|
| 续期日期 | 必填, 不早于当前日期 | "请选择有效期" / "有效期不能早于当前日期" |
### 响应式布局
| 断点 | 布局调整 |
|------|----------|
| ≥1280px桌面端 | 统计卡片一行三个;列表完整展示所有列 |
| 1024-1279pxPad横屏 | 统计卡片一行三个;"账号类型"列隐藏 |
| 768-1023pxPad竖屏 | 统计卡片一行三个(缩小间距);列表仅显示:序号、登录账号、绑定单位、剩余天数、操作 |
---
## 页面10到期提醒规则配置页
**页面编号**SA-A-08-P01
**端侧归属**Web专属
**页面路径**/account/expiry-settings
### 界面布局
```
┌──────────────────────────────────────────────────────────────────┐
│ [面包屑] 账号管理 > 到期提醒规则配置 │
├──────────────────────────────────────────────────────────────────┤
│ ── 提醒天数配置 ──────────────────────────────────────────── │
│ 提前提醒天数:[7] [15] [30] 天 (可多选,点击添加/移除) │
│ │
│ ── 提醒方式 ────────────────────────────────────────────── │
│ ☑ 用户登录时弹窗提醒 │
│ ☐ 邮件提醒(暂未开放) │
│ │
│ ── 提醒行为 ────────────────────────────────────────────── │
│ 提醒弹窗关闭后:☑ 可正常使用 ☐ 限制部分功能 │
│ 账号过期后: ☑ 禁止登录 ☐ 仅提醒 │
├──────────────────────────────────────────────────────────────────┤
│ [取消] [保存] │
└──────────────────────────────────────────────────────────────────┘
```
### 表单字段
| 字段名 | 控件类型 | 必填 | 默认值 | 数据来源 | 校验规则 |
|--------|----------|------|--------|----------|----------|
| 提前提醒天数 | 标签多选 | 是 | 7, 15, 30 | — | 至少选一个 |
| 用户登录弹窗 | 开关 | 是 | 开 | — | — |
| 弹窗关闭后行为 | 单选 | 是 | 可正常使用 | 固定选项 | — |
| 过期后行为 | 单选 | 是 | 禁止登录 | 固定选项 | — |
### API端点
| 页面操作 | API路径 | 方法 | 说明 |
|----------|---------|------|------|
| 查询配置 | /api/v1/system/configs/expiry-reminder | GET | — |
| 保存配置 | /api/v1/system/configs/expiry-reminder | PUT | — |
### 交互流程要求
1. **页面加载流程**
- 进入页面 → 调用 `GET /api/v1/system/configs/expiry-reminder` 加载当前配置 → 回填表单
- 加载中表单显示骨架屏
2. **表单填写与提交流程**
- 修改配置项 → 点击"保存"→ 前端校验(至少选一个提醒天数)→ 调用 `PUT` API → 成功提示"配置保存成功"
- 无需返回列表页,停留在当前页
3. **标签多选交互**
- "提前提醒天数"使用标签多选预设7/15/30/60/90天选项点击标签切换选中/取消
- 支持自定义天数输入(输入框+添加按钮)
- 至少保留一个选中项
4. **联动交互**
- "用户登录弹窗"关闭时 → "弹窗关闭后行为"选项才可编辑
- "过期后行为"选择"限制部分功能"时 → 显示附加配置(限制哪些功能)
5. **异常处理**
- 保存失败 → 提示错误信息,表单数据不丢失
- 配置加载失败 → 提示"配置加载失败",提供"重试"按钮
6. **权限控制交互**
-`permission:config:update` → 表单所有控件禁用,"保存"按钮不渲染
7. **[H1]防重复请求**
- 点击"保存"按钮后:按钮 :loading=true + 文案变为"保存中..." + 按钮disabledAPI返回成功/失败/超时)后恢复
- 保存期间不允许再次点击保存或关闭页面
8. **[H2]超时与加载反馈**
- 保存配置提交APIPUTtimeout=30秒
- 查询配置回填APIGETtimeout=15秒
- 提交中保存按钮保持:loading态 + 表单区域不可编辑(半透明遮罩可选)
- 超时处理:中断请求 → 提示"保存超时,请检查网络后重试" → 按钮恢复
9. **[H4]脏数据检测**
- 页面进入时deep clone表单初始数据作为快照
- 用户修改任意字段后标记isDirty=true
- 点击"取消"按钮或浏览器后退时:
* isDirty=true → ElMessageBox.confirm("当前修改尚未保存,离开后将丢失未保存的内容,是否确认离开?") → 确认则离开/取消则停留
* isDirty=false → 直接执行离开操作
- 使用vue-router的beforeRouteLeave导航守卫拦截路由切换
- 保存成功后将当前数据设为新快照重置isDirty=false
10. **[H8]操作结果反馈**
- 保存成功ElMessage.success("配置保存成功", duration=2000);停留在当前页不跳转
- 保存失败ElMessage.error(错误信息, duration=0);表单数据保持不丢失
- 唯一性校验失败对应字段下方红色文字提示不弹message本页面无唯一性字段
### 组件规范
| 元素 | 组件 | 配置参数 |
|------|------|----------|
| 表单 | el-form | label-width="140px", :model, :rules |
| 提前提醒天数 | el-check-tag | 多选模式预设7/15/30/60/90 |
| 自定义天数输入 | el-input-number | :min=1, :max=365, :step=1 |
| 用户登录弹窗 | el-switch | active-text="开启", inactive-text="关闭" |
| 弹窗关闭后行为 | el-radio-group | el-radio label="可正常使用"/"限制部分功能" |
| 过期后行为 | el-radio-group | el-radio label="禁止登录"/"仅提醒" |
| 保存按钮 | el-button | type="primary", :loading |
| 取消按钮 | el-button | @click="resetForm" |
### 校验规则
| 字段 | 规则 | 错误提示 |
|------|------|----------|
| 提前提醒天数 | 必填, 至少选一个 | "请至少选择一个提醒天数" |
| 自定义天数 | 1-365整数 | "天数范围1-365" |
### 响应式布局
| 断点 | 布局调整 |
|------|----------|
| ≥1280px桌面端 | 表单标签宽度140px输入区域宽度400px |
| 1024-1279pxPad横屏 | 表单标签宽度120px输入区域宽度350px |
| 768-1023pxPad竖屏 | 表单标签宽度100px输入区域宽度100% |
---
## 需求追溯
| 功能点编号 | 功能名称 | 文档来源 | 后续服务 | 关联功能 |
|------------|----------|----------|----------|----------|
| SA-A-01 | 医院信息管理 | 02-超级管理员 §1 / 03-超级管理员 §1 | 创建医院→创建医院账号 | 医院账号管理(绑定医院) |
| SA-A-02 | 物业公司信息管理 | 02-超级管理员 §1 / 03-超级管理员 §1 | 创建物业公司→创建物业账号 | 物业管理员账号管理(绑定物业) |
| SA-A-03 | 医院账号管理 | 02-超级管理员 §1 / 03-超级管理员 §1~2 | 账号创建→权限分配 | 权限管理(角色绑定) |
| SA-A-04 | 物业管理员账号管理 | 02-超级管理员 §1 / 03-超级管理员 §1~2 | 账号创建→权限分配,禁用→下属同步失效 | 权限管理(角色绑定) / 物业组织架构(下属账号) |
| SA-A-05 | 账号有效期设置 | 02-超级管理员 §1 / 03-超级管理员 §3 / 01 §1.4 | 创建时设置有效期 | 到期账号管理 / 到期提醒规则 |
| SA-A-06 | 账号可用性控制 | 02-超级管理员 §1 / 03-超级管理员 §2 | 禁用→session立即失效+小程序下线 | 操作日志(记录启停操作) |
| SA-A-07 | 到期账号管理 | 02-超级管理员 §1 / 03-超级管理员 §3 / 01 §1.4 | 手动续期→账号恢复 | 到期提醒规则配置 |
| SA-A-08 | 到期提醒规则配置 | 02-超级管理员 §1 / 03-超级管理员 §3 / 01 §1.4 | 配置→系统按规则自动提醒 | 到期账号管理 / 系统配置 |
## 业务规则
1. **新建使用单位流程**医院信息→物业公司信息→医院账号→物业管理员账号四步顺序创建来源03-超级管理员 §1
2. **账号有效期必填**:创建医院/物业管理员账号时必须设置有效期来源01 §1.4
3. **物业下属不单独设有效期**下属人员随物业公司管理员账号到期而自动失效来源01 §1.4 / 03-超级管理员 §3
4. **禁用立即生效**禁用账号后session立即失效小程序同步下线来源03-超级管理员 §2
5. **到期提醒弹窗**到期前N天登录时弹窗提醒关闭后可正常使用来源01 §1.4 / 03-超级管理员 §3
6. **过期禁止登录**账号过期后禁止登录需超管手动续期来源01 §1.4 / 03-超级管理员 §3
7. **超管不涉及业务数据**超级管理员仅管理系统配置与账号数据不查看任何业务数据来源01 §1.3 / 03-超级管理员 §7
8. **所有操作记录日志**:账号创建/启停/续期等操作自动记录操作日志来源06-项目技术要求 §4.5