# 账号管理 > 模块编码: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]超时与加载反馈** - 列表查询API:timeout=15秒;加载中表格区显示v-loading骨架屏遮罩 - 启停等写操作API:timeout=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-1279px(Pad横屏) | 查询条件区一行排列;"联系人"列隐藏,"创建时间"列隐藏 | | 768-1023px(Pad竖屏) | 查询条件区两行排列;列表仅显示:序号、医院名称、状态、操作;其余列折叠,点击行展开详情 | --- ## 页面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 + 文案变为"保存中..." + 按钮disabled;API返回(成功/失败/超时)后恢复 - 保存期间不允许再次点击保存或关闭页面 - 院区行动态增删操作:删除院区确认弹窗pending期间禁用其他删除按钮 8. **[H2]超时与加载反馈** - 新增/编辑提交API(POST/PUT):timeout=30秒 - 编辑模式详情回填API(GET):timeout=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-1279px(Pad横屏) | 基本信息两列排列;院区信息卡片宽度100% | | 768-1023px(Pad竖屏) | 基本信息单列排列(每行一个字段);院区信息卡片宽度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]超时与加载反馈** - 列表查询API:timeout=15秒;加载中表格区显示v-loading骨架屏遮罩 - 启停等写操作API:timeout=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-1279px(Pad横屏) | 查询条件一行排列;"联系人""联系电话"列隐藏 | | 768-1023px(Pad竖屏) | 查询条件两行排列;列表仅显示:序号、公司名称、状态、操作 | --- ## 页面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 + 文案变为"保存中..." + 按钮disabled;API返回(成功/失败/超时)后恢复 - 保存期间不允许再次点击保存或关闭页面 5. **[H2]超时与加载反馈** - 新增/编辑提交API(POST/PUT):timeout=30秒 - 编辑模式详情回填API(GET):timeout=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-1279px(Pad横屏) | 表单两列排列 | | 768-1023px(Pad竖屏) | 表单单列排列 | --- ## 页面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]超时与加载反馈** - 列表查询API:timeout=15秒;加载中表格区显示v-loading骨架屏遮罩 - 启停/续期/重置密码等写操作API:timeout=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-1279px(Pad横屏) | 查询条件一行排列;"角色"列隐藏 | | 768-1023px(Pad竖屏) | 查询条件两行排列;列表仅显示:序号、登录账号、绑定医院、状态、操作 | --- ## 页面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 + 文案变为"保存中..." + 按钮disabled;API返回(成功/失败/超时)后恢复 - 保存期间不允许再次点击保存或关闭页面 7. **[H2]超时与加载反馈** - 新增提交API(POST):timeout=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-1279px(Pad横屏) | 表单两列排列 | | 768-1023px(Pad竖屏) | 表单单列排列,每个字段独占一行 | --- ## 页面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]超时与加载反馈** - 列表查询API:timeout=15秒;加载中表格区显示v-loading骨架屏遮罩 - 启停/续期/重置密码等写操作API:timeout=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-1279px(Pad横屏) | 查询条件两行排列;"角色"列隐藏 | | 768-1023px(Pad竖屏) | 查询条件两行排列;列表仅显示:序号、登录账号、绑定物业、状态、操作 | --- ## 页面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 + 文案变为"保存中..." + 按钮disabled;API返回(成功/失败/超时)后恢复 - 保存期间不允许再次点击保存或关闭页面 7. **[H2]超时与加载反馈** - 新增提交API(POST):timeout=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-1279px(Pad横屏) | 表单两列排列 | | 768-1023px(Pad竖屏) | 表单单列排列 | --- ## 页面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]超时与加载反馈** - 列表查询API:timeout=15秒;加载中表格区显示v-loading骨架屏遮罩 - 续期写操作API:timeout=30秒;操作按钮:loading态 - 统计卡片数据加载API:timeout=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-1279px(Pad横屏) | 统计卡片一行三个;"账号类型"列隐藏 | | 768-1023px(Pad竖屏) | 统计卡片一行三个(缩小间距);列表仅显示:序号、登录账号、绑定单位、剩余天数、操作 | --- ## 页面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 + 文案变为"保存中..." + 按钮disabled;API返回(成功/失败/超时)后恢复 - 保存期间不允许再次点击保存或关闭页面 8. **[H2]超时与加载反馈** - 保存配置提交API(PUT):timeout=30秒 - 查询配置回填API(GET):timeout=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-1279px(Pad横屏) | 表单标签宽度120px,输入区域宽度350px | | 768-1023px(Pad竖屏) | 表单标签宽度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)