From 5077d7cefc3d519d223ac2816d0629f492831243 Mon Sep 17 00:00:00 2001 From: cysamurai Date: Tue, 7 Jul 2026 17:10:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tax-guidance/.cursor/rules/no-assumptions.mdc | 17 + tax-guidance/api/index.js | 20 + tax-guidance/api/print.js | 32 + tax-guidance/api/ticket.js | 65 + .../TakeTicketPopup/TakeTicketPopup.vue | 338 +++++ tax-guidance/components/TopDivBar.vue | 36 + tax-guidance/components/header.vue | 20 +- tax-guidance/pad-gateway-api-params(2).md | 590 ++++++++ tax-guidance/pages.json | 8 + tax-guidance/pages/index/index.vue | 1038 +++++++------ tax-guidance/pages/login/login.vue | 34 +- tax-guidance/pages/mod/hallInfo.vue | 24 +- tax-guidance/pages/mod/hallManagment.vue | 8 +- tax-guidance/pages/mod/pushMessage.vue | 886 ++++++++--- tax-guidance/pages/mod/sign-priority.vue | 229 +++ tax-guidance/pages/queue/business-select.vue | 940 ++++++------ tax-guidance/pages/queue/index.vue | 1301 +++++++++++------ tax-guidance/pages/table/appointment.vue | 18 +- tax-guidance/pages/table/dailyEntry.vue | 18 +- tax-guidance/pages/table/ticket.vue | 1198 +++++++++------ tax-guidance/scripts/generate-testcases.mjs | 162 ++ tax-guidance/utils/faceImage.js | 129 ++ tax-guidance/utils/printTemplate.js | 220 +++ tax-guidance/utils/request.js | 211 ++- tax-guidance/产品介绍.md | 258 ++++ tax-guidance/测试用例-紫云智能导税.xlsx | Bin 0 -> 18932 bytes 26 files changed, 5711 insertions(+), 2089 deletions(-) create mode 100644 tax-guidance/.cursor/rules/no-assumptions.mdc create mode 100644 tax-guidance/api/print.js create mode 100644 tax-guidance/components/TakeTicketPopup/TakeTicketPopup.vue create mode 100644 tax-guidance/components/TopDivBar.vue create mode 100644 tax-guidance/pad-gateway-api-params(2).md create mode 100644 tax-guidance/pages/mod/sign-priority.vue create mode 100644 tax-guidance/scripts/generate-testcases.mjs create mode 100644 tax-guidance/utils/faceImage.js create mode 100644 tax-guidance/utils/printTemplate.js create mode 100644 tax-guidance/产品介绍.md create mode 100644 tax-guidance/测试用例-紫云智能导税.xlsx diff --git a/tax-guidance/.cursor/rules/no-assumptions.mdc b/tax-guidance/.cursor/rules/no-assumptions.mdc new file mode 100644 index 0000000..4bbfc79 --- /dev/null +++ b/tax-guidance/.cursor/rules/no-assumptions.mdc @@ -0,0 +1,17 @@ +--- +description: Prevent AI from making assumptions or implementing unrequested features +alwaysApply: true +--- + +## Core Behavior Constraints (P0) +**Do NOT** implement features, code, or logic that was not explicitly requested by the user. +**Do NOT** add comments, documentation, or explanatory text unless explicitly requested. +**Do NOT** provide multiple solutions. Choose the optimal solution and implement it directly. + +## Handling Ambiguity (P0) +If the user's request is ambiguous or critical information is missing, **MUST** ask exactly **ONE** clarifying question before taking any action. +**DO NOT** generate any code or assume the missing information. + +## Citation Rules (P0) +**MUST** strictly reference rules from the **actual files** located in `.cursor/rules/`. +**DO NOT** invent, cite, or assume the existence of any rule that is not explicitly defined. diff --git a/tax-guidance/api/index.js b/tax-guidance/api/index.js index ae704fb..76d9970 100644 --- a/tax-guidance/api/index.js +++ b/tax-guidance/api/index.js @@ -12,6 +12,16 @@ export const userLogin = (loginData) => { }) } +// 获取当前用户信息(pad.auth -> /profile) +export const getAuthProfile = (params = {}) => { + return request({ + tag: 'pad.auth', + path: '/profile', + method: 'GET', + query: params + }) +} + // 今日进厅数据(按大厅相关归到 pad.hallSystem,可根据后端最终路由调整) export const getdailyEntry = (params) => { return request({ @@ -50,4 +60,14 @@ export const getBizList = (params = {}) => { method: 'GET', query: params }) +} + +// 生成健康报告查询 URL(pad.business -> /health-reopt/url) +export const getHealthReportUrl = (params = {}) => { + return request({ + tag: 'pad.business', + path: '/health-reopt/url', + method: 'POST', + body: params + }) } \ No newline at end of file diff --git a/tax-guidance/api/print.js b/tax-guidance/api/print.js new file mode 100644 index 0000000..d8cf5ad --- /dev/null +++ b/tax-guidance/api/print.js @@ -0,0 +1,32 @@ +import request from '@/utils/request.js' + +// 获取打印小票模板(无自定义配置时返回默认模板) +export const getPrintTemplate = (params = {}) => { + return request({ + tag: 'pad.print', + path: '/template', + method: 'GET', + query: params + }) +} + +// 按当前模板打印小票 +export const printTicket = (params = {}) => { + return request({ + tag: 'pad.print', + path: '/ticket', + method: 'POST', + body: params + }) +} + +// 保存打印小票模板(POST + body,签名参数与其他 POST 接口一致) +export const savePrintTemplate = (params = {}) => { + const body = JSON.parse(JSON.stringify(params)) + return request({ + tag: 'pad.print', + path: '/template', + method: 'POST', + body + }) +} diff --git a/tax-guidance/api/ticket.js b/tax-guidance/api/ticket.js index d6957b6..051c6f7 100644 --- a/tax-guidance/api/ticket.js +++ b/tax-guidance/api/ticket.js @@ -8,4 +8,69 @@ export const takeTicket = (params = {}) => { method: 'POST', body: params }) +} + +// 实名核验记录查询 +export const searchTicketVerify = (params = {}) => { + return request({ + tag: 'pad.ticket', + path: '/verify/search', + method: 'POST', + body: params + }) +} + +// 回签(复号) +export const resumeTicket = (params = {}) => { + return request({ + tag: 'pad.ticket', + path: '/resume', + method: 'POST', + body: params + }) +} + +// 优先取号(插队票) +export const createJumpTicket = (params = {}) => { + return request({ + tag: 'pad.ticket', + path: '/create-jump', + method: 'POST', + body: params + }) +} + +// 获取人脸照片(pad.ticket -> GET /face/image/{uid}) +export const getFaceImage = (uid) => { + const id = String(uid || '').trim() + if (!id) { + return Promise.reject(new Error('uid 不能为空')) + } + const requestParams = { + tag: 'pad.ticket', + path: `/face/image/${encodeURIComponent(id)}`, + method: 'GET', + query: {} + } + console.log('[face-image] 请求参数:', { + uid: id, + tag: requestParams.tag, + method: requestParams.method, + path: requestParams.path, + query: requestParams.query, + }) + return request(requestParams) + .then((result) => { + console.log('[face-image] 返回值:', result) + try { + console.log('[face-image] 返回值 JSON:', JSON.stringify(result, null, 2)) + } catch (error) { + console.log('[face-image] 返回值无法 JSON 序列化:', error) + } + return result + }) + .catch((error) => { + console.log('[face-image] 请求失败:', error) + throw error + }) } \ No newline at end of file diff --git a/tax-guidance/components/TakeTicketPopup/TakeTicketPopup.vue b/tax-guidance/components/TakeTicketPopup/TakeTicketPopup.vue new file mode 100644 index 0000000..f93bd67 --- /dev/null +++ b/tax-guidance/components/TakeTicketPopup/TakeTicketPopup.vue @@ -0,0 +1,338 @@ + + + + + diff --git a/tax-guidance/components/TopDivBar.vue b/tax-guidance/components/TopDivBar.vue new file mode 100644 index 0000000..42a49da --- /dev/null +++ b/tax-guidance/components/TopDivBar.vue @@ -0,0 +1,36 @@ + + + + + diff --git a/tax-guidance/components/header.vue b/tax-guidance/components/header.vue index 71670bf..00a8cbb 100644 --- a/tax-guidance/components/header.vue +++ b/tax-guidance/components/header.vue @@ -2,7 +2,7 @@
- 国家税务总局XX市XX税务局办税服务厅 + {{ hallName }}
{{ currentDate }} @@ -18,10 +18,20 @@ onMounted, onUnmounted } from 'vue' + import { onShow } from '@dcloudio/uni-app' const currentTime = ref('') const currentDate = ref('') const currentDay = ref('') + const hallName = ref('国家税务总局XX市XX税务局办税服务厅') + const hallNameEvent = 'hall-name-updated' + + const loadHallNameFromStorage = () => { + const savedHallName = uni.getStorageSync('swjgMC') + if (savedHallName) { + hallName.value = savedHallName + } + } const updateDateTime = () => { const now = new Date() @@ -46,14 +56,22 @@ let timer onMounted(() => { + loadHallNameFromStorage() + uni.$on(hallNameEvent, loadHallNameFromStorage) updateDateTime() // 立即执行一次 timer = setInterval(updateDateTime, 1000) // 每秒更新 }); + onShow(() => { + // 页面重新展示时再次读取,确保重启/回到页面后名称可见 + loadHallNameFromStorage() + }) + onUnmounted(() => { if (timer) { clearInterval(timer) } + uni.$off(hallNameEvent, loadHallNameFromStorage) }) diff --git a/tax-guidance/pad-gateway-api-params(2).md b/tax-guidance/pad-gateway-api-params(2).md new file mode 100644 index 0000000..bcdba93 --- /dev/null +++ b/tax-guidance/pad-gateway-api-params(2).md @@ -0,0 +1,590 @@ +# PAD 接口文档 + +## 1. 基础信息 + +- 服务默认端口:`8845` +- 默认基础地址:`http://:8845` +- 直连接口统一前缀:`/api/queue/**` +- 本文范围: + - `pad.*` 标签对应的直连接口 + - 虽然路径不在 `/api/queue/pad/**` 下,但 Swagger 标签仍属于 `pad.*` 的配套接口 + - `/public` 统一网关映射 + +## 2. 统一返回结构 + +### 2.1 直连接口返回结构 + +绝大多数直连接口返回统一结构 `R`: + +```json +{ + "code": 200, + "msg": "操作成功", + "data": {} +} +``` + +字段说明: + +| 字段 | 类型 | 说明 | +|---|---|---| +| `code` | `int` | 状态码,成功通常为 `200` | +| `msg` | `string` | 返回消息 | +| `data` | `object/array/null` | 具体业务数据 | + +### 2.2 `/public` 网关返回结构 + +`/public` 返回 `PublicGatewayResponseVo`,比 `R` 多一个 `traceId`: + +```json +{ + "code": 200, + "msg": "OK", + "traceId": "202604210001", + "data": {} +} +``` + +## 3. 接口清单 + +> 说明: +> - `Header.Authorization` 表示请求头 `Authorization: Bearer ` +> - `path.xxx` 表示路径参数 +> - `query.xxx` 表示查询参数 +> - `body.xxx` 表示 JSON 请求体字段 +> - `未接入` 表示当前不能通过 `/public` 的 `tag + map.path` 转发 + +### 3.1 `pad.auth` + +| 方法 | 请求地址 | 网关调用(tag/path) | 请求参数 | 返回值 | 备注 | +|---|---|---|---|---|---| +| `POST` | `/api/queue/pad/auth/login` | `pad.auth` + `/login` | `body.username`、`body.password`;`body.tenantId` 可选 | `R` | 登录 | +| `POST` | `/api/queue/pad/auth/refresh` | 未接入 | `body.refreshToken` | `R` | 刷新令牌 | +| `POST` | `/api/queue/pad/auth/logout` | 未接入 | `Header.Authorization` | `R` | 用户退出 | +| `GET` | `/api/queue/pad/auth/profile` | 未接入 | `Header.Authorization` | `R` | 当前用户信息 | +| `POST` | `/api/queue/pad/auth/sign` | `pad.auth` + `/sign` | `body.tag`、`body.path`、`body.timestamp`、`body.nonce`;`body.query`、`body.body` 可选 | `R` | 生成签名 | +| `GET` | `/api/queue/pad/auth/validate` | 未接入 | `Header.Authorization` | `R` | 校验令牌 | + +### 3.2 `pad.business` + +| 方法 | 请求地址 | 网关调用(tag/path) | 请求参数 | 返回值 | 备注 | +|---|---|---|---|---|---| +| `GET` | `/api/queue/pad/business` | 未接入 | 无 | `R>` | 查询所有业务类型 | +| `POST` | `/api/queue/pad/business` | 未接入 | `body.name`;`body.prefix` 可选但必须唯一;`body.enabled`、`body.type`、`body.handleCount`、`body.isSpecial` 可选 | `R` | 创建业务类型 | +| `GET` | `/api/queue/pad/business/{uid}` | 未接入 | `path.uid` | `R` | 按 UID 查询 | +| `PUT` | `/api/queue/pad/business/{uid}` | 未接入 | `path.uid`、`body.name`;其他字段同创建 | `R` | 更新业务类型 | +| `DELETE` | `/api/queue/pad/business/{uid}` | 未接入 | `path.uid` | `R` | 删除业务类型 | +| `PATCH` | `/api/queue/pad/business/{uid}/enabled` | 未接入 | `path.uid`、`query.enabled` | `R` | 更新启用状态 | +| `GET` | `/api/queue/pad/business/enabled` | `pad.business` + `/enabled` | 无 | `R>` | 查询启用业务类型 | +| `POST` | `/api/queue/pad/business/health-reopt/url` | 未接入 | `body.id`;`body.swjgMc`、`body.swjgDm` 可选 | `R` | 生成健康报告查询 URL | +| `GET` | `/api/queue/pad/business/page` | 未接入 | `query.page` 默认 `1`、`query.size` 默认 `10` | `R` | 分页查询 | +| `GET` | `/api/queue/pad/business/search` | 未接入 | `query.name` | `R>` | 名称模糊查询 | +| `GET` | `/api/queue/pad/business/special` | 未接入 | 无 | `R>` | 查询特殊业务 | +| `GET` | `/api/queue/pad/business/statistics` | 未接入 | 无 | `R` | 业务统计 | +| `GET` | `/api/queue/pad/business/type/{type}` | 未接入 | `path.type` | `R>` | 按类型查询 | +| `GET` | `/api/queue/pad/business/validate/name` | 未接入 | `query.name`;`query.excludeUid` 可选 | `R` | 校验名称唯一性 | +| `GET` | `/api/queue/pad/business/validate/prefix` | 未接入 | `query.prefix`;`query.excludeUid` 可选 | `R` | 校验前缀唯一性 | + +### 3.3 `pad.menu` + +| 方法 | 请求地址 | 网关调用(tag/path) | 请求参数 | 返回值 | 备注 | +|---|---|---|---|---|---| +| `GET` | `/api/queue/pad/menu/business-types` | 未接入 | 无 | `R` | 获取业务类型菜单 | +| `GET` | `/api/queue/pad/menu/main` | `pad.menu` + `/main` | 无 | `R` | 获取主菜单 | + +### 3.4 `pad.print` + +| 方法 | 请求地址 | 网关调用(tag/path) | 请求参数 | 返回值 | 备注 | +|---|---|---|---|---|---| +| `GET` | `/api/queue/pad/print/printers` | `pad.print` + `/printers` | 无 | `R>` | 获取打印机列表 | +| `POST` | `/api/queue/pad/print/text` | `pad.print` + `/text` | `body.content`;`body.printerName` 可选 | `R` | 打印文本 | + +### 3.5 `pad.test` + +| 方法 | 请求地址 | 网关调用(tag/path) | 请求参数 | 返回值 | 备注 | +|---|---|---|---|---|---| +| `POST` | `/api/queue/pad/test/medical-report/url` | 未接入 | `body.id`、`body.swjgMc`、`body.swjgDm` | `R` | 测试用医保报告 URL | + +### 3.6 `pad.ticket` + +| 方法 | 请求地址 | 网关调用(tag/path) | 请求参数 | 返回值 | 备注 | +|---|---|---|---|---|---| +| `GET` | `/api/queue/pad/business/list` | 未接入 | 无 | `R>` | 虽然路径不在 `/ticket` 下,但 Swagger Tag 属于 `pad.ticket` | +| `GET` | `/api/queue/pad/ticket/{uid}` | 未接入 | `path.uid` | `R` | 按 UID 查询票号 | +| `PATCH` | `/api/queue/pad/ticket/{uid}/assign-window` | 未接入 | `path.uid`、`query.winId` | `R` | 指派窗口 | +| `GET` | `/api/queue/pad/ticket/{uid}/status` | 未接入 | `path.uid` | `R` | 按 UID 查询状态 | +| `PATCH` | `/api/queue/pad/ticket/{uid}/status` | 未接入 | `path.uid`、`query.status` | `R` | 更新票号状态 | +| `GET` | `/api/queue/pad/ticket/business/{bizUid}` | 未接入 | `path.bizUid` | `R>` | 按业务类型查询票号 | +| `POST` | `/api/queue/pad/ticket/call/{ticketNumber}` | 未接入 | `path.ticketNumber`、`query.windowNumber` | `R` | 指定票号叫号 | +| `POST` | `/api/queue/pad/ticket/call/next` | 未接入 | `body.windowUid`;`body.empUid` 可选 | `R` | 叫下一号 | +| `POST` | `/api/queue/pad/ticket/call/specific/{ticketUid}` | 未接入 | `path.ticketUid`、`query.windowUid`;`query.empUid` 可选 | `R` | 叫指定票 | +| `POST` | `/api/queue/pad/ticket/create-jump` | 未接入 | `body.bizUid`;`body.rankUserName`、`body.rankUserPhone`、`body.idCard`、`body.tktId` 可选 | `R` | 创建插队票 | +| `GET` | `/api/queue/pad/ticket/list` | `pad.ticket` + `/list` | `query.page` 默认 `1`、`query.size` 默认 `10`;`query.status`、`query.businessType`、`query.tabType` 可选 | `R` | 票号列表 | +| `GET` | `/api/queue/pad/ticket/list-by-status/{status}` | 未接入 | `path.status` | `R>` | 按状态码查询 | +| `POST` | `/api/queue/pad/ticket/resume` | 未接入 | `body.resumeToken`;`body.targetPosition` 可选,默认 `3` | `R` | 复号 | +| `POST` | `/api/queue/pad/ticket/verify/search` | 未接入 | `body.name`、`body.idCard`、`body.phone` 至少传一个;三者均按包含匹配;`body.page` 默认 `1`、`body.size` 默认 `10` | `R` | 模糊查询 `ticket_verify` 并判断是否已完成实名/人脸比对 | +| `POST` | `/api/queue/pad/ticket/skip/{ticketNumber}` | 未接入 | `path.ticketNumber` | `R` | 跳过票号 | +| `GET` | `/api/queue/pad/ticket/status/{ticketNumber}` | 未接入 | `path.ticketNumber` | `R` | 按票号字符串查询状态 | +| `POST` | `/api/queue/pad/ticket/suspend` | 未接入 | `body.ticketUid`;`body.windowUid`、`body.empUid`、`body.idCard`、`body.phone` 可选 | `R` | 挂起票号 | +| `GET` | `/api/queue/pad/ticket/suspend/info/{token}` | 未接入 | `path.token` | `R` | 查询挂起详情 | +| `POST` | `/api/queue/pad/ticket/take` | `pad.queue` + `/ticket/take` | `body.bizUid`,且 `body.idCard` / `body.rankUserPhone` 至少传一个;`body.rankUserName`、`body.enterpriseId`、`body.appointmentUid`、`body.tktId` 可选 | `R` | 取号 | +| `POST` | `/api/queue/pad/ticket/take-by-appointment` | 未接入 | `body.bizUid`、`body.appointmentUid` | `R` | 预约换号 | +| `GET` | `/api/queue/pad/ticket/today_unified_tickets` | 未接入 | `query.page` 默认 `1`、`query.size` 默认 `10` | `R` | 今日统一票号列表 | +| `GET` | `/api/queue/pad/ticket/waiting-count` | 未接入 | `query.bizUid` 可选 | `R` | 查询等待人数 | + +### 3.7 `pad.appointment` + +| 方法 | 请求地址 | 网关调用(tag/path) | 请求参数 | 返回值 | 备注 | +|---|---|---|---|---|---| +| `GET` | `/api/queue/pad/appointment/time-slots` | `pad.appointment` + `/time-slots` | 无 | `R>` | 可用时间段 | +| `GET` | `/api/queue/pad/appointment/today` | 未接入 | `query.page` 默认 `1`、`query.size` 默认 `10`;`query.status`、`query.businessType`、`query.keyword` 可选 | `R` | 今日预约列表 | +| `DELETE` | `/api/queue/pad/appointment/{appointmentId}` | 未接入 | `path.appointmentId` | `R` | 取消预约 | +| `PUT` | `/api/queue/pad/appointment/{appointmentId}/status` | 未接入 | `path.appointmentId`、`query.status` | `R` | 更新预约状态 | +| `GET` | `/api/queue/pad/appointment/statistics` | 未接入 | `query.date` 可选 | `R` | 预约统计 | + +### 3.8 `pad.window` + +| 方法 | 请求地址 | 网关调用(tag/path) | 请求参数 | 返回值 | 备注 | +|---|---|---|---|---|---| +| `GET` | `/api/queue/pad/window/list` | `pad.window` + `/list` | `query.page` 默认 `1`、`query.size` 默认 `10`;`query.enabled`、`query.name` 可选 | `R` | 窗口列表 | +| `GET` | `/api/queue/pad/window/monitor/list` | `pad.window` + `/monitor/list` | 无 | `R` | 窗口监控列表 | +| `POST` | `/api/queue/pad/window/create` | 未接入 | `body.name`;`body.sid` 可选但必须唯一;`body.enabled`、`body.ledAddress`、`body.ledText`、`body.rankMode`、`body.rankAddress` 可选 | `R` | 创建窗口 | +| `PUT` | `/api/queue/pad/window/{windowId}` | 未接入 | `path.windowId`、`body.name`;其他字段同创建 | `R` | 更新窗口 | +| `PUT` | `/api/queue/pad/window/{windowId}/status` | 未接入 | `path.windowId`、`query.enabled` | `R` | 切换窗口状态 | +| `GET` | `/api/queue/pad/window/{windowId}` | 未接入 | `path.windowId` | `R` | 窗口详情 | +| `GET` | `/api/queue/pad/window/{windowId}/business` | 未接入 | `path.windowId` | `R` | 获取窗口业务关联 | +| `POST` | `/api/queue/pad/window/{windowId}/business` | 未接入 | `path.windowId`;`body` 为数组,元素至少要有 `businessId`,`priority`、`enabled` 可选 | `R` | 设置窗口业务关联 | +| `POST` | `/api/queue/pad/window/{windowId}/led` | 未接入 | `path.windowId`、`query.text` | `R` | LED 显示控制 | +| `DELETE` | `/api/queue/pad/window/{windowId}` | 未接入 | `path.windowId` | `R` | 删除窗口 | + +### 3.9 `tag.hallSystem` + +> Swagger 中标签名是 `tag.hallSystem`,但 `/public` 网关调用时实际 tag 要传 `pad.hallSystem`。 + +| 方法 | 请求地址 | 网关调用(tag/path) | 请求参数 | 返回值 | 备注 | +|---|---|---|---|---|---| +| `GET` | `/api/queue/pad/hallSystem/list` | `pad.hallSystem` + `/list` | `query.prefix` 可选 | `R` | 获取大厅配置列表 | +| `POST` | `/api/queue/pad/hallSystem/value` | `pad.hallSystem` + `/value` | `body.key`;`body.value` 可选 | `R` | 更新大厅配置 | +| `GET` | `/api/queue/pad/hallSystem/overview/metrics` | `pad.hallSystem` + `/overview/metrics`;`pad.queue` + `/overview/metrics` | 无 | `R` | 实时概览指标 | +| `GET` | `/api/queue/pad/hallSystem/overview/metrics/trend` | `pad.hallSystem` + `/overview/metrics/trend`;`pad.queue` + `/overview/metrics/trend` | `query.startTime`、`query.endTime` 可选,格式 `yyyy-MM-dd HH:mm:ss` | `R` | 概览趋势 | + +### 3.10 `pad.window` 配套 caller 窗口接口 + +| 方法 | 请求地址 | 网关调用(tag/path) | 请求参数 | 返回值 | 备注 | +|---|---|---|---|---|---| +| `GET` | `/api/queue/caller/windows/list` | 未接入 | `Header.Authorization` | `R` | 当前账号可绑定窗口列表 | +| `POST` | `/api/queue/caller/windows/select` | 未接入 | `Header.Authorization`、`body.windowUid` | `R` | 绑定窗口 | + +### 3.11 `pad.callTerminal` + +| 方法 | 请求地址 | 网关调用(tag/path) | 请求参数 | 返回值 | 备注 | +|---|---|---|---|---|---| +| `POST` | `/api/queue/caller/call-terminal/init` | 未接入 | `body.windowUid`;`body.empUid` 可选 | `R` | 初始化评价器 | +| `POST` | `/api/queue/caller/call-terminal/call` | `pad.callTerminal` + `/call` | `body.windowUid`;`body.ticketUid`、`body.empUid` 可选 | `R` | 叫号 | +| `POST` | `/api/queue/caller/call-terminal/recall` | 未接入 | `body.ticketUid`;`body.windowUid` 可选 | `R` | 重呼 | +| `POST` | `/api/queue/caller/call-terminal/start` | 未接入 | `body.ticketUid`;`body.windowUid`、`body.empUid` 可选 | `R` | 开始办理 | +| `POST` | `/api/queue/caller/call-terminal/complete` | 未接入 | `body.ticketUid` | `R` | 办结 | +| `POST` | `/api/queue/caller/call-terminal/abandon` | 未接入 | `body.ticketUid` | `R` | 弃号 | +| `POST` | `/api/queue/caller/call-terminal/transfer` | 未接入 | `body.ticketUid`、`body.targetWindowUid` | `R` | 转移 | +| `POST` | `/api/queue/caller/call-terminal/pause` | 未接入 | `body.windowUid`、`body.empUid`、`body.pauseReason` | `R` | 暂停窗口 | +| `POST` | `/api/queue/caller/call-terminal/resume` | 未接入 | `body.windowUid`、`body.empUid` | `R` | 恢复窗口 | +| `POST` | `/api/queue/caller/call-terminal/evaluate` | 未接入 | `body.ticketUid`;如果提交评分,`body.rank` 必须在 `0-6` | `R` | 发起评价或回写评价 | +| `GET` | `/api/queue/caller/call-terminal/is-rank` | 未接入 | `query.ticketUid` | `R>` | 是否已评价 | +| `GET` | `/api/queue/caller/call-terminal/queue-count` | 未接入 | `query.windowUid` | `R>` | 当前窗口排队人数 | +| `GET` | `/api/queue/caller/call-terminal/pool` | 未接入 | `query.winUid`;`query.keyword`、`query.keywords`、`query.status`、`query.page`、`query.size`、`query.pageSize`、`query.pagesize` 可选 | `R` | 可叫号票池 | + +`pad.callTerminal` 常见可选字段: + +- `customText` +- `serviceUrl` +- `forward` +- `read` +- `driver` +- `clients` +- `rankUserName` +- `rankUserPhone` +- `idCard` +- `phone` + +#### `pad.ticket` - 实名核验查询示例 + +请求示例: + +```json +{ + "name": "张", + "idCard": "3301", + "phone": "138", + "page": 1, + "size": 10 +} +``` + +模糊查询规则: + +- `name`:`LIKE %name%` +- `idCard`:`LIKE %idCard%` +- `phone`:`LIKE %phone%` +- 多个条件同时传入时按 `AND` 组合 +- `takeTicketVerified` 复用当前取号实名校验逻辑,仅在传入 `idCard` 或 `phone` 时返回 +- `faceComparePassed` 基于当前最新精确匹配记录的 `compareValue == 1` 判断 + +返回示例: + +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "page": 1, + "size": 10, + "total": 1, + "matched": true, + "takeTicketVerified": true, + "faceComparePassed": true, + "records": [ + { + "uid": 1, + "ticketUid": 101, + "name": "张三", + "idCard": "330102199001011234", + "phone": "13800138000", + "compareCode": "IDC", + "compareValue": 1, + "compareResult": "verified", + "faceComparePassed": true, + "verifyTime": "2026-04-22T10:00:00" + } + ] + } +} +``` + +## 4. `/public` 统一网关 + +### 4.1 入口 + +| 方法 | 请求地址 | 请求参数 | 返回值 | 备注 | +|---|---|---|---|---| +| `POST` | `/public` | 请求体为加密后的字符串;解密后结构见下文 | `PublicGatewayResponseVo` | 统一网关入口 | + +### 4.2 解密后的请求体结构 + +```json +{ + "tag": "pad.ticket", + "map": { + "traceId": "202604210001", + "head": { + "method": "GET", + "contentType": "application/json", + "timestamp": 1710000000000, + "nonce": "n-1", + "signature": "hex-signature" + }, + "path": "/list", + "query": {}, + "body": {} + } +} +``` + +字段说明: + +| 字段 | 说明 | +|---|---| +| `tag` | 路由标签,例如 `pad.auth`、`pad.ticket` | +| `map.traceId` | 链路跟踪号 | +| `map.head.method` | 请求方法;建议显式传 | +| `map.head.timestamp` | 签名时间戳 | +| `map.head.nonce` | 签名随机串 | +| `map.head.signature` | HMAC-SHA256 签名 | +| `map.path` | 子路径,例如 `/login`、`/list` | +| `map.query` | 查询参数对象 | +| `map.body` | 请求体对象 | + +### 4.3 当前已接入 `/public` 的接口 + +| 网关 tag | `map.path` | 实际分发到的直连接口 | 直连接口返回 | +|---|---|---|---| +| `pad.auth` | `/login` | `POST /api/queue/pad/auth/login` | `R` | +| `pad.auth` | `/sign` | `POST /api/queue/pad/auth/sign` | `R` | +| `pad.menu` | `/main` | `GET /api/queue/pad/menu/main` | `R` | +| `pad.appointment` | `/time-slots` | `GET /api/queue/pad/appointment/time-slots` | `R>` | +| `pad.hallSystem` | `/list` | `GET /api/queue/pad/hallSystem/list` | `R` | +| `pad.hallSystem` | `/value` | `POST /api/queue/pad/hallSystem/value` | `R` | +| `pad.hallSystem` | `/overview/metrics` | `GET /api/queue/pad/hallSystem/overview/metrics` | `R` | +| `pad.hallSystem` | `/overview/metrics/trend` | `GET /api/queue/pad/hallSystem/overview/metrics/trend` | `R` | +| `pad.queue` | `/ticket/take` | `POST /api/queue/pad/ticket/take` | `R` | +| `pad.queue` | `/overview/metrics` | `GET /api/queue/pad/hallSystem/overview/metrics` | `R` | +| `pad.queue` | `/overview/metrics/trend` | `GET /api/queue/pad/hallSystem/overview/metrics/trend` | `R` | +| `pad.business` | `/enabled` | `GET /api/queue/pad/business/enabled` | `R>` | +| `pad.ticket` | `/list` | `GET /api/queue/pad/ticket/list` | `R` | +| `pad.window` | `/list` | `GET /api/queue/pad/window/list` | `R` | +| `pad.window` | `/monitor/list` | `GET /api/queue/pad/window/monitor/list` | `R` | +| `pad.print` | `/printers` | `GET /api/queue/pad/print/printers` | `R>` | +| `pad.print` | `/text` | `POST /api/queue/pad/print/text` | `R` | +| `pad.callTerminal` | `/call` | `POST /api/queue/caller/call-terminal/call` | `R` | + +### 4.4 `/public` 调用规则 + +- 除 `pad.auth /login` 和 `pad.auth /sign` 外,其余已接入网关的接口都需要 `map.head.timestamp`、`map.head.nonce`、`map.head.signature` +- `POST` 类接口建议显式传 `map.head.method` +- `pad.auth /sign` 在实际使用时还需要 Bearer Token + +## 5. 常见返回字段说明 + +### 5.1 `LoginResponse` + +主要字段: + +- `access_token` +- `refresh_token` +- `tokenType` +- `expire_in` +- `refresh_expire_in` +- `client_id` +- `scope` +- `openid` +- `userInfo` + +`userInfo` 主要字段: + +- `id` +- `uid` +- `username` +- `realName` +- `role` +- `tenantId` +- `image` + +### 5.2 `Business` + +主要字段: + +- `uid` +- `prefix` +- `name` +- `enabled` +- `type` +- `handleCount` +- `isSpecial` + +### 5.3 `TakeTicketVo` / `TakeTicketResponse` + +主要字段: + +- `uid` +- `tktId` +- `tktIntid` +- `status` +- `bizUid` +- `bizName` +- `bizPrefix` +- `waitingCount` +- `waitingAhead` +- `estimatedWaitMinutes` +- `tktDate` +- `tktTime` +- `hallName` +- `windowNames` +- `message` + +### 5.4 `TicketPageVo` + +主要字段: + +- `list` +- `total` +- `page` +- `size` + +`list` 元素类型为 `UnifiedTicketVo` + +### 5.5 `TicketStatusVo` + +主要字段: + +- `uid` +- `tktId` +- `status` +- `statusText` +- `waitingAhead` +- `estimatedWaitMinutes` + +### 5.6 `TicketStatusDetailVo` + +主要字段: + +- `ticketNumber` +- `uid` +- `businessUid` +- `status` +- `rank` +- `customerName` +- `customerPhone` +- `generateTime` +- `windowId` + +### 5.7 `WaitingCountVo` + +主要字段: + +- `waitingCount` +- `estimatedWaitMinutes` +- `bizUid` + +### 5.8 `WindowPageVo` + +主要字段: + +- `list` +- `total` +- `page` +- `size` +- `enabled` +- `name` + +### 5.9 `WindowActionVo` + +主要字段: + +- `windowId` +- `name` +- `ledAddress` +- `enabled` +- `createTime` +- `displayText` +- `businessCount` + +### 5.10 `WindowDetailVo` + +主要字段: + +- `windowId` +- `name` +- `ledAddress` +- `ledText` +- `enabled` +- `sid` +- `rankMode` +- `rankAddress` +- `currentTicket` +- `todayServed` + +### 5.11 `WindowBusinessListVo` + +主要字段: + +- `windowId` +- `list` + +### 5.12 `WindowMonitorListVo` + +主要字段: + +- `list` +- `idleCount` +- `busyCount` +- `pausedCount` + +### 5.13 `HallSystemListVo` + +主要字段: + +- `list` +- `total` + +`list` 元素 `HallSystemItemVo` 主要字段: + +- `key` +- `value` +- `memo` + +### 5.14 `PadOverviewMetricsVo` + +主要字段: + +- `waitingCount` +- `todayAppointmentCount` +- `idleWindowCount` +- `idleKioskCount` +- `serverTime` + +### 5.15 `PadOverviewMetricsTrendVo` + +主要字段: + +- `startTime` +- `endTime` +- `list` + +### 5.16 `AppointmentPageVo` + +主要字段: + +- `list` +- `total` +- `page` +- `size` + +### 5.17 `AppointmentActionVo` + +主要字段: + +- `appointmentId` +- `newStatus` + +### 5.18 `AppointmentStatisticsVo` + +主要字段: + +- `totalAppointments` +- `todayAppointments` +- `completedAppointments` +- `cancelledAppointments` +- `timeSlotStats` + +### 5.19 `CallerWindowBindingResponse` + +主要字段: + +- `locked` +- `needSelect` +- `selectedWindowUid` +- `selectedWindowCode` +- `selectedWindowName` +- `nextStep` +- `windows` + +### 5.20 `CallTerminalActionResponse` + +主要字段: + +- `action` +- `success` +- `message` +- `ticketUid` +- `ticketNo` +- `ticketStatus` +- `ticketStatusText` +- `windowUid` +- `windowName` +- `resumeToken` +- `led` +- `taxerName` +- `taxerPhone` +- `waitSeconds` + +### 5.21 `CallTerminalTicketPoolResponse` + +主要字段: + +- `winUid` +- `keyword` +- `status` +- `page` +- `size` +- `total` +- `returnedCount` +- `list` diff --git a/tax-guidance/pages.json b/tax-guidance/pages.json index d9e0654..3214c7f 100644 --- a/tax-guidance/pages.json +++ b/tax-guidance/pages.json @@ -88,6 +88,14 @@ } } }, + { + "path": "pages/mod/sign-priority", + "style": { + "app-plus": { + "popGesture": "none" + } + } + }, { "path": "pages/queue/index", "style": { diff --git a/tax-guidance/pages/index/index.vue b/tax-guidance/pages/index/index.vue index f14cfcd..ef91ee8 100644 --- a/tax-guidance/pages/index/index.vue +++ b/tax-guidance/pages/index/index.vue @@ -1,494 +1,558 @@ \ No newline at end of file +.index-body { + height: 90vh; + width: 100vw; + margin-top: 2vh; + display: flex; + flex-direction: column; + // justify-content: center; + align-items: center; + + .body-title { + height: 10vh; + margin-bottom: 2vh; + display: flex; + align-items: center; + justify-content: space-between; + /* 关键:让标题区域占满可用宽度,space-between 才能拉开两端 */ + width: 80vw; + max-width: 100vw; + + .title-mian { + background: linear-gradient(to bottom, #adadad 10%, #fff 90%); + /* 2. 将背景裁剪成文字形状 (需要-webkit-前缀) */ + -webkit-background-clip: text; + background-clip: text; + /* 3. 将文字颜色设为透明,显示出渐变背景 */ + color: transparent; + /* 为了效果明显,可以设置一些字体样式 */ + font-size: 36px; + font-weight: 600; + } + + /* 顶部用户缩略信息 */ + .user-mini { + position: relative; + display: flex; + align-items: center; + background-color: rgba(0, 0, 0, 0.3); + border-radius: 25px; + padding: 6px 10px 6px 6px; + + .user-mini-avatar { + width: 40px; + height: 40px; + border-radius: 50%; + border: 1px solid #fff; + margin-right: 10px; + object-fit: cover; + } + + .user-mini-name { + font-size: 18px; + color: #fff; + } + + .user-menu { + position: absolute; + top: 100%; + right: 0; + margin-top: 6px; + background-color: rgba(0, 0, 0, 0.3); + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + overflow: hidden; + min-width: 120px; + z-index: 10; + + .user-menu-item { + padding: 10px 16px; + font-size: 14px; + color: #fff; + text-align: center; + } + + .user-menu-item:active { + background-color: #f5f5f5; + } + } + } + } + + .body-content { + display: flex; + flex-direction: row; + align-items: stretch; + width: 82vw; + max-width: 82vw; + --module-gap: 10px; + + /* 左侧数据展示列 */ + .stats-column { + width: 18vw; + margin-right: 4vw; + display: flex; + flex-direction: column; + flex: 0 0 18vw; + gap: var(--module-gap); + + h2 { + color: #fff; + margin-bottom: 0; + } + + .stat-card { + background: #ffffffe0; + border-radius: $card-border-radius; + padding: 16px 20px; + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + justify-content: center; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); + } + + .stat-title { + font-size: 16px; + color: #555; + margin-bottom: 8px; + } + + .stat-value { + font-size: 28px; + font-weight: bold; + color: #007aff; + } + } + + .btn-content { + flex: 1; + display: flex; + flex-direction: column; + gap: var(--module-gap); + + .btn-row1 { + flex: 1; + min-height: 0; + width: 100%; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: var(--module-gap); + + .row1-item1 { + background: #cc3333; + border-radius: $card-border-radius; + } + + .row1-item2 { + background-color: #cc3366; + border-radius: $card-border-radius; + } + + .row1-item3 { + background-color: #cc3399; + border-radius: $card-border-radius; + } + } + + .btn-row2 { + flex: 1.6; + min-height: 0; + width: 100%; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: var(--module-gap); + + .row2-item1 { + display: flex; + flex-direction: column; + gap: var(--module-gap); + + .row2-item1-top { + flex: 1; + background-color: #99ccff; + border-radius: $card-border-radius; + } + + .row2-item1-bottom { + flex: 1; + background-color: #9999ff; + border-radius: $card-border-radius; + } + } + + .row2-item2 { + grid-column: span 2; + display: flex; + flex-direction: row; + gap: var(--module-gap); + + .row2-item2-left { + flex: 1; + background-color: #ffcc99; + border-radius: $card-border-radius; + } + + .row2-item2-center { + flex: 1; + background-color: #ffcccc; + border-radius: $card-border-radius; + } + + .row2-item2-right { + flex: 1; + background-color: #ffccff; + border-radius: $card-border-radius; + } + } + } + } + } +} + +.col-center { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.row-center { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; +} + +.btn-p { + font-size: 22px; + font-weight: 800; + color: #fff; + margin: 8px; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); +} + diff --git a/tax-guidance/pages/login/login.vue b/tax-guidance/pages/login/login.vue index f9d6703..00d590b 100644 --- a/tax-guidance/pages/login/login.vue +++ b/tax-guidance/pages/login/login.vue @@ -1,4 +1,4 @@ -e