commit 60505081448e1e14786465d837f6e04a7af21ed7 Author: rain Date: Thu Jul 9 17:29:25 2026 +0800 家谱现有接口调试50% diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..aef8443 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..82682d7 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,115 @@ +# AGENTS.md +Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed. + +Tradeoff: These guidelines bias toward caution over speed. For trivial tasks, use judgment. + +1. Think Before Coding +Don't assume. Don't hide confusion. Surface tradeoffs. + +Before implementing: + +State your assumptions explicitly. If uncertain, ask. +If multiple interpretations exist, present them instead of picking silently. +If a simpler approach exists, say so. Push back when warranted. +If something is unclear, stop. Name what is confusing. Ask. + +2. Simplicity First +Minimum code that solves the problem. Nothing speculative. + +No features beyond what was asked. +No abstractions for single-use code. +No "flexibility" or "configurability" that was not requested. +No error handling for impossible scenarios. +If you write 200 lines and it could be 50, rewrite it. +Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify. + +3. Surgical Changes +Touch only what you must. Clean up only your own mess. + +When editing existing code: + +Do not "improve" adjacent code, comments, or formatting. +Do not refactor things that are not broken. +Match existing style, even if you would do it differently. +If you notice unrelated dead code, mention it instead of deleting it. + +When your changes create orphans: + +Remove imports, variables, functions, files, docs, and references that YOUR changes made unused. +Do not remove pre-existing dead code unless asked. +The test: Every changed line should trace directly to the user's request. + +4. Contract Discipline +When a new contract lands, make it the only contract in the same change. + +A contract means any source-of-truth rule that code depends on: data shape, API boundary, schema, field name, config key, prompt format, file layout, runtime entrypoint, ownership boundary, or workflow. + +When introducing or changing a contract, do all three in the same round: + +Specify the single owner. + +Identify the one module, function, schema, document, or service that owns the contract. +Do not leave the same rule duplicated across frontend, backend, scripts, prompts, docs, or tests. +If multiple places need the value, they should consume it from the owner rather than redefine it. + +Delete old fields and old entrypoints. + +Remove obsolete fields, fallback reads, compatibility branches, legacy routes, stale config keys, old scripts, and old docs. +Do not keep the old path "just in case" unless the user explicitly asks for a compatibility period. +If compatibility is required, name it as temporary, define the removal condition, and keep it narrow. + +Tighten validators and runtime together. + +Update schemas, validators, tests, fixtures, seed data, docs, and runtime code in the same change. +Make invalid old inputs fail early and loudly. +Do not allow runtime behavior to accept shapes that validators reject, or validators to allow shapes that runtime no longer supports. +The rule: New owner, old path removed, validator and runtime tightened. If one is missing, the contract migration is not complete. + +5. Goal-Driven Execution +Define success criteria. Loop until verified. + +Transform tasks into verifiable goals: + +"Add validation" -> "Write tests for invalid inputs, then make them pass." +"Fix the bug" -> "Write a test that reproduces it, then make it pass." +"Refactor X" -> "Ensure tests pass before and after." +"Change a contract" -> "Identify owner, remove old paths, tighten validators and runtime, then verify old inputs fail." + +For multi-step tasks, state a brief plan: + +1. [Step] -> verify: [check] +2. [Step] -> verify: [check] +3. [Step] -> verify: [check] + +Strong success criteria let you loop independently. Weak criteria like "make it work" require clarification. + +6. Verification Before Closure +A change is not finished until the relevant behavior is checked. + +Before reporting completion: + +Run the smallest relevant test, script, typecheck, build, or manual verification. +Prefer focused verification over broad, noisy suites when the task is narrow. +If verification cannot be run, say exactly why. +If verification is only by inspection, say that clearly. +If the task changed a contract, verify both the new valid path and the old invalid path. +Do not claim success from intent. Claim success from evidence. + +7. Reporting Results +Be concise, concrete, and honest. + +When summarizing work: + +Say what changed. +Say how it was verified. +Mention any remaining risk or skipped verification. +Do not bury important caveats in vague language. +Do not over-explain routine edits. + +Good final answer shape: + +Changed: [short summary] +Verified: [command/check] +Notes: [only if needed] + +These guidelines are working if: fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, fewer hidden compatibility paths, clearer ownership of contracts, and clarifying questions happen before implementation rather than after mistakes. diff --git a/APP.openapi.json b/APP.openapi.json new file mode 100644 index 0000000..f900e2d --- /dev/null +++ b/APP.openapi.json @@ -0,0 +1,7613 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "APP", + "description": "家谱业务 APP/PC 用户侧接口文档,用于 Apifox 导入。\n\n导入方式:Apifox -> 导入 API 数据 -> OpenAPI/Swagger -> 选择本文件。\n\n约定:\n- APP 路径前缀为 `/genealogy/app`\n- PC/H5 路径前缀为 `/genealogy/pc`\n- 后台管理接口不包含在本文件内\n- 登录后接口需要携带 token,且请求 Header 需携带 `clientid`\n", + "version": "1.0.0" + }, + "tags": [ + { + "name": "验证中心" + }, + { + "name": "认证登录" + }, + { + "name": "文件上传" + }, + { + "name": "行政区划" + }, + { + "name": "家谱" + }, + { + "name": "家谱成员" + }, + { + "name": "字辈谱" + }, + { + "name": "世系人物" + }, + { + "name": "家族圈" + }, + { + "name": "内容文章" + }, + { + "name": "相册" + }, + { + "name": "祭祀" + }, + { + "name": "族务记录" + }, + { + "name": "消息通知" + }, + { + "name": "意见反馈" + }, + { + "name": "VIP" + } + ], + "paths": { + "/captcha/require": { + "get": { + "summary": "查询当前场景是否需要验证", + "deprecated": false, + "description": "用于登录、注册、换绑手机等场景。按租户、客户端和场景编码匹配后台验证策略。", + "tags": [ + "验证中心" + ], + "parameters": [ + { + "name": "tenantId", + "in": "query", + "description": "租户ID,用于区分不同租户的数据和验证策略", + "required": false, + "example": "000000", + "schema": { + "type": "string" + } + }, + { + "name": "clientId", + "in": "query", + "description": "客户端ID,对应后台客户端管理中的APP/PC/小程序等终端", + "required": false, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + }, + { + "name": "sceneCode", + "in": "query", + "description": "验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER", + "required": true, + "example": "APP_LOGIN", + "schema": { + "type": "string" + } + }, + { + "name": "subject", + "in": "query", + "description": "验证主体,通常为手机号、用户ID或登录账号", + "required": false, + "example": "13800000000", + "schema": { + "type": "string" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VerificationRequireResult", + "description": "验证需求查询结果" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/captcha/challenge": { + "post": { + "summary": "生成验证挑战", + "deprecated": false, + "description": "统一生成验证码。策略为天爱时返回行为验证数据;策略为系统图形时返回 uuid 和 img。", + "tags": [ + "验证中心" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VerificationChallengeBody" + }, + "example": { + "tenantId": "000000", + "clientId": "428a8310cd442757ae699df5d894f051", + "sceneCode": "APP_LOGIN", + "subject": "13800000000" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VerificationChallengeResult", + "description": "验证挑战生成结果" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/captcha/verify": { + "post": { + "summary": "校验验证结果并换取 validToken", + "deprecated": false, + "description": "验证通过后返回 validToken,登录、注册、发短信等业务接口将 validToken 随请求体一起提交。", + "tags": [ + "验证中心" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VerificationCheckBody" + }, + "examples": { + "tianai_behavior": { + "value": { + "tenantId": "000000", + "clientId": "428a8310cd442757ae699df5d894f051", + "sceneCode": "APP_LOGIN", + "subject": "13800000000", + "challengeId": "CAPTCHA_CHALLENGE_ID", + "providerCode": "tianai", + "captchaType": "SLIDER", + "payload": { + "id": "tianai-captcha-id", + "data": { + "bgImageWidth": 340, + "bgImageHeight": 180, + "startSlidingTime": 1720000000000, + "endSlidingTime": 1720000001500, + "trackList": [] + } + } + }, + "summary": "天爱滑块/旋转/滑动还原/文字点选验证" + }, + "system_image": { + "value": { + "tenantId": "000000", + "clientId": "428a8310cd442757ae699df5d894f051", + "sceneCode": "APP_LOGIN", + "subject": "13800000000", + "challengeId": "CAPTCHA_CHALLENGE_ID", + "providerCode": "ruoyi_image", + "captchaType": "math", + "payload": { + "uuid": "captcha-uuid", + "code": "6" + } + }, + "summary": "系统图形验证码" + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VerificationCheckResult", + "description": "验证校验结果" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/auth/code": { + "get": { + "summary": "兼容旧系统图形验证码", + "deprecated": false, + "description": "老版后台登录图形验证码入口。新的 APP/PC 业务优先使用 /captcha/require + /captcha/challenge + /captcha/verify。", + "tags": [ + "验证中心" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/LegacyCaptchaResult", + "description": "兼容旧图形验证码结果" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/auth/register": { + "post": { + "summary": "APP 用户注册", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PasswordRegisterBody" + }, + "example": { + "grantType": "password", + "tenantId": "000000", + "phone": "13800000000", + "password": "e10adc3949ba59abbe56e057f20f883e", + "nickName": "测试用户", + "registerSource": "app", + "validToken": "captcha-ticket-or-sms-token" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/LoginResult", + "description": "登录结果" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/auth/login": { + "post": { + "summary": "APP 密码登录", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PasswordLoginBody" + }, + "example": { + "grantType": "password", + "tenantId": "000000", + "phone": "13800000000", + "password": "e10adc3949ba59abbe56e057f20f883e", + "validToken": "captcha-ticket" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/LoginResult", + "description": "登录结果" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/auth/login/sms": { + "post": { + "summary": "APP 短信登录", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SmsLoginBody" + }, + "example": { + "grantType": "sms", + "tenantId": "000000", + "phone": "13800000000", + "smsCode": "123456", + "validToken": "captcha-ticket" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/LoginResult", + "description": "登录结果" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/auth/sms/code": { + "post": { + "summary": "APP 发送短信验证码", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SmsCodeBody" + }, + "example": { + "tenantId": "000000", + "phone": "13800000000", + "sceneCode": "APP_LOGIN", + "validToken": "captcha-ticket" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/auth/profile": { + "get": { + "summary": "APP 当前用户资料", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "put": { + "summary": "APP 修改用户资料", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfileUpdateBody" + }, + "example": { + "nickName": "张三", + "avatarOssId": "2060000000000000000", + "sex": "0", + "birthday": "1990-01-01", + "regionCode": "110101", + "addressDetail": "北京市东城区" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/auth/password": { + "put": { + "summary": "APP 修改密码", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PasswordChangeBody" + }, + "example": { + "oldPassword": "e10adc3949ba59abbe56e057f20f883e", + "newPassword": "25d55ad283aa400af464c76d713c07ad", + "validToken": "captcha-ticket" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/auth/password/reset": { + "put": { + "summary": "APP 找回密码", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PasswordResetBody" + }, + "example": { + "tenantId": "000000", + "phone": "13800000000", + "smsCode": "123456", + "newPassword": "25d55ad283aa400af464c76d713c07ad", + "validToken": "captcha-ticket" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/auth/phone": { + "put": { + "summary": "APP 换绑手机号", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PhoneChangeBody" + }, + "example": { + "newPhone": "13900000000", + "smsCode": "123456", + "validToken": "captcha-ticket" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/auth/account/deactivate": { + "post": { + "summary": "APP 注销账号", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountDeactivateBody" + }, + "example": { + "password": "e10adc3949ba59abbe56e057f20f883e", + "reason": "user request" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/auth/logout": { + "delete": { + "summary": "APP 退出登录", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/files/upload": { + "post": { + "summary": "APP 单文件上传", + "deprecated": false, + "description": "", + "tags": [ + "文件上传" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "binary", + "example": "" + } + }, + "required": [ + "file" + ] + }, + "example": { + "file": "" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/FileUploadResult", + "description": "文件上传结果" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/files/resumable/init": { + "post": { + "summary": "APP 分片上传初始化", + "deprecated": false, + "description": "", + "tags": [ + "文件上传" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResumableInitBody" + }, + "example": { + "fileName": "cover.jpg", + "fileSize": 9532, + "fileMd5": "d41d8cd98f00b204e9800998ecf8427e", + "contentType": "image/jpeg", + "chunkSize": 4194304, + "totalChunks": 1, + "bizType": "genealogy", + "usageScene": "cover" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/files/resumable/chunk": { + "post": { + "summary": "APP 上传分片", + "deprecated": false, + "description": "", + "tags": [ + "文件上传" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "uploadId": { + "type": "string", + "example": "" + }, + "chunkIndex": { + "type": "integer", + "example": 0 + }, + "chunkMd5": { + "type": "string", + "example": "d41d8cd98f00b204e9800998ecf8427e" + }, + "file": { + "type": "string", + "format": "binary", + "example": "" + } + }, + "required": [ + "uploadId", + "chunkIndex", + "chunkMd5", + "file" + ] + }, + "example": { + "uploadId": "", + "chunkIndex": 0, + "chunkMd5": "d41d8cd98f00b204e9800998ecf8427e", + "file": "" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/files/resumable/complete": { + "post": { + "summary": "APP 完成分片上传", + "deprecated": false, + "description": "", + "tags": [ + "文件上传" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResumableCompleteBody" + }, + "example": { + "uploadId": "UPLOAD202607090001", + "fileMd5": "d41d8cd98f00b204e9800998ecf8427e", + "fileSize": 9532 + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/FileUploadResult", + "description": "文件上传结果" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/files/reference": { + "post": { + "summary": "APP 绑定文件业务引用", + "deprecated": false, + "description": "", + "tags": [ + "文件上传" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileReferenceBody" + }, + "example": { + "bizType": "family_feed", + "bizName": "family feed image", + "bizTable": "gen_family_feed", + "bizId": 900013001, + "bizField": "media_oss_ids", + "ossId": "2060000000000000000", + "usageScene": "feed_image" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "delete": { + "summary": "APP 释放文件业务引用", + "deprecated": false, + "description": "", + "tags": [ + "文件上传" + ], + "parameters": [ + { + "name": "bizTable", + "in": "query", + "description": "业务表名,用于文件引用绑定", + "required": true, + "example": "gen_family_feed", + "schema": { + "type": "string" + } + }, + { + "name": "bizId", + "in": "query", + "description": "业务数据ID,用于文件引用绑定", + "required": true, + "example": 900013001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "bizField", + "in": "query", + "description": "业务字段名,例如avatar_oss_id、cover_oss_id", + "required": true, + "example": "media_ids", + "schema": { + "type": "string" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/region/children": { + "get": { + "summary": "查询下级行政区划", + "deprecated": false, + "description": "", + "tags": [ + "行政区划" + ], + "parameters": [ + { + "name": "parentCode", + "in": "query", + "description": "父级行政区划编码,不传或传0查省级", + "required": false, + "example": "51", + "schema": { + "type": "string" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/region/path/{regionCode}": { + "get": { + "summary": "查询行政区划路径", + "deprecated": false, + "description": "", + "tags": [ + "行政区划" + ], + "parameters": [ + { + "name": "regionCode", + "in": "path", + "description": "行政区划编码", + "required": true, + "example": "510000", + "schema": { + "type": "string" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/region/search": { + "get": { + "summary": "搜索行政区划", + "deprecated": false, + "description": "", + "tags": [ + "行政区划" + ], + "parameters": [ + { + "name": "keyword", + "in": "query", + "description": "搜索关键词,支持地区名称或编码模糊查询", + "required": true, + "example": "北京", + "schema": { + "type": "string" + } + }, + { + "name": "level", + "in": "query", + "description": "行政区划级别,1省、2市、3区县、4乡镇街道、5村社区", + "required": false, + "example": 3, + "schema": { + "type": "integer" + } + }, + { + "name": "limit", + "in": "query", + "description": "返回数量上限", + "required": false, + "example": 20, + "schema": { + "type": "integer" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/region/{regionCode}": { + "get": { + "summary": "查询行政区划详情", + "deprecated": false, + "description": "", + "tags": [ + "行政区划" + ], + "parameters": [ + { + "name": "regionCode", + "in": "path", + "description": "行政区划编码", + "required": true, + "example": "510000", + "schema": { + "type": "string" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies": { + "post": { + "summary": "APP 创建家谱", + "deprecated": false, + "description": "", + "tags": [ + "家谱" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenealogyCreateBody" + }, + "example": { + "genealogyName": "彭氏家谱", + "surname": "彭", + "ancestralHall": "陇西堂", + "originPlace": "江西吉安", + "regionCode": "360800", + "addressDetail": "江西省吉安市", + "coverOssId": "2060000000000000000", + "intro": "彭氏家谱简介", + "visibility": "1", + "joinMode": "1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/mine": { + "get": { + "summary": "APP 我的家谱", + "deprecated": false, + "description": "", + "tags": [ + "家谱" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/public": { + "get": { + "summary": "APP 公开家谱", + "deprecated": false, + "description": "", + "tags": [ + "家谱" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/options": { + "get": { + "summary": "APP 家谱下拉选项", + "deprecated": false, + "description": "", + "tags": [ + "家谱" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}": { + "get": { + "summary": "APP 家谱详情", + "deprecated": false, + "description": "", + "tags": [ + "家谱" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "put": { + "summary": "APP 修改家谱", + "deprecated": false, + "description": "", + "tags": [ + "家谱" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenealogyUpdateBody" + }, + "example": { + "genealogyName": "彭氏家谱", + "surname": "彭", + "ancestralHall": "陇西堂", + "originPlace": "江西吉安", + "regionCode": "360800", + "addressDetail": "江西省吉安市", + "coverOssId": "2060000000000000000", + "intro": "更新后的家谱简介", + "visibility": "1", + "joinMode": "1" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/overview": { + "get": { + "summary": "APP 家谱概览", + "deprecated": false, + "description": "", + "tags": [ + "家谱" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/join-applies": { + "post": { + "summary": "APP 申请加入家谱", + "deprecated": false, + "description": "", + "tags": [ + "家谱" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenealogyJoinApplyBody" + }, + "example": { + "applicantName": "彭小明", + "phone": "13800000000", + "relationDesc": "本族成员", + "applyReason": "申请加入家谱维护", + "inviterUserId": 900000001 + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/join-applies/mine": { + "get": { + "summary": "APP 我的加入申请", + "deprecated": false, + "description": "", + "tags": [ + "家谱" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/join-applies/pending": { + "get": { + "summary": "APP 待审核加入申请", + "deprecated": false, + "description": "", + "tags": [ + "家谱" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/join-applies/{applyId}/audit": { + "put": { + "summary": "APP 审核加入申请", + "deprecated": false, + "description": "", + "tags": [ + "家谱" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "applyId", + "in": "path", + "description": "加入家谱申请ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenealogyJoinAuditBody" + }, + "example": { + "status": "1", + "auditRemark": "信息核验通过" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/join-applies/{applyId}": { + "delete": { + "summary": "APP 撤销加入申请", + "deprecated": false, + "description": "", + "tags": [ + "家谱" + ], + "parameters": [ + { + "name": "applyId", + "in": "path", + "description": "加入家谱申请ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/members": { + "get": { + "summary": "APP 家谱成员列表", + "deprecated": false, + "description": "", + "tags": [ + "家谱成员" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/members/options": { + "get": { + "summary": "APP 家谱成员选项", + "deprecated": false, + "description": "", + "tags": [ + "家谱成员" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/members/{memberId}": { + "put": { + "summary": "APP 修改成员", + "deprecated": false, + "description": "", + "tags": [ + "家谱成员" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "memberId", + "in": "path", + "description": "家谱成员ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenealogyMemberUpdateBody" + }, + "example": { + "memberName": "彭小明", + "relationName": "族亲", + "roleType": "member", + "lineagePersonId": 900020001 + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "delete": { + "summary": "APP 移除成员", + "deprecated": false, + "description": "", + "tags": [ + "家谱成员" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "memberId", + "in": "path", + "description": "家谱成员ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/members/me": { + "delete": { + "summary": "APP 退出家谱", + "deprecated": false, + "description": "", + "tags": [ + "家谱成员" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/members/owner-transfer": { + "put": { + "summary": "APP 转让家谱所有者", + "deprecated": false, + "description": "", + "tags": [ + "家谱成员" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenealogyOwnerTransferBody" + }, + "example": { + "targetMemberId": 900010002 + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/generation-poems": { + "get": { + "summary": "APP 查询字辈谱", + "deprecated": false, + "description": "", + "tags": [ + "字辈谱" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "post": { + "summary": "APP 新增字辈", + "deprecated": false, + "description": "", + "tags": [ + "字辈谱" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerationPoemBody" + }, + "example": { + "generationNo": 5, + "generationText": "忠", + "description": "fifth generation poem", + "sortOrder": 5, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/generation-poems/batch/preview": { + "post": { + "summary": "APP 批量字辈预览", + "deprecated": false, + "description": "", + "tags": [ + "字辈谱" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerationPoemBatchBody" + }, + "example": { + "genealogyId": 900001001, + "poemText": "德承家亦\n忠厚传芳", + "disableMissing": false + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/generation-poems/batch/save": { + "post": { + "summary": "APP 批量保存字辈", + "deprecated": false, + "description": "", + "tags": [ + "字辈谱" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerationPoemBatchBody" + }, + "example": { + "genealogyId": 900001001, + "poemText": "德承家亦\n忠厚传芳", + "disableMissing": false + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/generation-poems/{poemId}": { + "put": { + "summary": "APP 修改字辈", + "deprecated": false, + "description": "", + "tags": [ + "字辈谱" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "poemId", + "in": "path", + "description": "字辈谱ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerationPoemBody" + }, + "example": { + "generationNo": 5, + "generationText": "忠", + "description": "fifth generation poem", + "sortOrder": 5, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/lineage/persons": { + "get": { + "summary": "APP 世系人物列表", + "deprecated": false, + "description": "", + "tags": [ + "世系人物" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "post": { + "summary": "APP 新增世系人物", + "deprecated": false, + "description": "", + "tags": [ + "世系人物" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LineagePersonBody" + }, + "example": { + "appUserId": 900000001, + "personNo": "P202607090001", + "personName": "彭德明", + "aliasName": "德明", + "sex": "0", + "generationNo": 1, + "generationName": "德", + "avatarOssId": "2060000000000000000", + "birthDate": "1950-01-01", + "deathDate": null, + "introduction": "first generation introduction" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/lineage/persons/page": { + "get": { + "summary": "APP 世系人物分页", + "deprecated": false, + "description": "", + "tags": [ + "世系人物" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "pageNum", + "in": "query", + "description": "页码,从1开始", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "description": "每页条数", + "required": false, + "schema": { + "type": "integer", + "default": 10 + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PageResult", + "description": "通用分页响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/lineage/persons/options": { + "get": { + "summary": "APP 世系人物选项", + "deprecated": false, + "description": "", + "tags": [ + "世系人物" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/lineage/tree": { + "get": { + "summary": "APP 世系树", + "deprecated": false, + "description": "", + "tags": [ + "世系人物" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/lineage/persons/{personId}": { + "get": { + "summary": "APP 世系人物详情", + "deprecated": false, + "description": "", + "tags": [ + "世系人物" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "personId", + "in": "path", + "description": "世系人物ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "put": { + "summary": "APP 修改世系人物", + "deprecated": false, + "description": "", + "tags": [ + "世系人物" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "personId", + "in": "path", + "description": "世系人物ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LineagePersonBody" + }, + "example": { + "appUserId": 900000001, + "personNo": "P202607090001", + "personName": "彭德明", + "aliasName": "德明", + "sex": "0", + "generationNo": 1, + "generationName": "德", + "avatarOssId": "2060000000000000000", + "birthDate": "1950-01-01", + "deathDate": null, + "introduction": "first generation introduction" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "delete": { + "summary": "APP 删除世系人物", + "deprecated": false, + "description": "", + "tags": [ + "世系人物" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "personId", + "in": "path", + "description": "世系人物ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/lineage/persons/{personId}/children": { + "post": { + "summary": "APP 给人物添加子女", + "deprecated": false, + "description": "", + "tags": [ + "世系人物" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "personId", + "in": "path", + "description": "世系人物ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LineagePersonBody" + }, + "example": { + "appUserId": 900000001, + "personNo": "P202607090001", + "personName": "彭德明", + "aliasName": "德明", + "sex": "0", + "generationNo": 1, + "generationName": "德", + "avatarOssId": "2060000000000000000", + "birthDate": "1950-01-01", + "deathDate": null, + "introduction": "first generation introduction" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/lineage/persons/{personId}/parents": { + "post": { + "summary": "APP 给人物添加父母", + "deprecated": false, + "description": "", + "tags": [ + "世系人物" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "personId", + "in": "path", + "description": "世系人物ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LineagePersonBody" + }, + "example": { + "appUserId": 900000001, + "personNo": "P202607090001", + "personName": "彭德明", + "aliasName": "德明", + "sex": "0", + "generationNo": 1, + "generationName": "德", + "avatarOssId": "2060000000000000000", + "birthDate": "1950-01-01", + "deathDate": null, + "introduction": "first generation introduction" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/lineage/persons/{personId}/siblings": { + "post": { + "summary": "APP 给人物添加兄弟姐妹", + "deprecated": false, + "description": "", + "tags": [ + "世系人物" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "personId", + "in": "path", + "description": "世系人物ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LineagePersonBody" + }, + "example": { + "appUserId": 900000001, + "personNo": "P202607090001", + "personName": "彭德明", + "aliasName": "德明", + "sex": "0", + "generationNo": 1, + "generationName": "德", + "avatarOssId": "2060000000000000000", + "birthDate": "1950-01-01", + "deathDate": null, + "introduction": "first generation introduction" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/lineage/persons/{personId}/spouses": { + "post": { + "summary": "APP 给人物添加配偶", + "deprecated": false, + "description": "", + "tags": [ + "世系人物" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "personId", + "in": "path", + "description": "世系人物ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LineagePersonBody" + }, + "example": { + "appUserId": 900000001, + "personNo": "P202607090001", + "personName": "彭德明", + "aliasName": "德明", + "sex": "0", + "generationNo": 1, + "generationName": "德", + "avatarOssId": "2060000000000000000", + "birthDate": "1950-01-01", + "deathDate": null, + "introduction": "first generation introduction" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/feeds": { + "get": { + "summary": "APP 家族圈动态列表", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "post": { + "summary": "APP 发布家族圈动态", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FamilyFeedBody" + }, + "example": { + "content": "今天上传一张老照片。", + "mediaOssIds": "2060000000000000001,2060000000000000002", + "visibility": "1", + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/feeds/page": { + "get": { + "summary": "APP 家族圈动态分页", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "pageNum", + "in": "query", + "description": "页码,从1开始", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "description": "每页条数", + "required": false, + "schema": { + "type": "integer", + "default": 10 + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PageResult", + "description": "通用分页响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/feeds/{feedId}": { + "get": { + "summary": "APP 家族圈动态详情", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "put": { + "summary": "APP 修改家族圈动态", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FamilyFeedBody" + }, + "example": { + "content": "今天上传一张老照片。", + "mediaOssIds": "2060000000000000001,2060000000000000002", + "visibility": "1", + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/feeds/{feedId}/likes": { + "post": { + "summary": "APP 点赞动态", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "delete": { + "summary": "APP 取消点赞动态", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/feeds/{feedId}/comments": { + "get": { + "summary": "APP 动态评论列表", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "post": { + "summary": "APP 评论动态", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FamilyFeedCommentBody" + }, + "example": { + "parentCommentId": null, + "replyUserId": null, + "content": "这张照片很有年代感。" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/feeds/{feedId}/comments/page": { + "get": { + "summary": "APP 动态评论分页", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "pageNum", + "in": "query", + "description": "页码,从1开始", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "description": "每页条数", + "required": false, + "schema": { + "type": "integer", + "default": 10 + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PageResult", + "description": "通用分页响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/feeds/{feedId}/comments/{commentId}": { + "delete": { + "summary": "APP 删除动态评论", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "commentId", + "in": "path", + "description": "评论ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/article-categories": { + "get": { + "summary": "APP 谱文分类", + "deprecated": false, + "description": "", + "tags": [ + "内容文章" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/articles": { + "get": { + "summary": "APP 谱文列表", + "deprecated": false, + "description": "", + "tags": [ + "内容文章" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "post": { + "summary": "APP 新增谱文", + "deprecated": false, + "description": "", + "tags": [ + "内容文章" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArticleBody" + }, + "example": { + "categoryId": 900040001, + "articleTitle": "族谱源流", + "articleSummary": "介绍本支源流", + "coverOssId": "2060000000000000000", + "articleContent": "

article content

", + "authorName": "族谱管理员", + "sortOrder": 1, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/articles/{articleId}": { + "get": { + "summary": "APP 谱文详情", + "deprecated": false, + "description": "", + "tags": [ + "内容文章" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "articleId", + "in": "path", + "description": "谱文/官网文章ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "put": { + "summary": "APP 修改谱文", + "deprecated": false, + "description": "", + "tags": [ + "内容文章" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "articleId", + "in": "path", + "description": "谱文/官网文章ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArticleBody" + }, + "example": { + "categoryId": 900040001, + "articleTitle": "族谱源流", + "articleSummary": "介绍本支源流", + "coverOssId": "2060000000000000000", + "articleContent": "

article content

", + "authorName": "族谱管理员", + "sortOrder": 1, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/help-articles": { + "get": { + "summary": "APP 帮助文章列表", + "deprecated": false, + "description": "", + "tags": [ + "内容文章" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/help-articles/{helpId}": { + "get": { + "summary": "APP 帮助文章详情", + "deprecated": false, + "description": "", + "tags": [ + "内容文章" + ], + "parameters": [ + { + "name": "helpId", + "in": "path", + "description": "帮助文章ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/promotions": { + "get": { + "summary": "APP 应用推广列表", + "deprecated": false, + "description": "", + "tags": [ + "内容文章" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/albums": { + "get": { + "summary": "APP 相册列表", + "deprecated": false, + "description": "", + "tags": [ + "相册" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "post": { + "summary": "APP 创建相册", + "deprecated": false, + "description": "", + "tags": [ + "相册" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AlbumBody" + }, + "example": { + "albumName": "老照片", + "albumDesc": "family old photos", + "coverOssId": "2060000000000000000", + "sortOrder": 1, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/albums/{albumId}": { + "put": { + "summary": "APP 修改相册", + "deprecated": false, + "description": "", + "tags": [ + "相册" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "albumId", + "in": "path", + "description": "相册ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AlbumBody" + }, + "example": { + "albumName": "老照片", + "albumDesc": "family old photos", + "coverOssId": "2060000000000000000", + "sortOrder": 1, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/albums/{albumId}/photos": { + "get": { + "summary": "APP 相册图片列表", + "deprecated": false, + "description": "", + "tags": [ + "相册" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "albumId", + "in": "path", + "description": "相册ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "post": { + "summary": "APP 上传相册图片记录", + "deprecated": false, + "description": "", + "tags": [ + "相册" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "albumId", + "in": "path", + "description": "相册ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AlbumPhotoBody" + }, + "example": { + "ossId": "2060000000000000000", + "photoTitle": "老宅合影", + "photoDesc": "old house photo", + "photographer": "彭小明", + "shootTime": "2026-07-09 10:00:00", + "sortOrder": 1, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/ceremonies": { + "get": { + "summary": "APP 祭祀列表", + "deprecated": false, + "description": "", + "tags": [ + "祭祀" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "post": { + "summary": "APP 创建祭祀", + "deprecated": false, + "description": "", + "tags": [ + "祭祀" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CeremonyBody" + }, + "example": { + "ceremonyType": "ancestor", + "ceremonyTitle": "清明祭祖", + "ceremonyDesc": "online ancestor ceremony", + "ceremonyTime": "2026-04-04 09:00:00", + "location": "祠堂", + "coverOssId": "2060000000000000000", + "sortOrder": 1, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/ceremonies/{ceremonyId}": { + "get": { + "summary": "APP 祭祀详情", + "deprecated": false, + "description": "", + "tags": [ + "祭祀" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "ceremonyId", + "in": "path", + "description": "祭祀/活动ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "put": { + "summary": "APP 修改祭祀", + "deprecated": false, + "description": "", + "tags": [ + "祭祀" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "ceremonyId", + "in": "path", + "description": "祭祀/活动ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CeremonyBody" + }, + "example": { + "ceremonyType": "ancestor", + "ceremonyTitle": "清明祭祖", + "ceremonyDesc": "online ancestor ceremony", + "ceremonyTime": "2026-04-04 09:00:00", + "location": "祠堂", + "coverOssId": "2060000000000000000", + "sortOrder": 1, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/ceremonies/{ceremonyId}/gifts": { + "get": { + "summary": "APP 祭祀献礼列表", + "deprecated": false, + "description": "", + "tags": [ + "祭祀" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "ceremonyId", + "in": "path", + "description": "祭祀/活动ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "post": { + "summary": "APP 新增祭祀献礼", + "deprecated": false, + "description": "", + "tags": [ + "祭祀" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "ceremonyId", + "in": "path", + "description": "祭祀/活动ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CeremonyGiftBody" + }, + "example": { + "giverName": "彭小明", + "giftAmount": 66.66, + "giftMessage": "缅怀先祖" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/growth-records": { + "get": { + "summary": "APP 成长记录列表", + "deprecated": false, + "description": "", + "tags": [ + "族务记录" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "post": { + "summary": "APP 新增成长记录", + "deprecated": false, + "description": "", + "tags": [ + "族务记录" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GrowthRecordBody" + }, + "example": { + "lineagePersonId": 900020001, + "recordType": "birth", + "recordTitle": "出生记录", + "recordContent": "birth record", + "recordDate": "2026-07-09 10:00:00", + "remindTime": "2027-07-09 10:00:00", + "mediaOssIds": "2060000000000000001", + "sortOrder": 1, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/growth-records/{recordId}": { + "get": { + "summary": "APP 成长记录详情", + "deprecated": false, + "description": "", + "tags": [ + "族务记录" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "recordId", + "in": "path", + "description": "记录ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "put": { + "summary": "APP 修改成长记录", + "deprecated": false, + "description": "", + "tags": [ + "族务记录" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "recordId", + "in": "path", + "description": "记录ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GrowthRecordBody" + }, + "example": { + "lineagePersonId": 900020001, + "recordType": "birth", + "recordTitle": "出生记录", + "recordContent": "birth record", + "recordDate": "2026-07-09 10:00:00", + "remindTime": "2027-07-09 10:00:00", + "mediaOssIds": "2060000000000000001", + "sortOrder": 1, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/memos": { + "get": { + "summary": "APP 备忘录列表", + "deprecated": false, + "description": "", + "tags": [ + "族务记录" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "post": { + "summary": "APP 新增备忘录", + "deprecated": false, + "description": "", + "tags": [ + "族务记录" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemoBody" + }, + "example": { + "memoTitle": "修谱事项", + "memoContent": "contact family members", + "remindTime": "2026-08-01 09:00:00", + "completed": "0", + "mediaOssIds": "2060000000000000001", + "sortOrder": 1, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/memos/{memoId}": { + "get": { + "summary": "APP 备忘录详情", + "deprecated": false, + "description": "", + "tags": [ + "族务记录" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "memoId", + "in": "path", + "description": "", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "put": { + "summary": "APP 修改备忘录", + "deprecated": false, + "description": "", + "tags": [ + "族务记录" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "memoId", + "in": "path", + "description": "", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemoBody" + }, + "example": { + "memoTitle": "修谱事项", + "memoContent": "contact family members", + "remindTime": "2026-08-01 09:00:00", + "completed": "0", + "mediaOssIds": "2060000000000000001", + "sortOrder": 1, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/genealogies/{genealogyId}/merit-records": { + "get": { + "summary": "APP 功德记录列表", + "deprecated": false, + "description": "", + "tags": [ + "族务记录" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "post": { + "summary": "APP 新增功德记录", + "deprecated": false, + "description": "", + "tags": [ + "族务记录" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MeritRecordBody" + }, + "example": { + "donorName": "彭小明", + "meritType": "donation", + "meritTitle": "修谱捐款", + "meritContent": "support genealogy work", + "amount": 100, + "meritTime": "2026-07-09 10:00:00", + "sortOrder": 1, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/notifications": { + "get": { + "summary": "APP 消息通知列表", + "deprecated": false, + "description": "", + "tags": [ + "消息通知" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/notifications/{notificationId}/read": { + "post": { + "summary": "APP 标记通知已读", + "deprecated": false, + "description": "", + "tags": [ + "消息通知" + ], + "parameters": [ + { + "name": "notificationId", + "in": "path", + "description": "通知ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/notifications/read-all": { + "post": { + "summary": "APP 全部标记已读", + "deprecated": false, + "description": "", + "tags": [ + "消息通知" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/feedback": { + "get": { + "summary": "APP 我的反馈列表", + "deprecated": false, + "description": "", + "tags": [ + "意见反馈" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "post": { + "summary": "APP 提交反馈", + "deprecated": false, + "description": "", + "tags": [ + "意见反馈" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackBody" + }, + "example": { + "feedbackType": "bug", + "feedbackContent": "upload image failed", + "contactInfo": "13800000000" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/vip/packages": { + "get": { + "summary": "APP VIP 套餐列表", + "deprecated": false, + "description": "", + "tags": [ + "VIP" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + }, + "/genealogy/app/vip/orders": { + "get": { + "summary": "APP VIP 订单列表", + "deprecated": false, + "description": "", + "tags": [ + "VIP" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + }, + "post": { + "summary": "APP 创建 VIP 订单", + "deprecated": false, + "description": "", + "tags": [ + "VIP" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VipOrderBody" + }, + "example": { + "packageId": 900060001, + "genealogyId": 900001001, + "payType": "wechat" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken": [] + } + ] + } + } + }, + "components": { + "schemas": { + "PasswordRegisterBody": { + "type": "object", + "required": [ + "grantType", + "tenantId", + "phone", + "password" + ], + "properties": { + "clientId": { + "description": "客户端ID,对应后台客户端管理中的APP/PC/小程序等终端", + "type": "string" + }, + "grantType": { + "description": "授权类型,例如password、sms、wechat、apple", + "type": "string", + "example": "password" + }, + "tenantId": { + "description": "租户ID,用于区分不同租户的数据和验证策略", + "type": "string", + "example": "000000" + }, + "phone": { + "description": "手机号", + "type": "string", + "example": "13800000000" + }, + "password": { + "type": "string", + "description": "32 位 MD5", + "example": "e10adc3949ba59abbe56e057f20f883e" + }, + "nickName": { + "description": "用户昵称", + "type": "string" + }, + "registerSource": { + "description": "注册来源,例如APP、PC、H5、MINI_PROGRAM", + "type": "string" + }, + "validToken": { + "description": "验证中心通过后返回的票据,用于注册/登录/换绑等二次校验", + "type": "string" + } + } + }, + "PasswordLoginBody": { + "type": "object", + "required": [ + "grantType", + "tenantId", + "phone", + "password" + ], + "properties": { + "clientId": { + "description": "客户端ID,对应后台客户端管理中的APP/PC/小程序等终端", + "type": "string" + }, + "grantType": { + "description": "授权类型,例如password、sms、wechat、apple", + "type": "string", + "example": "password" + }, + "tenantId": { + "description": "租户ID,用于区分不同租户的数据和验证策略", + "type": "string", + "example": "000000" + }, + "phone": { + "description": "手机号", + "type": "string", + "example": "13800000000" + }, + "password": { + "type": "string", + "description": "32 位 MD5", + "example": "e10adc3949ba59abbe56e057f20f883e" + }, + "validToken": { + "description": "验证中心通过后返回的票据,用于注册/登录/换绑等二次校验", + "type": "string" + } + } + }, + "SmsLoginBody": { + "type": "object", + "required": [ + "grantType", + "tenantId", + "phone", + "smsCode" + ], + "properties": { + "clientId": { + "description": "客户端ID,对应后台客户端管理中的APP/PC/小程序等终端", + "type": "string" + }, + "grantType": { + "description": "授权类型,例如password、sms、wechat、apple", + "type": "string", + "example": "sms" + }, + "tenantId": { + "description": "租户ID,用于区分不同租户的数据和验证策略", + "type": "string", + "example": "000000" + }, + "phone": { + "description": "手机号", + "type": "string", + "example": "13800000000" + }, + "smsCode": { + "description": "短信验证码", + "type": "string", + "example": "1234" + } + } + }, + "SmsCodeBody": { + "type": "object", + "required": [ + "grantType", + "tenantId", + "sceneCode", + "phone", + "validToken" + ], + "properties": { + "clientId": { + "description": "客户端ID,对应后台客户端管理中的APP/PC/小程序等终端", + "type": "string" + }, + "grantType": { + "description": "授权类型,例如password、sms、wechat、apple", + "type": "string", + "example": "sms" + }, + "tenantId": { + "description": "租户ID,用于区分不同租户的数据和验证策略", + "type": "string", + "example": "000000" + }, + "sceneCode": { + "description": "验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER", + "type": "string", + "example": "APP_LOGIN" + }, + "phone": { + "description": "手机号", + "type": "string", + "example": "13800000000" + }, + "validToken": { + "description": "验证中心通过后返回的票据,用于注册/登录/换绑等二次校验", + "type": "string" + } + } + }, + "ProfileUpdateBody": { + "type": "object", + "properties": { + "nickName": { + "description": "用户昵称", + "type": "string" + }, + "avatarOssId": { + "description": "头像文件OSS ID", + "type": "integer", + "format": "int64" + }, + "sex": { + "description": "性别,建议使用系统字典值", + "type": "string" + }, + "birthday": { + "description": "生日,格式 yyyy-MM-dd", + "type": "string", + "format": "date" + }, + "provinceCode": { + "type": "string" + }, + "cityCode": { + "type": "string" + }, + "districtCode": { + "type": "string" + } + } + }, + "PasswordChangeBody": { + "type": "object", + "required": [ + "oldPassword", + "newPassword" + ], + "properties": { + "oldPassword": { + "type": "string", + "description": "32 位 MD5" + }, + "newPassword": { + "type": "string", + "description": "32 位 MD5" + } + } + }, + "PasswordResetBody": { + "type": "object", + "required": [ + "tenantId", + "phone", + "smsCode", + "newPassword" + ], + "properties": { + "tenantId": { + "description": "租户ID,用于区分不同租户的数据和验证策略", + "type": "string", + "example": "000000" + }, + "phone": { + "description": "手机号", + "type": "string" + }, + "smsCode": { + "description": "短信验证码", + "type": "string" + }, + "newPassword": { + "type": "string", + "description": "32 位 MD5" + }, + "validToken": { + "description": "验证中心通过后返回的票据,用于注册/登录/换绑等二次校验", + "type": "string" + } + } + }, + "PhoneChangeBody": { + "type": "object", + "required": [ + "newPhone", + "smsCode" + ], + "properties": { + "newPhone": { + "type": "string" + }, + "smsCode": { + "description": "短信验证码", + "type": "string" + }, + "validToken": { + "description": "验证中心通过后返回的票据,用于注册/登录/换绑等二次校验", + "type": "string" + } + } + }, + "AccountDeactivateBody": { + "type": "object", + "required": [ + "password" + ], + "properties": { + "password": { + "type": "string", + "description": "32 位 MD5" + }, + "reason": { + "description": "操作原因或说明", + "type": "string" + } + } + }, + "ResumableInitBody": { + "type": "object", + "required": [ + "fileName", + "fileSize", + "fileMd5", + "chunkSize", + "totalChunks" + ], + "properties": { + "fileName": { + "description": "原始文件名", + "type": "string" + }, + "fileSize": { + "description": "文件大小,单位字节", + "type": "integer", + "format": "int64" + }, + "fileMd5": { + "description": "完整文件MD5,用于秒传和完整性校验", + "type": "string" + }, + "contentType": { + "description": "文件MIME类型,例如 image/jpeg、video/mp4", + "type": "string" + }, + "chunkSize": { + "description": "单个分片大小,单位字节", + "type": "integer", + "example": 4194304 + }, + "totalChunks": { + "description": "总分片数", + "type": "integer" + }, + "bizType": { + "description": "业务类型,例如avatar、cover、feed_image", + "type": "string" + }, + "usageScene": { + "description": "文件使用场景,便于后续引用统计和清理", + "type": "string" + } + } + }, + "ResumableCompleteBody": { + "type": "object", + "required": [ + "uploadId", + "fileMd5", + "fileSize" + ], + "properties": { + "uploadId": { + "description": "分片上传任务ID", + "type": "string" + }, + "fileMd5": { + "description": "完整文件MD5,用于秒传和完整性校验", + "type": "string" + }, + "fileSize": { + "description": "文件大小,单位字节", + "type": "integer", + "format": "int64" + } + } + }, + "FileReferenceBody": { + "type": "object", + "required": [ + "bizType", + "bizTable", + "bizId", + "bizField" + ], + "properties": { + "bizType": { + "description": "业务类型,例如avatar、cover、feed_image", + "type": "string", + "example": "family_feed" + }, + "bizName": { + "description": "业务名称,用于后台展示文件引用来源", + "type": "string", + "example": "家族圈动态" + }, + "bizTable": { + "description": "业务表名,用于文件引用绑定", + "type": "string", + "example": "gen_family_feed" + }, + "bizId": { + "description": "业务数据ID,用于文件引用绑定", + "type": "integer", + "format": "int64" + }, + "bizField": { + "description": "业务字段名,例如avatar_oss_id、cover_oss_id", + "type": "string", + "example": "media_ids" + }, + "ossId": { + "description": "OSS文件ID,表单内部使用,用户侧应通过上传组件获取", + "type": "integer", + "format": "int64" + }, + "ossIds": { + "type": "string", + "example": "2060001,2060002" + }, + "usageScene": { + "description": "文件使用场景,便于后续引用统计和清理", + "type": "string" + }, + "usageName": { + "type": "string" + } + } + }, + "GenerationPoemBatchBody": { + "type": "object", + "properties": { + "content": { + "type": "string", + "example": "德承家亦" + }, + "stopMissingOldGeneration": { + "type": "boolean", + "example": false + } + } + }, + "LineagePersonBody": { + "type": "object", + "properties": { + "appUserId": { + "type": "integer", + "format": "int64" + }, + "personNo": { + "type": "string" + }, + "personName": { + "type": "string" + }, + "aliasName": { + "type": "string" + }, + "sex": { + "description": "性别,建议使用系统字典值", + "type": "string", + "example": "0" + }, + "generationNo": { + "description": "世代序号", + "type": "integer" + }, + "generationName": { + "description": "字辈", + "type": "string" + }, + "avatarOssId": { + "description": "头像文件OSS ID", + "type": "integer", + "format": "int64" + }, + "birthDate": { + "type": "string" + }, + "deathDate": { + "type": "string" + }, + "introduction": { + "type": "string" + } + } + }, + "FamilyFeedBody": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "mediaOssIds": { + "description": "媒体文件OSS ID列表,多个用逗号或数组传递以接口约定为准", + "type": "string", + "example": "2060001,2060002" + }, + "visibility": { + "description": "可见范围,例如0私密、1公开、2成员可见", + "type": "string", + "example": "1" + }, + "status": { + "description": "状态,建议使用对应字典值", + "type": "string", + "example": "0" + } + } + }, + "FamilyFeedCommentBody": { + "type": "object", + "required": [ + "content" + ], + "properties": { + "parentCommentId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "replyUserId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "content": { + "type": "string" + } + } + }, + "GenealogyCreateBody": { + "type": "object", + "required": [ + "genealogyName", + "surname", + "regionCode" + ], + "properties": { + "genealogyName": { + "description": "家谱名称", + "type": "string" + }, + "surname": { + "description": "姓氏", + "type": "string" + }, + "ancestralHall": { + "description": "堂号/祠堂名称", + "type": "string" + }, + "originPlace": { + "description": "籍贯/起源地", + "type": "string" + }, + "regionCode": { + "description": "行政区划编码", + "type": "string" + }, + "addressDetail": { + "description": "详细地址", + "type": "string" + }, + "coverOssId": { + "description": "封面图片OSS ID", + "type": "integer", + "format": "int64" + }, + "intro": { + "description": "简介", + "type": "string" + }, + "visibility": { + "description": "可见范围,例如0私密、1公开、2成员可见", + "type": "string" + }, + "joinMode": { + "description": "加入方式,例如0关闭、1审核、2邀请码", + "type": "string" + } + }, + "example": { + "genealogyName": "彭氏家谱", + "surname": "彭", + "ancestralHall": "陇西堂", + "originPlace": "江西吉安", + "regionCode": "360800", + "addressDetail": "江西省吉安市", + "coverOssId": 2060000000000000000, + "intro": "彭氏家谱简介", + "visibility": "1", + "joinMode": "1" + } + }, + "GenealogyUpdateBody": { + "type": "object", + "properties": { + "genealogyName": { + "description": "家谱名称", + "type": "string" + }, + "surname": { + "description": "姓氏", + "type": "string" + }, + "ancestralHall": { + "description": "堂号/祠堂名称", + "type": "string" + }, + "originPlace": { + "description": "籍贯/起源地", + "type": "string" + }, + "regionCode": { + "description": "行政区划编码", + "type": "string" + }, + "addressDetail": { + "description": "详细地址", + "type": "string" + }, + "coverOssId": { + "description": "封面图片OSS ID", + "type": "integer", + "format": "int64" + }, + "intro": { + "description": "简介", + "type": "string" + }, + "visibility": { + "description": "可见范围,例如0私密、1公开、2成员可见", + "type": "string" + }, + "joinMode": { + "description": "加入方式,例如0关闭、1审核、2邀请码", + "type": "string" + } + }, + "example": { + "genealogyName": "彭氏家谱", + "surname": "彭", + "ancestralHall": "陇西堂", + "originPlace": "江西吉安", + "regionCode": "360800", + "addressDetail": "江西省吉安市", + "coverOssId": 2060000000000000000, + "intro": "更新后的家谱简介", + "visibility": "1", + "joinMode": "1" + } + }, + "GenealogyJoinApplyBody": { + "type": "object", + "properties": { + "applicantName": { + "description": "申请人姓名", + "type": "string" + }, + "phone": { + "description": "手机号", + "type": "string" + }, + "relationDesc": { + "description": "与家谱/人物的关系说明", + "type": "string" + }, + "applyReason": { + "description": "申请理由", + "type": "string" + }, + "inviterUserId": { + "description": "邀请人业务用户ID", + "type": "integer", + "format": "int64" + } + }, + "example": { + "applicantName": "彭小明", + "phone": "13800000000", + "relationDesc": "本族成员", + "applyReason": "申请加入家谱维护", + "inviterUserId": 900000001 + } + }, + "GenealogyJoinAuditBody": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "description": "状态,建议使用对应字典值", + "type": "string" + }, + "auditRemark": { + "description": "审核备注", + "type": "string" + } + }, + "example": { + "status": "1", + "auditRemark": "信息核验通过" + } + }, + "GenealogyMemberUpdateBody": { + "type": "object", + "properties": { + "memberName": { + "description": "成员姓名", + "type": "string" + }, + "relationName": { + "description": "成员关系名称", + "type": "string" + }, + "roleType": { + "description": "成员角色类型,例如创建者、管理员、普通成员", + "type": "string" + }, + "lineagePersonId": { + "description": "绑定的世系人物ID", + "type": "integer", + "format": "int64" + } + }, + "example": { + "memberName": "彭小明", + "relationName": "族亲", + "roleType": "member", + "lineagePersonId": 900020001 + } + }, + "GenealogyOwnerTransferBody": { + "type": "object", + "required": [ + "targetMemberId" + ], + "properties": { + "targetMemberId": { + "description": "目标成员ID", + "type": "integer", + "format": "int64" + } + }, + "example": { + "targetMemberId": 900010002 + } + }, + "GenerationPoemBody": { + "type": "object", + "required": [ + "generationNo", + "generationText" + ], + "properties": { + "generationNo": { + "description": "世代序号", + "type": "integer", + "format": "int64" + }, + "generationText": { + "description": "字辈内容,可单字或批量字辈文本", + "type": "string" + }, + "description": { + "description": "说明或备注", + "type": "string" + }, + "sortOrder": { + "description": "排序值,数值越小越靠前", + "type": "integer", + "format": "int64" + }, + "status": { + "description": "状态,建议使用对应字典值", + "type": "string" + } + }, + "example": { + "generationNo": 5, + "generationText": "忠", + "description": "fifth generation poem", + "sortOrder": 5, + "status": "0" + } + }, + "ArticleBody": { + "type": "object", + "required": [ + "articleTitle", + "articleContent" + ], + "properties": { + "categoryId": { + "description": "分类ID", + "type": "integer", + "format": "int64" + }, + "articleTitle": { + "description": "文章标题", + "type": "string" + }, + "articleSummary": { + "description": "文章摘要", + "type": "string" + }, + "coverOssId": { + "description": "封面图片OSS ID", + "type": "integer", + "format": "int64" + }, + "articleContent": { + "description": "文章内容", + "type": "string" + }, + "authorName": { + "description": "作者名称", + "type": "string" + }, + "sortOrder": { + "description": "排序值,数值越小越靠前", + "type": "integer", + "format": "int64" + }, + "status": { + "description": "状态,建议使用对应字典值", + "type": "string" + } + }, + "example": { + "categoryId": 900040001, + "articleTitle": "族谱源流", + "articleSummary": "介绍本支源流", + "coverOssId": 2060000000000000000, + "articleContent": "

article content

", + "authorName": "族谱管理员", + "sortOrder": 1, + "status": "0" + } + }, + "AlbumBody": { + "type": "object", + "required": [ + "albumName" + ], + "properties": { + "albumName": { + "description": "相册名称", + "type": "string" + }, + "albumDesc": { + "description": "相册说明", + "type": "string" + }, + "coverOssId": { + "description": "封面图片OSS ID", + "type": "integer", + "format": "int64" + }, + "sortOrder": { + "description": "排序值,数值越小越靠前", + "type": "integer", + "format": "int64" + }, + "status": { + "description": "状态,建议使用对应字典值", + "type": "string" + } + }, + "example": { + "albumName": "老照片", + "albumDesc": "family old photos", + "coverOssId": 2060000000000000000, + "sortOrder": 1, + "status": "0" + } + }, + "AlbumPhotoBody": { + "type": "object", + "required": [ + "ossId" + ], + "properties": { + "ossId": { + "description": "OSS文件ID,表单内部使用,用户侧应通过上传组件获取", + "type": "integer", + "format": "int64" + }, + "photoTitle": { + "description": "图片标题", + "type": "string" + }, + "photoDesc": { + "description": "图片说明", + "type": "string" + }, + "photographer": { + "description": "拍摄人", + "type": "string" + }, + "shootTime": { + "description": "拍摄时间", + "type": "string" + }, + "sortOrder": { + "description": "排序值,数值越小越靠前", + "type": "integer", + "format": "int64" + }, + "status": { + "description": "状态,建议使用对应字典值", + "type": "string" + } + }, + "example": { + "ossId": 2060000000000000000, + "photoTitle": "老宅合影", + "photoDesc": "old house photo", + "photographer": "彭小明", + "shootTime": "2026-07-09 10:00:00", + "sortOrder": 1, + "status": "0" + } + }, + "CeremonyBody": { + "type": "object", + "required": [ + "ceremonyType", + "ceremonyTitle" + ], + "properties": { + "ceremonyType": { + "description": "祭祀/活动类型", + "type": "string" + }, + "ceremonyTitle": { + "description": "祭祀/活动标题", + "type": "string" + }, + "ceremonyDesc": { + "description": "祭祀/活动说明", + "type": "string" + }, + "ceremonyTime": { + "description": "祭祀/活动时间", + "type": "string" + }, + "location": { + "description": "地点", + "type": "string" + }, + "coverOssId": { + "description": "封面图片OSS ID", + "type": "integer", + "format": "int64" + }, + "sortOrder": { + "description": "排序值,数值越小越靠前", + "type": "integer", + "format": "int64" + }, + "status": { + "description": "状态,建议使用对应字典值", + "type": "string" + } + }, + "example": { + "ceremonyType": "ancestor", + "ceremonyTitle": "清明祭祖", + "ceremonyDesc": "online ancestor ceremony", + "ceremonyTime": "2026-04-04 09:00:00", + "location": "祠堂", + "coverOssId": 2060000000000000000, + "sortOrder": 1, + "status": "0" + } + }, + "CeremonyGiftBody": { + "type": "object", + "required": [ + "giftAmount" + ], + "properties": { + "giverName": { + "description": "赠送人/献礼人姓名", + "type": "string" + }, + "giftAmount": { + "description": "礼物金额", + "type": "number", + "format": "double" + }, + "giftMessage": { + "description": "礼物留言", + "type": "string" + } + }, + "example": { + "giverName": "彭小明", + "giftAmount": 66.66, + "giftMessage": "缅怀先祖" + } + }, + "GrowthRecordBody": { + "type": "object", + "required": [ + "recordTitle" + ], + "properties": { + "lineagePersonId": { + "description": "绑定的世系人物ID", + "type": "integer", + "format": "int64" + }, + "recordType": { + "description": "记录类型", + "type": "string" + }, + "recordTitle": { + "description": "记录标题", + "type": "string" + }, + "recordContent": { + "description": "记录内容", + "type": "string" + }, + "recordDate": { + "description": "记录日期", + "type": "string" + }, + "remindTime": { + "description": "提醒时间", + "type": "string" + }, + "mediaOssIds": { + "description": "媒体文件OSS ID列表,多个用逗号或数组传递以接口约定为准", + "type": "string" + }, + "sortOrder": { + "description": "排序值,数值越小越靠前", + "type": "integer", + "format": "int64" + }, + "status": { + "description": "状态,建议使用对应字典值", + "type": "string" + } + }, + "example": { + "lineagePersonId": 900020001, + "recordType": "birth", + "recordTitle": "出生记录", + "recordContent": "birth record", + "recordDate": "2026-07-09 10:00:00", + "remindTime": "2027-07-09 10:00:00", + "mediaOssIds": "2060000000000000001", + "sortOrder": 1, + "status": "0" + } + }, + "MemoBody": { + "type": "object", + "required": [ + "memoTitle" + ], + "properties": { + "memoTitle": { + "description": "备忘标题", + "type": "string" + }, + "memoContent": { + "description": "备忘内容", + "type": "string" + }, + "remindTime": { + "description": "提醒时间", + "type": "string" + }, + "completed": { + "description": "是否已完成", + "type": "string" + }, + "mediaOssIds": { + "description": "媒体文件OSS ID列表,多个用逗号或数组传递以接口约定为准", + "type": "string" + }, + "sortOrder": { + "description": "排序值,数值越小越靠前", + "type": "integer", + "format": "int64" + }, + "status": { + "description": "状态,建议使用对应字典值", + "type": "string" + } + }, + "example": { + "memoTitle": "修谱事项", + "memoContent": "contact family members", + "remindTime": "2026-08-01 09:00:00", + "completed": "0", + "mediaOssIds": "2060000000000000001", + "sortOrder": 1, + "status": "0" + } + }, + "MeritRecordBody": { + "type": "object", + "required": [ + "donorName", + "meritTitle" + ], + "properties": { + "donorName": { + "description": "功德人/捐赠人姓名", + "type": "string" + }, + "meritType": { + "description": "功德类型", + "type": "string" + }, + "meritTitle": { + "description": "功德标题", + "type": "string" + }, + "meritContent": { + "description": "功德内容", + "type": "string" + }, + "amount": { + "description": "金额", + "type": "number", + "format": "double" + }, + "meritTime": { + "description": "功德时间", + "type": "string" + }, + "sortOrder": { + "description": "排序值,数值越小越靠前", + "type": "integer", + "format": "int64" + }, + "status": { + "description": "状态,建议使用对应字典值", + "type": "string" + } + }, + "example": { + "donorName": "彭小明", + "meritType": "donation", + "meritTitle": "修谱捐款", + "meritContent": "support genealogy work", + "amount": 100, + "meritTime": "2026-07-09 10:00:00", + "sortOrder": 1, + "status": "0" + } + }, + "FeedbackBody": { + "type": "object", + "required": [ + "feedbackContent" + ], + "properties": { + "feedbackType": { + "description": "反馈类型", + "type": "string" + }, + "feedbackContent": { + "description": "反馈内容", + "type": "string" + }, + "contactInfo": { + "description": "联系方式", + "type": "string" + } + }, + "example": { + "feedbackType": "bug", + "feedbackContent": "upload image failed", + "contactInfo": "13800000000" + } + }, + "VipOrderBody": { + "type": "object", + "required": [ + "packageId" + ], + "properties": { + "packageId": { + "description": "VIP套餐ID", + "type": "integer", + "format": "int64" + }, + "genealogyId": { + "description": "家谱ID", + "type": "integer", + "format": "int64" + }, + "payType": { + "description": "支付方式", + "type": "string" + } + }, + "example": { + "packageId": 900060001, + "genealogyId": 900001001, + "payType": "wechat" + } + }, + "VerificationChallengeBody": { + "type": "object", + "required": [ + "sceneCode" + ], + "properties": { + "tenantId": { + "type": "string", + "description": "租户 ID" + }, + "clientId": { + "type": "string", + "description": "客户端 ID" + }, + "sceneCode": { + "type": "string", + "description": "验证场景编码,如 ADMIN_LOGIN/APP_LOGIN/APP_REGISTER" + }, + "subject": { + "type": "string", + "description": "验证主体,如手机号/用户名/IP" + } + }, + "example": { + "tenantId": "000000", + "clientId": "428a8310cd442757ae699df5d894f051", + "sceneCode": "APP_LOGIN", + "subject": "13800000000" + } + }, + "VerificationCheckBody": { + "type": "object", + "required": [ + "sceneCode" + ], + "properties": { + "tenantId": { + "description": "租户ID,用于区分不同租户的数据和验证策略", + "type": "string" + }, + "clientId": { + "description": "客户端ID,对应后台客户端管理中的APP/PC/小程序等终端", + "type": "string" + }, + "sceneCode": { + "description": "验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER", + "type": "string" + }, + "subject": { + "description": "验证主体,通常为手机号、用户ID或登录账号", + "type": "string" + }, + "challengeId": { + "description": "验证挑战ID,由验证挑战接口返回", + "type": "string" + }, + "providerCode": { + "type": "string", + "description": "验证服务,tianai 或 ruoyi_image" + }, + "captchaType": { + "type": "string", + "description": "SLIDER/ROTATE/CONCAT/WORD_IMAGE_CLICK/math/char" + }, + "payload": { + "type": "object", + "additionalProperties": true, + "description": "前端验证控件返回的轨迹/点击/图形验证码数据", + "properties": {} + } + } + }, + "RVoid": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "type": "null" + } + } + }, + "RObject": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "type": "object", + "additionalProperties": true, + "properties": {} + } + } + }, + "RList": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "properties": {} + } + } + } + }, + "PageResult": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "rows": { + "description": "分页数据列表", + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "properties": {} + } + }, + "total": { + "type": "integer", + "example": 0 + } + } + }, + "RLogin": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/LoginVo" + } + } + }, + "LoginVo": { + "type": "object", + "properties": { + "token": { + "type": "string" + }, + "accessToken": { + "type": "string" + }, + "tokenValue": { + "type": "string" + }, + "userId": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "description": "租户ID,用于区分不同租户的数据和验证策略", + "type": "string" + }, + "clientId": { + "description": "客户端ID,对应后台客户端管理中的APP/PC/小程序等终端", + "type": "string" + }, + "clientKey": { + "type": "string" + } + } + }, + "RFileUpload": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/FileUploadVo" + } + } + }, + "FileUploadVo": { + "type": "object", + "properties": { + "ossId": { + "description": "OSS文件ID,表单内部使用,用户侧应通过上传组件获取", + "type": "integer", + "format": "int64" + }, + "url": { + "type": "string" + }, + "thumbnailUrl": { + "type": "string", + "nullable": true + }, + "fileName": { + "description": "原始文件名", + "type": "string" + }, + "originalName": { + "type": "string" + } + } + }, + "RVerificationRequire": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/VerificationRequireVo" + } + } + }, + "VerificationRequireVo": { + "type": "object", + "properties": { + "required": { + "description": "当前场景是否需要验证", + "type": "boolean" + }, + "providerCode": { + "description": "第三方服务商编码", + "type": "string" + }, + "captchaType": { + "description": "验证码类型,例如SLIDER、ROTATE、CONCAT、WORD_IMAGE_CLICK", + "type": "string" + }, + "sceneCode": { + "description": "验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER", + "type": "string" + }, + "ttlSeconds": { + "description": "验证或票据有效秒数", + "type": "integer" + } + }, + "example": { + "required": true, + "providerCode": "tianai", + "captchaType": "SLIDER", + "sceneCode": "APP_LOGIN", + "ttlSeconds": 300 + } + }, + "RVerificationChallenge": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/VerificationChallengeVo" + } + } + }, + "VerificationChallengeVo": { + "type": "object", + "properties": { + "required": { + "description": "当前场景是否需要验证", + "type": "boolean" + }, + "providerCode": { + "description": "第三方服务商编码", + "type": "string" + }, + "captchaType": { + "description": "验证码类型,例如SLIDER、ROTATE、CONCAT、WORD_IMAGE_CLICK", + "type": "string" + }, + "challengeId": { + "description": "验证挑战ID,由验证挑战接口返回", + "type": "string" + }, + "uuid": { + "type": "string", + "description": "系统图形验证码 uuid" + }, + "img": { + "type": "string", + "description": "系统图形验证码 base64 图片" + }, + "payload": { + "type": "object", + "additionalProperties": true, + "description": "天爱验证码数据,包括背景图、模板图、尺寸等", + "properties": {} + }, + "expireSeconds": { + "description": "验证挑战过期秒数", + "type": "integer" + } + }, + "example": { + "required": true, + "providerCode": "tianai", + "captchaType": "SLIDER", + "challengeId": "CAPTCHA_CHALLENGE_ID", + "payload": { + "backgroundImage": "data:image/png;base64,...", + "templateImage": "data:image/png;base64,...", + "backgroundImageWidth": 340, + "backgroundImageHeight": 180 + }, + "expireSeconds": 300 + } + }, + "RVerificationCheck": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/VerificationCheckVo" + } + } + }, + "VerificationCheckVo": { + "type": "object", + "properties": { + "passed": { + "description": "验证是否通过", + "type": "boolean" + }, + "validToken": { + "type": "string", + "description": "验证通过后交给业务接口的票据" + }, + "expireSeconds": { + "description": "验证挑战过期秒数", + "type": "integer" + }, + "message": { + "description": "返回消息", + "type": "string" + } + }, + "example": { + "passed": true, + "validToken": "captcha-ticket", + "expireSeconds": 300, + "message": "验证通过" + } + }, + "RLegacyCaptcha": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/LegacyCaptchaVo" + } + } + }, + "LegacyCaptchaVo": { + "type": "object", + "properties": { + "captchaEnabled": { + "type": "boolean" + }, + "uuid": { + "description": "系统图形验证码UUID", + "type": "string" + }, + "img": { + "type": "string", + "description": "base64 图形验证码" + } + }, + "example": { + "captchaEnabled": true, + "uuid": "captcha-uuid", + "img": "data:image/png;base64,..." + } + } + }, + "responses": { + "VoidResult": { + "description": "通用成功响应", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RVoid" + } + } + } + }, + "ObjectResult": { + "description": "通用对象响应", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RObject" + } + } + } + }, + "ListResult": { + "description": "通用列表响应", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RList" + } + } + } + }, + "PageResult": { + "description": "通用分页响应", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PageResult" + } + } + } + }, + "LoginResult": { + "description": "登录结果", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RLogin" + } + } + } + }, + "FileUploadResult": { + "description": "文件上传结果", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RFileUpload" + } + } + } + }, + "VerificationRequireResult": { + "description": "验证需求查询结果", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RVerificationRequire" + } + } + } + }, + "VerificationChallengeResult": { + "description": "验证挑战生成结果", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RVerificationChallenge" + } + } + } + }, + "VerificationCheckResult": { + "description": "验证校验结果", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RVerificationCheck" + } + } + } + }, + "LegacyCaptchaResult": { + "description": "兼容旧图形验证码结果", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RLegacyCaptcha" + } + } + } + } + }, + "securitySchemes": { + "SaToken": { + "type": "apiKey", + "in": "header", + "name": "Authorization", + "description": "登录后返回的 token。若本地配置使用 sa-token 名称,请按项目实际 token-name 调整。" + } + } + }, + "servers": [], + "security": [ + { + "SaToken": [] + } + ] +} \ No newline at end of file diff --git a/about.html b/about.html new file mode 100644 index 0000000..4af0ec9 --- /dev/null +++ b/about.html @@ -0,0 +1,115 @@ + + + + + + 关于我们 - 代代相传 + + + + + + +
+
+
+
+
About Us
+

用数字化方式守护家族记忆

+

+ 代代相传面向家庭、宗亲会与地方文化组织,提供家谱创建、资料管理、文化展示和亲人协作工具。 +

+
+ 数字家谱亲人协作文化传承 +
+
+
+
+ +

记录家族,连接亲人,传承文化。

+
+
+
+ +
+
+
+

我们重视

+

让家族资料真实、清晰、可协作,也能长期传下去。

+
+
+
+
+

真实

+

尊重家族资料来源,鼓励记录出处与修订过程。

+
+
+
+

协作

+

让更多亲人参与补充资料,形成长期维护机制。

+
+
+
+

传承

+

把家族文化整理成可持续保存的数字档案。

+
+
+
+
+
+ + + + + diff --git a/app.html b/app.html new file mode 100644 index 0000000..acfcdeb --- /dev/null +++ b/app.html @@ -0,0 +1,150 @@ + + + + + + 应用下载 - 代代相传 + + + + + + +
+
+
+
+
Mobile App
+

手机端随时记录,电脑端清晰浏览

+

+ 家族成员可以在手机端上传照片、查看家谱、发布动态;PC + 端适合浏览门户内容和公开家谱。 +

+ +
+ 相册上传视频发布家谱查看 +
+
+
+
+ APP首页截图 + 家谱主页截图 +
+
+
+ +
+
+
+

移动端能力

+

+ 承接 APP 里的主要功能,让用户理解手机端适合日常记录和共同维护。 +

+
+
+
+
+

创建家谱

+

手机上快速创建或加入家族空间。

+
+
+
+

宣传相册

+

随手上传老照片、祖屋照片和聚会照片。

+
+
+
+

宣传视频

+

发布家族活动与人物影像。

+
+
+
+

谱文详情

+

阅读谱序、传记和家族文章。

+
+
+
+
+ +
+
+
+

应用推广

+

展示后台维护的应用推广、公告和下载引导内容。

+
+
+
应用推广内容加载中
+
+
+
+
+ + + + + + + diff --git a/article-detail.html b/article-detail.html new file mode 100644 index 0000000..7ea601b --- /dev/null +++ b/article-detail.html @@ -0,0 +1,109 @@ + + + + + + 为什么家谱不只是一本名册 - 文章详情 - 代代相传 + + + + + + +
+
+
+
+
Article Detail
+

为什么家谱不只是一本名册

+

+ 家谱记录的是家族关系,也记录一个家庭如何理解自己的来处、秩序和传承。 +

+ +
+
+ +

记录来处,也照见未来。

+
+
+
+ +
+
+
+

谱文详情加载中...

+
+ +
+
+
+ + + + + + + diff --git a/assets/ancestral-hall-new.png b/assets/ancestral-hall-new.png new file mode 100644 index 0000000..beee181 Binary files /dev/null and b/assets/ancestral-hall-new.png differ diff --git a/assets/ancestral-hall.png b/assets/ancestral-hall.png new file mode 100644 index 0000000..beee181 Binary files /dev/null and b/assets/ancestral-hall.png differ diff --git a/assets/ancestral-hall.svg b/assets/ancestral-hall.svg new file mode 100644 index 0000000..857066f --- /dev/null +++ b/assets/ancestral-hall.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config.js b/config.js new file mode 100644 index 0000000..885fcee --- /dev/null +++ b/config.js @@ -0,0 +1,77 @@ +(function (root, factory) { + // 全局配置同时支持浏览器页面和 Node 测试。 + if (typeof module === 'object' && module.exports) { + module.exports = factory; + return; + } + + root.GenealogyConfig = factory(root); +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var DEFAULT_ENV = 'development'; + var API_BASE_URLS = { + development: 'http://test-genealogy-api.ddxcjp.cn', + production: '' + }; + var TEST_API_HTTP_URL = 'http://test-genealogy-api.ddxcjp.cn'; + var TEST_API_HTTPS_URL = 'https://test-genealogy-api.ddxcjp.cn'; + var CLIENT_ID = 'ced7e5f0498645c6ec642dcf450b036f'; + var TENANT_ID = '000000'; + var TOKEN_KEY = 'genealogy_auth_token'; + var TOKEN_HEADER_NAME = 'Authorization'; + var ENV_KEY = 'genealogy_env'; + var BASE_URL_KEY = 'genealogy_api_base_url'; + + function readStorage(key) { + try { + return root.localStorage && root.localStorage.getItem(key); + } catch (error) { + return null; + } + } + + function getEnv() { + return readStorage(ENV_KEY) || DEFAULT_ENV; + } + + function normalizeApiBaseUrl(value) { + // 测试域名当前 HTTPS 握手失败,开发环境统一走可访问的 HTTP 地址。 + var url = String(value || '').replace(/\/+$/, ''); + + if (url === TEST_API_HTTPS_URL) return TEST_API_HTTP_URL; + return url; + } + + function getApiBaseUrl() { + return normalizeApiBaseUrl(readStorage(BASE_URL_KEY) || API_BASE_URLS[getEnv()] || API_BASE_URLS.development); + } + + function getConfig() { + return { + env: getEnv(), + apiBaseUrl: getApiBaseUrl(), + clientId: CLIENT_ID, + tenantId: TENANT_ID, + tokenKey: TOKEN_KEY, + tokenHeaderName: TOKEN_HEADER_NAME + }; + } + + return { + getConfig: getConfig, + getApiBaseUrl: getApiBaseUrl, + getClientId: function () { + return CLIENT_ID; + }, + getTenantId: function () { + return TENANT_ID; + }, + getTokenKey: function () { + return TOKEN_KEY; + }, + getTokenHeaderName: function () { + return TOKEN_HEADER_NAME; + } + }; +}); diff --git a/create-genealogy.html b/create-genealogy.html new file mode 100644 index 0000000..6de63f4 --- /dev/null +++ b/create-genealogy.html @@ -0,0 +1,136 @@ + + + + + + 创建家谱 - 代代相传 + + + + + + +
+
+
+
+
Start Genealogy
+

创建属于家族的数字空间

+

+ 填写姓氏谱名、地区与简介,先建立家谱主页,再逐步邀请亲人共同完善资料。 +

+
+ 基础资料成员世系亲人共修 +
+
+
+
+

创建流程

+
+

1填写基础信息

+

2添加第一批成员

+

3邀请亲人共修

+
+
+
+
+ +
+
+
+
+

家谱信息

+

+ 先填写基本资料,后续可以继续补充成员、相册和谱文。 +

+
+ + + +
+ + + + +
+ + + +
+
+
+
+
+ + + + + + + + + + + + diff --git a/culture.html b/culture.html new file mode 100644 index 0000000..f59af31 --- /dev/null +++ b/culture.html @@ -0,0 +1,128 @@ + + + + + + 家族文化 - 代代相传 + + + + + + +
+
+
+
+
Family Culture
+

让家风、族训与故事被认真保存

+

+ 家族文化承接新闻、知识、公告和故事,让谱文之外的记忆也能被阅读、分享和传承。 +

+
+ 家风家训谱文故事使用指南 +
+
+
+
+ 家风 +

+ 一张老照片、一段口述、一篇谱序,都可以成为后人理解家族的入口。 +

+
+
+
+ +
+ +
+
+ + + + + diff --git a/docs/api-page-integration-2026-07-09.md b/docs/api-page-integration-2026-07-09.md new file mode 100644 index 0000000..8c0fc24 --- /dev/null +++ b/docs/api-page-integration-2026-07-09.md @@ -0,0 +1,102 @@ +# 接口对接规划与进度记录 + +## 基本信息 + +- 项目路径:`C:\Users\Administrator\Desktop\job\家谱` +- 接口文档:`APP.openapi.json` +- 记录创建时间:2026-07-09 11:51:16 +08:00 +- 执行范围:先做核心闭环页面接口对接,再按页面模块继续扩展。 +- 当前假设:接口文档以 `/genealogy/app` 为主,PC/H5 页面先复用这些用户侧接口。 + +## 约束摘要 + +- 先规划再实现,所有执行进度需要记录年月日时间。 +- 改动要小,只触碰接口对接必需文件。 +- 新接口契约由共享 API 客户端统一维护,页面脚本只调用封装方法。 +- 完成后做最小可验证检查;不能验证的地方要明确说明。 +- 样式尽量写入 `public.css` 或页面对应 CSS 文件,通过 class 控制,不用 JS 注入样式。 +- 新增 HTML、JS、CSS 注释统一使用中文。 +- CSS 结构先公共后页面:`public.css` 放全站公共组件,页面 CSS 只放本页面差异样式。 +- 个人中心模块页优先复用 `profile-module.css`,只有页面独有样式再新增或修改对应页面 CSS。 +- 页面专用 JS 放 `public/js`,命名跟页面或模块对应;公告详情页如需 JS 使用 `public/js/notice-detail.js`。 +- 业务代码不压缩,保持展开、可读;需要解释意图、边界或接口字段映射的位置使用中文注释。 +- 第三方库、压缩库、已存在乱码文件不做无关重写,避免引入额外风险。 + +## 核心闭环范围 + +1. 共享接口层 + - 新增统一 API 客户端,负责 `baseUrl`、`clientid`、`tenantId`、token 存取、请求头、响应解包和错误提示。 + - 默认 `clientid` 使用接口文档示例:`ced7e5f0498645c6ec642dcf450b036f`。 + - 默认 `tenantId` 使用接口文档示例:`000000`。 + - API 基础地址从 `window.GENEALOGY_API_BASE_URL` 或 `localStorage.genealogy_api_base_url` 读取,未配置时使用同域相对路径。 + - 浏览器端公共脚本放 `public/js`;`utils` 目前只适合作为纯工具类目录,不能作为页面脚本入口。 + - 页面专用 JS 按页面或模块放 `public/js`,例如认证模块用 `auth-pages.js`,公告详情页如需脚本则用 `notice-detail.js`。 + +2. 登录注册找回 + - `login.html` -> `POST /genealogy/app/auth/login` + - `register.html` -> `POST /genealogy/app/auth/register` + - `forgot-password.html` -> `POST /genealogy/app/auth/sms/code`,`PUT /genealogy/app/auth/password/reset` + - 密码按文档要求提交 32 位 MD5,复用已有 `public/js/md5.js`。 + +3. 家谱核心 + - `create-genealogy.html` -> `POST /genealogy/app/genealogies` + - `profile-families.html` -> `GET /genealogy/app/genealogies/mine` + - `plaza.html` -> `GET /genealogy/app/genealogies/public` + - `family-detail.html` -> `GET /genealogy/app/genealogies/{genealogyId}` + +4. 个人资料与反馈 + - `profile.html`、`profile-data.html` -> `GET /genealogy/app/auth/profile` + - `profile-feedback.html`、`submit-ticket.html` -> `GET/POST /genealogy/app/feedback` + +5. 拦截处理 + - `public/js/page-effects.js` 当前会拦截登录、注册、创建家谱、个人中心页面并显示“功能待开发”。 + - 对已接入接口的页面移除该拦截,保留未接入页面的提示。 + +## 验证计划 + +1. 新增共享 API 客户端测试,先确认测试失败,再实现通过。 +2. 检查核心页面均加载共享脚本和页面脚本。 +3. 用静态结构检查确认接口路径、表单字段、脚本引用存在。 +4. 运行语法检查或 Node 烟测,确保 JS 可加载、核心方法可调用。 +5. 检查新增样式是否位于 CSS 文件,不通过 JS 注入。 + +## 进度日志 + +- 2026-07-09 11:47:20 +08:00:已读取 `AGENTS.md`、项目目录、`APP.openapi.json`,确认项目是静态 HTML + jQuery/Layui,接口文档主要为 `/genealogy/app`。 +- 2026-07-09 11:51:16 +08:00:用户确认按核心闭环方案执行,创建本规划与进度记录。 +- 2026-07-09 11:52:53 +08:00:新增 `tests/api-client.test.js`,先运行 `node tests\api-client.test.js`,确认失败原因为缺少 `public/js/api-client.js`,进入共享 API 客户端实现。 +- 2026-07-09 11:54:00 +08:00:用户补充要求:尽量不用 JS 注入样式,样式写在公共或页面 CSS 中并通过 class 查找;新增 HTML/JS/CSS 注释使用中文。 +- 2026-07-09 11:57:19 +08:00:复查项目结构,确认 HTML 当前统一引用 `public/js`,`utils/DateUtil.js` 未被页面引用且存在乱码/语法风险;撤回过宽的 `api-pages.js` 方案,改为共享 `api-client.js` + 按模块/页面拆分脚本。 +- 2026-07-09 11:59:22 +08:00:用户补充 CSS 架构要求:抽出可复用公共样式,每个页面 CSS 只写差异样式;将 CSS 结构整理加入接口接入前置约束。 +- 2026-07-09 12:06:46 +08:00:完成认证模块第一步:新增 `public/js/auth-pages.js` 和 `tests/auth-pages.test.js`;`login.html`、`register.html`、`forgot-password.html` 改为真实表单;认证页公共 CSS 抽到 `public.css`,页面 CSS 只保留差异;重写 `page-effects.js`,移除“功能待开发”拦截和 JS 注入样式。 +- 2026-07-09 12:10:54 +08:00:完成家谱核心第一步:新增 `public/js/genealogy-pages.js` 和 `tests/genealogy-pages.test.js`;`create-genealogy.html` 接入创建家谱接口;`plaza.html` 接入公开家谱列表;`profile-families.html` 接入我的家谱列表。验证:`node tests\api-client.test.js`、`node tests\auth-pages.test.js`、`node tests\genealogy-pages.test.js` 均通过;`node --check` 检查新增业务 JS 均通过;新增业务 JS 未发现 `createElement("style")` 或 `style.textContent` 注入样式。 +- 2026-07-09 12:13:36 +08:00:用户补充可读性要求:现有该注释的位置补中文注释,代码不要压缩。执行范围限定为业务代码和本次维护的 CSS,第三方压缩库不改。 +- 2026-07-09 12:16:27 +08:00:已给本次新增/重写业务代码补中文注释,覆盖 `api-client.js`、`auth-pages.js`、`genealogy-pages.js`、`page-effects.js` 和 `public.css` 的关键逻辑/样式区块;代码保持展开可读。验证:三组 Node 测试通过,四个业务 JS `node --check` 通过,业务 JS 未发现样式注入写法。 +- 2026-07-09 12:23:36 +08:00:开始下一阶段:个人资料读取展示与反馈提交/历史反馈列表。范围:`profile.html`、`profile-data.html`、`profile-feedback.html`、`submit-ticket.html`;先写测试,再实现模块脚本和页面接入。 +- 2026-07-09 12:28:30 +08:00:完成个人资料与反馈阶段:新增 `public/js/profile-pages.js`、`public/js/feedback-pages.js`、`tests/profile-pages.test.js`、`tests/feedback-pages.test.js`;`profile.html`、`profile-data.html` 接入当前用户资料读取展示;`profile-feedback.html`、`submit-ticket.html` 接入反馈提交,`profile-feedback.html` 接入历史反馈列表。验证:五组 Node 测试通过,六个业务 JS `node --check` 通过,业务 JS 未发现样式注入写法。 +- 2026-07-09 12:31:51 +08:00:按用户要求重新验证第一阶段和第二阶段。第一阶段 API 客户端、认证页、家谱核心页测试均重新通过;第二阶段个人资料、反馈模块测试重新通过。复验命令包括 `node tests\api-client.test.js`、`node tests\auth-pages.test.js`、`node tests\genealogy-pages.test.js`、`node tests\profile-pages.test.js`、`node tests\feedback-pages.test.js`,以及六个业务脚本的 `node --check`。同时重新检查页面脚本引用、`data-*` 挂载点、业务 JS 样式注入写法和 `TODO/TBD/待实现/压缩` 标记,未发现异常。 +- 2026-07-09 12:38:13 +08:00:继续推进家谱详情与加入申请阶段。`family-detail.html` 接入 `GET /genealogy/app/genealogies/{genealogyId}`,通过 `data-family-*` 挂载标题、简介、统计和家谱信息,并把“申请加入”链接带上家谱 ID;`join-genealogy.html` 改为真实申请表单,接入 `POST /genealogy/app/genealogies/{genealogyId}/join-applies`。新增/更新 `api-client.js` 和 `genealogy-pages.js` 中的申请加入、详情渲染、表单提交逻辑,并在对应 CSS 中补中文注释和页面差异样式。验证:五组 Node 测试全部通过,六个业务 JS `node --check` 全部通过,页面脚本引用与 `data-*` 挂载点检查通过,业务 JS 未发现样式注入写法,核心文件未发现 `TODO/TBD/待实现/压缩` 标记。 +- 2026-07-09 12:40:15 +08:00:根据接口 schema 补充 `joinMode` 枚举中文展示映射:`0` 显示为“关闭加入”、`1` 显示为“审核加入”、`2` 显示为“邀请码加入”,避免详情页直接显示数字。重新验证五组 Node 测试、六个业务 JS 语法检查、详情页/加入页挂载点、业务 JS 样式注入检查和核心文件遗留标记检查,结果均正常。 +- 2026-07-09 12:45:24 +08:00:继续推进消息中心阶段。`profile-messages.html` 接入 `GET /genealogy/app/notifications`、`POST /genealogy/app/notifications/{notificationId}/read`、`POST /genealogy/app/notifications/read-all`,新增 `public/js/notification-pages.js` 负责消息列表渲染、单条已读、全部已读和刷新;`api-client.js` 新增通知相关封装;`profile-module.css` 增加按钮型模块卡片和未读消息样式,保持样式写入 CSS。验证:六组 Node 测试全部通过,七个业务 JS `node --check` 全部通过,消息页脚本引用和 `data-*` 挂载点检查通过,业务 JS 未发现样式注入写法,核心文件未发现 `TODO/TBD/待实现/压缩` 标记。 +- 2026-07-09 12:59:39 +08:00:按用户要求接入 `public/tac` 滑动验证到 `login.html`、`register.html`、`forgot-password.html`。依据 `APP.openapi.json` 的 `/captcha/require`、`/captcha/challenge`、`/captcha/verify` 字段实现 `public/js/captcha-pages.js` 适配层,将接口返回的 `captchaType/challengeId/payload` 转为 TAC 需要的 `type/id/backgroundImage/templateImage`,验证成功后把 `data.validToken` 写入当前表单隐藏字段;`auth-pages.js` 在登录、注册、发送短信验证码、重置密码前先执行滑动验证;`register.html` 原可见 `validToken` 输入改为昵称输入,避免和滑动验证隐藏字段冲突。三张认证页均引入 `public/tac/css/tac.css`、`public/tac/js/tac.min.js`、`public/js/captcha-pages.js`,并增加 `data-captcha-box` 挂载容器;`public.css` 增加认证页验证码容器样式。验证:七组 Node 测试全部通过,八个业务 JS `node --check` 全部通过,三张认证页 TAC 资源、隐藏 `validToken`、`data-captcha-box` 检查通过,自有业务 JS 未发现样式注入写法,核心文件未发现 `TODO/TBD/待实现/压缩` 标记;`git diff --check` 因当前目录不是 git 仓库无法执行。 +- 2026-07-09 13:07:18 +08:00:按用户提醒重新以 `APP.openapi.json` 为依据梳理剩余 `/genealogy/app` 页面归属,优先推进接口和页面最匹配的帮助中心。`help.html` 接入 `GET /genealogy/app/help-articles` 帮助文章列表,并在展开条目时按需调用 `GET /genealogy/app/help-articles/{helpId}` 补充详情;新增 `public/js/help-pages.js`,`api-client.js` 新增 `helpArticles`、`helpArticleDetail` 封装;`help.css` 补帮助列表加载状态注释和样式。验证:八组 Node 测试全部通过,九个业务 JS `node --check` 全部通过,帮助页 `data-help-list`、`api-client.js`、`help-pages.js` 引用检查通过,自有业务 JS 未发现样式注入写法,核心文件未发现 `TODO/TBD/待实现/压缩` 标记。 +- 2026-07-09 13:13:33 +08:00:继续按 `APP.openapi.json` 推进加入申请阶段。`profile-join-review.html` 接入 `GET /genealogy/app/genealogies/{genealogyId}/join-applies/pending` 待审核申请和 `PUT /genealogy/app/genealogies/{genealogyId}/join-applies/{applyId}/audit` 审核接口;`profile-join-family.html` 接入 `GET /genealogy/app/genealogies/join-applies/mine` 我的加入申请,并支持 `DELETE /genealogy/app/genealogies/join-applies/{applyId}` 撤销申请。新增 `public/js/join-apply-pages.js`,`api-client.js` 新增 `myJoinApplies`、`pendingJoinApplies`、`auditJoinApply`、`cancelJoinApply` 封装;`profile-module.css` 补加入申请行样式。验证:九组 Node 测试全部通过,十个业务 JS `node --check` 全部通过,两张页面脚本引用与 `data-join-apply-list` 挂载点检查通过,自有业务 JS 未发现样式注入写法,核心文件未发现 `TODO/TBD/待实现/压缩` 标记。 +- 2026-07-09 13:19:07 +08:00:继续按 `APP.openapi.json` 推进字辈谱阶段。`profile-generation.html` 接入 `GET /genealogy/app/genealogies/{genealogyId}/generation-poems` 查询字辈谱、`POST /genealogy/app/genealogies/{genealogyId}/generation-poems` 新增字辈、`PUT /genealogy/app/genealogies/{genealogyId}/generation-poems/{poemId}` 修改字辈;新增 `public/js/generation-pages.js`,`api-client.js` 新增 `generationPoems`、`createGenerationPoem`、`updateGenerationPoem` 封装;`profile-module.css` 补字辈谱行样式。本轮暂不接批量预览/批量保存接口,避免没有页面入口时过度实现。验证:十组 Node 测试全部通过,十一个业务 JS `node --check` 全部通过,字辈页 `data-generation-list`、`data-generation-add`、`generation-pages.js` 引用检查通过,自有业务 JS 未发现样式注入写法,核心文件未发现 `TODO/TBD/待实现/压缩` 标记。 +- 2026-07-09 13:21:31 +08:00:根据用户提醒停止无编号推进,新增 `docs/api-page-integration-tracker-2026-07-09.md` 作为后续唯一编号规划与状态追踪表。后续每个模块必须先在追踪表中标记“进行中”,完成并验证后再标记“已完成”。 +- 2026-07-09 13:33:41 +08:00:完成 API-011 世系树与人物管理。`profile-tree.html` 接入世系树、人物分页搜索、人物详情、新增成员和父母/配偶/子女快捷关系添加;`profile-family-home.html` 接入世系树预览和家谱主页摘要;新增 `public/js/lineage-pages.js`,`api-client.js` 新增 `lineageTree`、`lineagePersons`、`lineagePersonsPage`、`lineagePersonOptions`、`lineagePersonDetail`、`createLineagePerson`、`updateLineagePerson`、`deleteLineagePerson`、`addLineageRelation` 封装;`profile-module.css` 补世系列表、搜索栏和树结构样式。验证:全部 Node 测试通过,十二个业务 JS `node --check` 通过,两张页面脚本引用与 `data-*` 挂载点检查通过,`public/js/api-client.js` 和 `public/js/lineage-pages.js` 未发现 `style` 样式注入片段。 +- 2026-07-09 13:40:47 +08:00:完成 API-012 成员与家族管理。`profile-family-admin.html` 接入家谱概览和成员列表,支持成员权限更新和移除;`profile-admin-permissions.html` 接入成员选项、角色保存和所有者转让;`profile-invite.html`、`profile-share.html` 使用家谱概览生成邀请码/分享链接展示,不伪造接口文档中不存在的邀请生成接口。新增 `public/js/member-admin-pages.js`,`api-client.js` 新增 `genealogyOverview`、`genealogyMembers`、`genealogyMemberOptions`、`updateGenealogyMember`、`removeGenealogyMember`、`leaveGenealogy`、`transferGenealogyOwner` 封装;`profile-module.css` 补成员操作行样式。验证:全部 Node 测试通过,十三个业务 JS `node --check` 通过,四张页面脚本引用与 `data-*` 挂载点检查通过,`public/js/api-client.js` 和 `public/js/member-admin-pages.js` 未发现 `style` 样式注入片段。 +- 2026-07-09 13:46:31 +08:00:完成 API-014 谱文与内容文章。`profile-article.html` 接入谱文列表;`profile-article-edit.html` 接入分类列表、谱文新增和修改;`article-detail.html` 接入谱文详情;`profile-content.html` 的最近内容先接谱文列表,其他内容类型按后续编号继续接。新增 `public/js/article-pages.js`,`api-client.js` 新增 `articleCategories`、`articles`、`articleDetail`、`createArticle`、`updateArticle` 封装;`profile-module.css` 补谱文行样式。验证:全部 Node 测试通过,十四个业务 JS `node --check` 通过,四张页面脚本引用与 `data-*` 挂载点检查通过,`public/js/api-client.js` 和 `public/js/article-pages.js` 未发现 `style` 样式注入片段。 +- 2026-07-09 13:52:03 +08:00:完成 API-013 家族圈动态。`profile-feed.html` 接入动态分页列表、点赞和评论;`profile-feed-edit.html` 接入动态发布和修改。新增 `public/js/feed-pages.js`,`api-client.js` 新增 `feeds`、`feedsPage`、`feedDetail`、`createFeed`、`updateFeed`、`likeFeed`、`unlikeFeed`、`feedComments`、`feedCommentsPage`、`createFeedComment`、`deleteFeedComment` 封装;`profile-module.css` 补动态行样式。验证:全部 Node 测试通过,十五个业务 JS `node --check` 通过,两张页面脚本引用与 `data-*` 挂载点检查通过,`public/js/api-client.js` 和 `public/js/feed-pages.js` 未发现 `style` 样式注入片段。 +- 2026-07-09 13:57:31 +08:00:完成 API-015 相册照片。`profile-album.html` 接入相册列表、新增/修改相册、照片列表和新增照片;`promo-album.html` 复用相册列表做宣传相册展示。新增 `public/js/album-pages.js`,`api-client.js` 新增 `albums`、`createAlbum`、`updateAlbum`、`albumPhotos`、`createAlbumPhoto` 封装;`profile-module.css` 补相册和照片行样式。验证:全部 Node 测试通过,十六个业务 JS `node --check` 通过,两张页面脚本引用与 `data-*` 挂载点检查通过,`public/js/api-client.js` 和 `public/js/album-pages.js` 未发现 `style` 样式注入片段。 +- 2026-07-09 14:04:52 +08:00:完成 API-019 文件上传公共能力。`api-client.js` 接入单文件上传、文件引用绑定/释放、分片初始化、分片上传、分片完成接口;新增 `public/js/upload-pages.js` 统一处理文件选择、上传、OSS ID 回填和上传状态展示;`profile-article-edit.html` 接入谱文封面上传,`profile-feed-edit.html` 接入动态图片上传,`profile-album.html` 接入相册封面和照片上传;`profile-module.css` 补上传控件样式。验证:全部 Node 测试通过,十七个业务 JS `node --check` 通过,上传控件挂载点检查通过,`public/js/api-client.js` 和 `public/js/upload-pages.js` 未发现样式注入片段。 +- 2026-07-09 14:12:27 +08:00:完成 API-016 祭祀、献礼与功德记录。`profile-gift.html` 接入祭祀/贺礼列表、献礼列表和新增献礼;`profile-gift-edit.html` 接入贺礼创建/修改和封面上传;`profile-merit.html` 接入功德记录列表;`profile-merit-edit.html` 接入新增功德记录。新增 `public/js/ceremony-pages.js`,`api-client.js` 新增 `ceremonies`、`ceremonyDetail`、`createCeremony`、`updateCeremony`、`ceremonyGifts`、`createCeremonyGift`、`meritRecords`、`createMeritRecord` 封装;`profile-module.css` 补贺礼、献礼和功德记录行样式。验证:全部 Node 测试通过,十八个业务 JS `node --check` 通过,四张页面脚本引用与 `data-*` 挂载点检查通过,`public/js/api-client.js` 和 `public/js/ceremony-pages.js` 未发现样式注入片段。 +- 2026-07-09 14:18:15 +08:00:完成 API-017 成长记录。`profile-growth.html` 接入成长记录列表;`profile-growth-edit.html` 接入成长记录新增/修改和附件上传。新增 `public/js/growth-pages.js`,`api-client.js` 新增 `growthRecords`、`growthRecordDetail`、`createGrowthRecord`、`updateGrowthRecord` 封装;`profile-module.css` 补成长记录行样式。验证:全部 Node 测试通过,十九个业务 JS `node --check` 通过,两张页面脚本引用与 `data-*` 挂载点检查通过,`public/js/api-client.js` 和 `public/js/growth-pages.js` 未发现样式注入片段。 +- 2026-07-09 14:24:10 +08:00:完成 API-018 备忘录。`profile-memo.html` 接入备忘录列表;`profile-memo-edit.html` 接入备忘录新增/修改和附件上传。新增 `public/js/memo-pages.js`,`api-client.js` 新增 `memos`、`memoDetail`、`createMemo`、`updateMemo` 封装;`profile-module.css` 补备忘录行样式。验证:全部 Node 测试通过,二十个业务 JS `node --check` 通过,两张页面脚本引用与 `data-*` 挂载点检查通过,`public/js/api-client.js` 和 `public/js/memo-pages.js` 未发现样式注入片段。 +- 2026-07-09 14:31:29 +08:00:完成 API-020 会员套餐与订单创建。`profile-services.html` 接入会员套餐列表、会员订单创建表单和会员订单列表;新增 `public/js/vip-pages.js`,`api-client.js` 新增 `vipPackages`、`vipOrders`、`createVipOrder` 封装,`profile-module.css` 补会员套餐卡片和订单行样式。验证:`node tests\vip-pages.test.js`、`node tests\api-client.test.js`、全部 `tests/*.test.js`、业务 JS `node --check`、会员页挂载点/字段检查均通过;API-020 自有 JS 未发现样式注入片段;`git diff --check` 因当前目录不是 Git 仓库无法执行。 +- 2026-07-09 14:37:25 +08:00:完成 API-021 推广/公告类内容。`app.html` 接入应用推广列表,`download.html` 接入下载推广入口,`notice-detail.html` 从 `/genealogy/app/promotions` 列表按 `id` 选择公告详情,未伪造接口文档不存在的详情接口。新增 `public/js/promotion-pages.js`,`api-client.js` 新增 `promotions` 封装,`app.css` 补推广卡片样式。验证:`node tests\promotion-pages.test.js`、`node tests\api-client.test.js`、全部 `tests/*.test.js`、业务 JS `node --check`、三张页面推广挂载点检查均通过;API-021 自有 JS 未发现样式注入片段;`git diff --check` 因当前目录不是 Git 仓库无法执行。 +- 2026-07-09 14:42:47 +08:00:完成 API-022 账号安全。`profile-security.html` 接入修改密码、换绑手机号、短信登录校验、退出登录和账号注销表单;新增 `public/js/security-pages.js`,`api-client.js` 新增 `changePassword`、`changePhone`、`smsLogin`、`logout`、`deactivateAccount` 封装,`profile-module.css` 补安全表单和危险操作样式。验证:`node tests\security-pages.test.js`、`node tests\api-client.test.js`、全部 `tests/*.test.js`、业务 JS `node --check`、账号安全页挂载点检查均通过;API-022 自有 JS 未发现样式注入片段;`git diff --check` 因当前目录不是 Git 仓库无法执行。 +- 2026-07-09 14:49:54 +08:00:完成 API-023 地区选择公共能力。`create-genealogy.html` 接入省/市/区选择器并回填 `regionCode`,`profile-data.html` 接入个人资料省市区更新表单;新增 `public/js/region-pages.js`,`api-client.js` 新增 `regionChildren`、`regionPath`、`regionSearch`、`regionDetail` 封装,`genealogy-pages.js` 去掉 `regionCode=000000` 兜底并改为提交前校验真实地区编码,两个 CSS 文件补地区选择器样式。验证:`node tests\region-pages.test.js`、`node tests\api-client.test.js`、全部 `tests/*.test.js`、业务 JS `node --check`、两张页面地区挂载点检查均通过;API-023 自有 JS 未发现样式注入片段;`git diff --check` 因当前目录不是 Git 仓库无法执行。 +- 2026-07-09 14:50:47 +08:00:复查 API-024/API-025。`surname.html`、`surname-detail.html`、`search-result.html`、`genealogy.html`、`culture.html`、`about.html`、`index.html` 未发现明确同名接口,公开家谱接口也不支持搜索参数,因此标记暂缓;`profile-video.html`、`promo-video.html`、`profile-data-reminders.html` 暂无视频/资料提醒独立接口,标记暂缓;`profile-create-family.html` 可复用创建家谱能力,单独规划为 API-026。 +- 2026-07-09 14:53:50 +08:00:完成 API-026 个人中心创建家谱页。`profile-create-family.html` 改为真实创建家谱表单,接入 `api-client.js`、`region-pages.js`、`genealogy-pages.js`,复用 `/genealogy/app/genealogies` 和地区选择公共能力;`profile-module.css` 补个人中心创建表单样式,`tests/genealogy-pages.test.js` 补真实 `regionCode` 断言。验证:`node tests\genealogy-pages.test.js`、`node tests\region-pages.test.js`、`node tests\api-client.test.js`、全部 `tests/*.test.js`、业务 JS `node --check`、页面挂载点检查均通过;API-026 自有 JS 未发现样式注入片段;`git diff --check` 因当前目录不是 Git 仓库无法执行。 +- 2026-07-09 15:03:27 +08:00:完成 API-027 个人资料编辑提交。`profile-data.html` 增加真实个人资料编辑表单,`profile-pages.js` 增加 `ProfileUpdateBody` 构造、表单填充和 `PUT /genealogy/app/auth/profile` 提交,`profile-module.css` 补本页表单状态样式,`tests/profile-pages.test.js` 和 `tests/api-client.test.js` 补转换与接口契约测试。验证:先观察 `node tests\profile-pages.test.js` 因缺少 `buildProfileUpdateBody` 失败;实现后 `node tests\profile-pages.test.js`、`node tests\api-client.test.js`、全部 `tests/*.test.js`、业务 JS `node --check`、页面挂载点检查均通过,未发现样式注入片段和 TODO 类占位;`git diff --check` 因当前目录不是 Git 仓库无法执行。 +- 2026-07-09 15:11:37 +08:00:完成 API-028 字辈批量预览与批量保存。`profile-generation.html` 增加批量字辈表单和预览区域,`api-client.js` 增加 `previewGenerationPoems`、`saveGenerationPoemsBatch`,`generation-pages.js` 增加 `GenerationPoemBatchBody` 构造、预览渲染和保存动作,`profile-module.css` 补批量面板样式,`tests/generation-pages.test.js` 和 `tests/api-client.test.js` 补批量接口测试。验证:先观察 `node tests\generation-pages.test.js` 因缺少 `buildGenerationBatchBody` 失败、`node tests\api-client.test.js` 因缺少 `previewGenerationPoems` 失败;实现后 `node tests\generation-pages.test.js`、`node tests\api-client.test.js`、全部 `tests/*.test.js`、业务 JS `node --check`、页面挂载点检查均通过,未发现样式注入片段和 TODO 类占位;`git diff --check` 因当前目录不是 Git 仓库无法执行。 diff --git a/docs/api-page-integration-tracker-2026-07-09.md b/docs/api-page-integration-tracker-2026-07-09.md new file mode 100644 index 0000000..3a07127 --- /dev/null +++ b/docs/api-page-integration-tracker-2026-07-09.md @@ -0,0 +1,114 @@ +# 接口对接编号规划与状态追踪 + +创建时间:2026-07-09 13:21:31 +08:00 +流程修正时间:2026-07-09 13:21:31 +08:00 +接口依据:`APP.openapi.json` +主进度记录:`docs/api-page-integration-2026-07-09.md` + +## 执行规则 + +1. 后续不再无编号推进,所有接口对接必须先进入本追踪表。 +2. 每一项开始前,先把状态从“待开始”改为“进行中”,并记录开始时间。 +3. 每一项完成后,必须记录完成时间、修改文件、接口路径、验证命令或静态检查结果。 +4. 每一项验证通过后,才能把状态改为“已完成”。 +5. 若接口文档没有明确对应接口,状态标记为“待确认”或“暂缓”,不强行接入。 +6. 样式继续遵守公共 `public.css` + 页面 CSS 的结构,业务 JS 不注入样式。 +7. 新增或修改的 HTML、JS、CSS 注释使用中文,代码保持可读、不压缩。 + +## 状态说明 + +- 已完成:已按接口文档接入,并完成对应验证。 +- 进行中:当前正在推进,未完成前不能开始下一个无关模块。 +- 待开始:已规划,尚未改代码。 +- 待确认:页面存在,但接口归属需要进一步确认。 +- 暂缓:接口或页面入口不足,暂不实现,避免过度开发。 + +## 已完成回填 + +| 编号 | 状态 | 页面范围 | 接口范围 | 完成记录 | +| --- | --- | --- | --- | --- | +| API-000 | 已完成 | 全项目 | `APP.openapi.json`、`AGENTS.md`、项目结构 | 2026-07-09 11:47:20 +08:00 已读取约束、接口文档和目录结构。 | +| API-001 | 已完成 | 全项目公共请求 | 共享请求客户端、认证头、租户头、Token 存取 | 2026-07-09 11:52:53 +08:00 至 12:16:27 +08:00 完成 `public/js/api-client.js` 与测试。 | +| API-002 | 已完成 | `login.html`、`register.html`、`forgot-password.html` | `/genealogy/app/auth/login`、`/genealogy/app/auth/register`、`/genealogy/app/auth/sms/code`、`/genealogy/app/auth/password/reset`、`/captcha/require`、`/captcha/challenge`、`/captcha/verify` | 2026-07-09 12:06:46 +08:00 完成认证页;2026-07-09 12:59:39 +08:00 完成 TAC 滑动验证接入。 | +| API-003 | 已完成 | `create-genealogy.html`、`plaza.html`、`profile-families.html` | `/genealogy/app/genealogies`、`/genealogy/app/genealogies/public`、`/genealogy/app/genealogies/mine` | 2026-07-09 12:10:54 +08:00 完成创建家谱、公开家谱、我的家谱列表。 | +| API-004 | 已完成 | `family-detail.html`、`join-genealogy.html` | `/genealogy/app/genealogies/{genealogyId}`、`/genealogy/app/genealogies/{genealogyId}/join-applies` | 2026-07-09 12:38:13 +08:00 完成家谱详情与申请加入;2026-07-09 12:40:15 +08:00 补 `joinMode` 展示映射。 | +| API-005 | 已完成 | `profile.html`、`profile-data.html` | `/genealogy/app/auth/profile` | 2026-07-09 12:28:30 +08:00 完成个人资料读取展示。 | +| API-006 | 已完成 | `profile-feedback.html`、`submit-ticket.html` | `/genealogy/app/feedback` | 2026-07-09 12:28:30 +08:00 完成反馈提交与历史反馈。 | +| API-007 | 已完成 | `profile-messages.html` | `/genealogy/app/notifications`、`/genealogy/app/notifications/{notificationId}/read`、`/genealogy/app/notifications/read-all` | 2026-07-09 12:45:24 +08:00 完成消息中心。 | +| API-008 | 已完成 | `help.html` | `/genealogy/app/help-articles`、`/genealogy/app/help-articles/{helpId}` | 2026-07-09 13:07:18 +08:00 完成帮助中心。 | +| API-009 | 已完成 | `profile-join-review.html`、`profile-join-family.html` | `/genealogy/app/genealogies/{genealogyId}/join-applies/pending`、`/genealogy/app/genealogies/{genealogyId}/join-applies/{applyId}/audit`、`/genealogy/app/genealogies/join-applies/mine`、`/genealogy/app/genealogies/join-applies/{applyId}` | 2026-07-09 13:13:33 +08:00 完成入谱申请审核与我的申请。 | +| API-010 | 已完成 | `profile-generation.html` | `/genealogy/app/genealogies/{genealogyId}/generation-poems`、`/genealogy/app/genealogies/{genealogyId}/generation-poems/{poemId}` | 2026-07-09 13:19:07 +08:00 完成字辈谱查询、新增、修改;批量接口因页面无入口暂缓。 | + +## 后续编号规划 + +| 编号 | 状态 | 页面范围 | 接口范围 | 计划与验证 | +| --- | --- | --- | --- | --- | +| API-011 | 已完成 | `profile-tree.html`、`profile-family-home.html` | `/genealogy/app/genealogies/{genealogyId}/lineage/tree`、`/genealogy/app/genealogies/{genealogyId}/lineage/persons`、`/genealogy/app/genealogies/{genealogyId}/lineage/persons/page`、`/genealogy/app/genealogies/{genealogyId}/lineage/persons/{personId}`、`/genealogy/app/genealogies/{genealogyId}/lineage/persons/{personId}/children`、`/parents`、`/siblings`、`/spouses` | 2026-07-09 13:25:03 +08:00 开始;2026-07-09 13:33:41 +08:00 完成。新增 `public/js/lineage-pages.js`、`tests/lineage-pages.test.js`;更新 `public/js/api-client.js`、`tests/api-client.test.js`、`profile-tree.html`、`profile-family-home.html`、`public/css/profile-module.css`。验证:全部 Node 测试通过,业务 JS 语法检查通过,页面挂载点检查通过,API-011 自有 JS 未发现样式注入片段。 | +| API-012 | 已完成 | `profile-family-admin.html`、`profile-admin-permissions.html`、`profile-invite.html`、`profile-share.html` | `/genealogy/app/genealogies/{genealogyId}/members`、`/genealogy/app/genealogies/{genealogyId}/members/{memberId}`、`/genealogy/app/genealogies/{genealogyId}/members/me`、`/genealogy/app/genealogies/{genealogyId}/members/options`、`/genealogy/app/genealogies/{genealogyId}/members/owner-transfer`、`/genealogy/app/genealogies/{genealogyId}/overview` | 2026-07-09 13:34:36 +08:00 开始;2026-07-09 13:40:47 +08:00 完成。新增 `public/js/member-admin-pages.js`、`tests/member-admin-pages.test.js`;更新 `public/js/api-client.js`、`tests/api-client.test.js`、四张成员/邀请页面和 `public/css/profile-module.css`。验证:全部 Node 测试通过,业务 JS 语法检查通过,页面挂载点检查通过,API-012 自有 JS 未发现样式注入片段。 | +| API-013 | 已完成 | `profile-feed.html`、`profile-feed-edit.html` | `/genealogy/app/genealogies/{genealogyId}/feeds`、`/genealogy/app/genealogies/{genealogyId}/feeds/page`、`/genealogy/app/genealogies/{genealogyId}/feeds/{feedId}`、`/genealogy/app/genealogies/{genealogyId}/feeds/{feedId}/likes`、评论接口 | 2026-07-09 13:47:29 +08:00 开始;2026-07-09 13:52:03 +08:00 完成。新增 `public/js/feed-pages.js`、`tests/feed-pages.test.js`;更新 `public/js/api-client.js`、`tests/api-client.test.js`、两张动态页面和 `public/css/profile-module.css`。验证:全部 Node 测试通过,业务 JS 语法检查通过,页面挂载点检查通过,API-013 自有 JS 未发现样式注入片段。 | +| API-014 | 已完成 | `profile-article.html`、`profile-article-edit.html`、`article-detail.html`、`profile-content.html` | `/genealogy/app/genealogies/{genealogyId}/article-categories`、`/genealogy/app/genealogies/{genealogyId}/articles`、`/genealogy/app/genealogies/{genealogyId}/articles/{articleId}` | 2026-07-09 13:41:44 +08:00 开始;2026-07-09 13:46:31 +08:00 完成。新增 `public/js/article-pages.js`、`tests/article-pages.test.js`;更新 `public/js/api-client.js`、`tests/api-client.test.js`、四张谱文/内容页面和 `public/css/profile-module.css`。验证:全部 Node 测试通过,业务 JS 语法检查通过,页面挂载点检查通过,API-014 自有 JS 未发现样式注入片段。 | +| API-015 | 已完成 | `profile-album.html`、`promo-album.html` | `/genealogy/app/genealogies/{genealogyId}/albums`、`/genealogy/app/genealogies/{genealogyId}/albums/{albumId}`、`/genealogy/app/genealogies/{genealogyId}/albums/{albumId}/photos` | 2026-07-09 13:52:58 +08:00 开始;2026-07-09 13:57:31 +08:00 完成。新增 `public/js/album-pages.js`、`tests/album-pages.test.js`;更新 `public/js/api-client.js`、`tests/api-client.test.js`、两张相册页面和 `public/css/profile-module.css`。验证:全部 Node 测试通过,业务 JS 语法检查通过,页面挂载点检查通过,API-015 自有 JS 未发现样式注入片段。 | +| API-016 | 已完成 | `profile-gift.html`、`profile-gift-edit.html`、`profile-merit.html`、`profile-merit-edit.html` | `/genealogy/app/genealogies/{genealogyId}/ceremonies`、`/genealogy/app/genealogies/{genealogyId}/ceremonies/{ceremonyId}`、`/genealogy/app/genealogies/{genealogyId}/ceremonies/{ceremonyId}/gifts`、`/genealogy/app/genealogies/{genealogyId}/merit-records` | 2026-07-09 14:05:57 +08:00 开始;2026-07-09 14:12:27 +08:00 完成。新增 `public/js/ceremony-pages.js`、`tests/ceremony-pages.test.js`;更新 `public/js/api-client.js`、`tests/api-client.test.js`、四张贺礼/功德页面和 `public/css/profile-module.css`。验证:全部 Node 测试通过,业务 JS 语法检查通过,页面挂载点检查通过,API-016 自有 JS 未发现样式注入片段。 | +| API-017 | 已完成 | `profile-growth.html`、`profile-growth-edit.html` | `/genealogy/app/genealogies/{genealogyId}/growth-records`、`/genealogy/app/genealogies/{genealogyId}/growth-records/{recordId}` | 2026-07-09 14:13:25 +08:00 开始;2026-07-09 14:18:15 +08:00 完成。新增 `public/js/growth-pages.js`、`tests/growth-pages.test.js`;更新 `public/js/api-client.js`、`tests/api-client.test.js`、两张成长记录页面和 `public/css/profile-module.css`。验证:全部 Node 测试通过,业务 JS 语法检查通过,页面挂载点检查通过,API-017 自有 JS 未发现样式注入片段。 | +| API-018 | 已完成 | `profile-memo.html`、`profile-memo-edit.html` | `/genealogy/app/genealogies/{genealogyId}/memos`、`/genealogy/app/genealogies/{genealogyId}/memos/{memoId}` | 2026-07-09 14:19:26 +08:00 开始;2026-07-09 14:24:10 +08:00 完成。新增 `public/js/memo-pages.js`、`tests/memo-pages.test.js`;更新 `public/js/api-client.js`、`tests/api-client.test.js`、两张备忘录页面和 `public/css/profile-module.css`。验证:全部 Node 测试通过,业务 JS 语法检查通过,页面挂载点检查通过,API-018 自有 JS 未发现样式注入片段。 | +| API-019 | 已完成 | 文章、动态、相册、成长记录等需要上传的页面 | `/genealogy/app/files/upload`、`/genealogy/app/files/reference`、`/genealogy/app/files/resumable/init`、`/genealogy/app/files/resumable/chunk`、`/genealogy/app/files/resumable/complete` | 2026-07-09 14:00:01 +08:00 开始;2026-07-09 14:04:52 +08:00 完成。新增 `public/js/upload-pages.js`、`tests/upload-pages.test.js`;更新 `public/js/api-client.js`、`tests/api-client.test.js`、`profile-article-edit.html`、`profile-feed-edit.html`、`profile-album.html`、`public/css/profile-module.css`。验证:全部 Node 测试通过,业务 JS 语法检查通过,上传控件挂载点检查通过,API-019 自有 JS 未发现样式注入片段。 | +| API-020 | 已完成 | `profile-services.html` | `/genealogy/app/vip/packages`、`/genealogy/app/vip/orders` | 2026-07-09 14:26:34 +08:00 开始;2026-07-09 14:31:29 +08:00 完成。新增 `public/js/vip-pages.js`、`tests/vip-pages.test.js`;更新 `public/js/api-client.js`、`tests/api-client.test.js`、`profile-services.html`、`public/css/profile-module.css`。验证:全部 Node 测试通过,业务 JS 语法检查通过,会员页挂载点和表单字段检查通过,API-020 自有 JS 未发现样式注入片段;`git diff --check` 因当前目录不是 Git 仓库无法执行。 | +| API-021 | 已完成 | `app.html`、`download.html`、`notice-detail.html` | `/genealogy/app/promotions` | 2026-07-09 14:33:01 +08:00 开始;2026-07-09 14:37:25 +08:00 完成。新增 `public/js/promotion-pages.js`、`tests/promotion-pages.test.js`;更新 `public/js/api-client.js`、`tests/api-client.test.js`、`app.html`、`download.html`、`notice-detail.html`、`public/css/app.css`。验证:全部 Node 测试通过,业务 JS 语法检查通过,三张页面推广挂载点检查通过,API-021 自有 JS 未发现样式注入片段;`git diff --check` 因当前目录不是 Git 仓库无法执行。 | +| API-022 | 已完成 | `profile-security.html` | `/genealogy/app/auth/password`、`/genealogy/app/auth/phone`、`/genealogy/app/auth/account/deactivate`、`/genealogy/app/auth/logout`、`/genealogy/app/auth/login/sms` | 2026-07-09 14:38:22 +08:00 开始;2026-07-09 14:42:47 +08:00 完成。新增 `public/js/security-pages.js`、`tests/security-pages.test.js`;更新 `public/js/api-client.js`、`tests/api-client.test.js`、`profile-security.html`、`public/css/profile-module.css`。验证:全部 Node 测试通过,业务 JS 语法检查通过,账号安全页表单挂载点检查通过,API-022 自有 JS 未发现样式注入片段;`git diff --check` 因当前目录不是 Git 仓库无法执行。 | +| API-023 | 已完成 | `create-genealogy.html`、`profile-data.html`、涉及地区选择的编辑页 | `/genealogy/region/children`、`/genealogy/region/search`、`/genealogy/region/{regionCode}`、`/genealogy/region/path/{regionCode}` | 2026-07-09 14:44:43 +08:00 开始;2026-07-09 14:49:54 +08:00 完成。新增 `public/js/region-pages.js`、`tests/region-pages.test.js`;更新 `public/js/api-client.js`、`public/js/genealogy-pages.js`、`tests/api-client.test.js`、`tests/genealogy-pages.test.js`、`create-genealogy.html`、`profile-data.html`、`public/css/create-genealogy.css`、`public/css/profile-module.css`。验证:全部 Node 测试通过,业务 JS 语法检查通过,两张页面地区选择挂载点检查通过,API-023 自有 JS 未发现样式注入片段;`git diff --check` 因当前目录不是 Git 仓库无法执行。 | +| API-024 | 暂缓 | `surname.html`、`surname-detail.html`、`search-result.html`、`genealogy.html`、`culture.html`、`about.html`、`index.html` | 当前接口文档未发现明确同名业务接口 | 2026-07-09 14:50:47 +08:00 复查。公开家谱接口不支持搜索参数,文章接口依赖 `genealogyId`,推广接口只有列表,不强行映射为姓氏百科或全站搜索。 | +| API-025 | 暂缓 | `profile-video.html`、`promo-video.html`、`profile-data-reminders.html` | 当前接口文档未发现明确视频、资料提醒独立接口 | 2026-07-09 14:50:47 +08:00 复查。视频和资料提醒没有独立接口,暂不伪造接口;`profile-create-family.html` 归入新增 API-026。 | +| API-026 | 已完成 | `profile-create-family.html` | `/genealogy/app/genealogies`、`/genealogy/region/children` | 2026-07-09 14:51:39 +08:00 开始;2026-07-09 14:53:50 +08:00 完成。更新 `profile-create-family.html`、`public/css/profile-module.css`、`tests/genealogy-pages.test.js`,复用 `public/js/api-client.js`、`public/js/region-pages.js`、`public/js/genealogy-pages.js`。验证:全部 Node 测试通过,业务 JS 语法检查通过,页面创建表单和地区选择挂载点检查通过,API-026 自有 JS 未发现样式注入片段;`git diff --check` 因当前目录不是 Git 仓库无法执行。 | +| API-027 | 已完成 | `profile-data.html` | `/genealogy/app/auth/profile` | 2026-07-09 14:57:51 +08:00 开始;2026-07-09 15:03:27 +08:00 完成。更新 `profile-data.html`、`public/js/profile-pages.js`、`public/css/profile-module.css`、`tests/profile-pages.test.js`、`tests/api-client.test.js`。补齐个人资料编辑提交,字段对应 `ProfileUpdateBody`:`nickName`、`avatarOssId`、`sex`、`birthday`;地区字段继续沿用 API-023。验证:已观察 `node tests\profile-pages.test.js` 因缺少 `buildProfileUpdateBody` 失败;实现后 `node tests\profile-pages.test.js`、`node tests\api-client.test.js`、全部 `tests/*.test.js`、业务 JS `node --check`、页面挂载点检查均通过;未发现样式注入片段和 TODO 类占位;`git diff --check` 因当前目录不是 Git 仓库无法执行。 | +| API-028 | 已完成 | `profile-generation.html` | `/genealogy/app/genealogies/{genealogyId}/generation-poems/batch/preview`、`/genealogy/app/genealogies/{genealogyId}/generation-poems/batch/save` | 2026-07-09 15:05:58 +08:00 开始;2026-07-09 15:11:37 +08:00 完成。更新 `profile-generation.html`、`public/js/api-client.js`、`public/js/generation-pages.js`、`public/css/profile-module.css`、`tests/generation-pages.test.js`、`tests/api-client.test.js`。补齐字辈批量预览和批量保存,字段对应 `GenerationPoemBatchBody`:`content`、`stopMissingOldGeneration`。验证:已观察 `node tests\generation-pages.test.js` 因缺少 `buildGenerationBatchBody` 失败,`node tests\api-client.test.js` 因缺少 `previewGenerationPoems` 失败;实现后 `node tests\generation-pages.test.js`、`node tests\api-client.test.js`、全部 `tests/*.test.js`、业务 JS `node --check`、页面挂载点检查均通过;未发现样式注入片段和 TODO 类占位;`git diff --check` 因当前目录不是 Git 仓库无法执行。 | +| API-029 | 已中止 | `profile-services.html` | `/genealogy/app/genealogies/options` | 2026-07-09 15:13:03 +08:00 开始;2026-07-09 15:14:58 +08:00 中止。原因:用户确认此前依据的 `APP.openapi.json` 不是 PC 接口文档,PC/H5 应改用 `genealogy-pc-openapi.yaml`,且接口地址使用 `https://test-genealogy-api.ddxcjp.cn`。本项不再继续按 APP 接口推进。 | + +## 下一步执行顺序 + +1. API-011:世系树与人物管理。 +2. API-012:成员与家族管理。 +3. API-014:谱文与内容文章。 +4. API-013:家族圈动态。 +5. API-015:相册照片。 +6. API-019:上传公共能力。 +7. API-016 至 API-023:按页面入口完整度继续推进。 +8. API-024、API-025:只在确认接口归属后推进。 + +## 本次流程修正记录 + +- 2026-07-09 13:21:31 +08:00:收到用户提醒,停止继续无编号推进;补充本编号追踪表,并要求后续每项先标记状态再实现,完成后再标记已完成。 +- 2026-07-09 14:26:34 +08:00:开始执行 API-020,状态从“待开始”改为“进行中”。 +- 2026-07-09 14:31:29 +08:00:API-020 执行完成,状态从“进行中”改为“已完成”。 +- 2026-07-09 14:33:01 +08:00:开始执行 API-021,状态从“待开始”改为“进行中”。 +- 2026-07-09 14:37:25 +08:00:API-021 执行完成,状态从“进行中”改为“已完成”。 +- 2026-07-09 14:38:22 +08:00:开始执行 API-022,状态从“待开始”改为“进行中”。 +- 2026-07-09 14:42:47 +08:00:API-022 执行完成,状态从“进行中”改为“已完成”。 +- 2026-07-09 14:44:43 +08:00:开始执行 API-023,状态从“待开始”改为“进行中”。 +- 2026-07-09 14:49:54 +08:00:API-023 执行完成,状态从“进行中”改为“已完成”。 +- 2026-07-09 14:50:47 +08:00:复查 API-024/API-025,缺少明确接口的页面标记为“暂缓”,将 `profile-create-family.html` 单独规划为 API-026。 +- 2026-07-09 14:51:39 +08:00:开始执行 API-026,状态从“待开始”改为“进行中”。 +- 2026-07-09 14:53:50 +08:00:API-026 执行完成,状态从“进行中”改为“已完成”。 +- 2026-07-09 14:57:51 +08:00:开始执行 API-027,状态标记为“进行中”;按接口文档把 `profile-data.html` 的个人资料更新归到 `/genealogy/app/auth/profile` PUT,先写测试再实现。 +- 2026-07-09 15:03:27 +08:00:API-027 执行完成,状态从“进行中”改为“已完成”;完成个人资料编辑表单、提交逻辑、页面样式和测试验证。 +- 2026-07-09 15:05:58 +08:00:开始执行 API-028,状态标记为“进行中”;按接口文档把 `profile-generation.html` 的批量字辈能力归到 `/generation-poems/batch/preview` 和 `/generation-poems/batch/save`。 +- 2026-07-09 15:11:37 +08:00:API-028 执行完成,状态从“进行中”改为“已完成”;完成批量字辈预览、批量保存、页面入口、样式和测试验证。 +- 2026-07-09 15:13:03 +08:00:开始执行 API-029,状态标记为“进行中”;把 `profile-services.html` 的家谱选择归到 `/genealogy/app/genealogies/options`。 +- 2026-07-09 15:14:58 +08:00:收到用户纠正,确认旧依据 `APP.openapi.json` 不适合作为 PC 页面接口文档;中止 API-029,后续改以 `genealogy-pc-openapi.yaml` 和 `https://test-genealogy-api.ddxcjp.cn` 重新规划。 +- 2026-07-09 13:25:03 +08:00:开始执行 API-011,状态从“待开始”改为“进行中”。 +- 2026-07-09 13:33:41 +08:00:API-011 执行完成,状态从“进行中”改为“已完成”。 +- 2026-07-09 13:34:36 +08:00:开始执行 API-012,状态从“待开始”改为“进行中”。 +- 2026-07-09 13:40:47 +08:00:API-012 执行完成,状态从“进行中”改为“已完成”。 +- 2026-07-09 13:41:44 +08:00:开始执行 API-014,状态从“待开始”改为“进行中”。 +- 2026-07-09 13:46:31 +08:00:API-014 执行完成,状态从“进行中”改为“已完成”。 +- 2026-07-09 13:47:29 +08:00:开始执行 API-013,状态从“待开始”改为“进行中”。 +- 2026-07-09 13:52:03 +08:00:API-013 执行完成,状态从“进行中”改为“已完成”。 +- 2026-07-09 13:52:58 +08:00:开始执行 API-015,状态从“待开始”改为“进行中”。 +- 2026-07-09 13:57:31 +08:00:API-015 执行完成,状态从“进行中”改为“已完成”。 +- 2026-07-09 14:00:01 +08:00:开始执行 API-019,状态从“待开始”改为“进行中”。 +- 2026-07-09 14:04:52 +08:00:API-019 执行完成,状态从“进行中”改为“已完成”。 +- 2026-07-09 14:05:57 +08:00:开始执行 API-016,状态从“待开始”改为“进行中”。 +- 2026-07-09 14:12:27 +08:00:API-016 执行完成,状态从“进行中”改为“已完成”。 +- 2026-07-09 14:13:25 +08:00:开始执行 API-017,状态从“待开始”改为“进行中”。 +- 2026-07-09 14:18:15 +08:00:API-017 执行完成,状态从“进行中”改为“已完成”。 +- 2026-07-09 14:19:26 +08:00:开始执行 API-018,状态从“待开始”改为“进行中”。 +- 2026-07-09 14:24:10 +08:00:API-018 执行完成,状态从“进行中”改为“已完成”。 diff --git a/docs/pc-api-page-integration-tracker-2026-07-09.md b/docs/pc-api-page-integration-tracker-2026-07-09.md new file mode 100644 index 0000000..b43d2a7 --- /dev/null +++ b/docs/pc-api-page-integration-tracker-2026-07-09.md @@ -0,0 +1,134 @@ +# PC 接口对接重新规划与状态追踪 + +创建时间:2026-07-09 15:14:58 +08:00 +接口依据:`genealogy-pc-openapi.yaml` +接口基础地址:`http://test-genealogy-api.ddxcjp.cn` +旧依据处理:`APP.openapi.json` 只作为历史误用记录,不再作为 PC 页面继续对接依据。 +旧进度记录:`docs/api-page-integration-tracker-2026-07-09.md` + +## 执行规则 + +1. 所有 PC 接口对接必须先进入本追踪表,再改代码。 +2. 每一项开始前先标记“进行中”,记录年月日时分秒和时区。 +3. 每一项完成后记录完成时间、修改文件、接口路径、验证命令和结果。 +4. 只有完成验证后才能标记“已完成”。 +5. `genealogy-pc-openapi.yaml` 没有明确接口的页面,标记“暂缓”或“待确认”,不继续套用 APP 路径。 +6. 样式继续写入 CSS 文件,业务 JS 不注入样式。 +7. 新增或修改的 HTML、JS、CSS 注释使用中文,代码保持可读、不压缩。 + +## PC 文档当前接口范围 + +- 验证中心:`/captcha/require`、`/captcha/challenge`、`/captcha/verify`、`/auth/code` +- PC 认证:`/genealogy/pc/auth/register`、`/genealogy/pc/auth/login`、`/genealogy/pc/auth/login/sms`、`/genealogy/pc/auth/sms/code`、`/genealogy/pc/auth/profile`、`/genealogy/pc/auth/password`、`/genealogy/pc/auth/password/reset`、`/genealogy/pc/auth/phone`、`/genealogy/pc/auth/account/deactivate`、`/genealogy/pc/auth/logout` +- PC 文件:`/genealogy/pc/files/upload`、`/genealogy/pc/files/resumable/init`、`/genealogy/pc/files/resumable/chunk`、`/genealogy/pc/files/resumable/complete`、`/genealogy/pc/files/reference` +- 行政区划:`/genealogy/region/children`、`/genealogy/region/path/{regionCode}`、`/genealogy/region/search`、`/genealogy/region/{regionCode}` + +## 最新 PC YAML 分析记录 + +分析时间:2026-07-09 15:36:16 +08:00 +分析文件:`genealogy-pc-openapi.yaml` + +1. `paths` 中实际存在 25 个接口操作,分为验证码、PC 认证、PC 文件、行政区划四类。 +2. `servers` 仍是本地地址示例,项目实际对接基础地址继续按用户指定使用 `https://test-genealogy-api.ddxcjp.cn`。 +3. 登录后接口使用 `Authorization` 作为 token header,同时所有 PC 认证和文件接口都要求 header `clientid`;用户最新提供的 PC `clientid` 为 `ced7e5f0498645c6ec642dcf450b036f`,客户端 Key 为 `web_pc`。 +4. `components.tags` 和 `components.schemas/requestBodies` 中出现家谱、动态、世系、字辈等业务模型,但 `paths` 没有对应接口路径;实现时不能把这些 schema 当成可调用 PC 接口。 +5. 验证码 schema 示例里仍出现 `APP_LOGIN`、`PC_LOGIN` 等文本示例;实际页面场景以后台配置为准,当前明确可用的 PC/H5 场景为 `WEB_H5_LOGIN`、`WEB_H5_REGISTER`、`WEB_H5_FORGOT_PASSWORD`。 +6. `ProfileUpdate` 示例出现 `regionCode/addressDetail`,但 schema 实际字段是 `provinceCode/cityCode/districtCode`,页面资料提交时应以 schema 字段为准。 +7. `SmsLoginBody` 未声明 `validToken` 字段,但短信发送 `SmsCodeBody` 必填 `validToken`;短信登录流程应先通过验证码拿票据,再发短信验证码,最后用短信码登录。 +8. 文件引用 `FileReferenceBody` 必填 `bizType/bizTable/bizId/bizField`,`ossId/ossIds/usageScene/usageName` 可选;但文章、动态、相册等业务主体接口未提供,文件引用只能作为公共上传能力,不代表主业务已可提交。 + +## 验证码与 TAC 调用记录 + +- 后台验证码配置截图中,PC/H5 当前明确可用场景是 `WEB_H5_LOGIN`、`WEB_H5_REGISTER`、`WEB_H5_FORGOT_PASSWORD`,客户端 Key 为 `web_pc`,验证服务为“天爱行为验证码”,验证码类型为“滑块验证码”。 +- `ADMIN_LOGIN` 是后台管理登录,类型为旋转验证码,不归当前 PC/H5 页面使用。 +- `APP_LOGIN`、`APP_REGISTER`、`APP_PHONE_CHANGE`、`APP_FORGOT_PASSWORD`、`APP_SMS_CODE`、`APP_ACCOUNT_DEACTIVATE` 的客户端 Key 为 `app`,不能继续作为 PC/H5 场景默认值。 +- PC/H5 登录页调用链: + 1. 页面加载 `public/tac/css/tac.css`、`public/tac/js/tac.min.js`、`public/js/captcha-pages.js`。 + 2. 表单提供 `input[name="validToken"]` 隐藏字段和 `[data-captcha-box]` 容器。 + 3. `auth-pages.js` 在登录提交前调用 `CaptchaPages.ensureToken(form, { sceneCode: 'WEB_H5_LOGIN', subject: 手机号 })`。 + 4. `captcha-pages.js` 先请求 `GET /captcha/require?tenantId=000000&clientId=ced7e5f0498645c6ec642dcf450b036f&sceneCode=WEB_H5_LOGIN&subject=手机号`。 + 5. 如果返回 `required=false`,业务表单可继续提交;如果需要验证,则创建 `new CaptchaConfig(...)` 和 `new TAC(config, ...)`。 + 6. TAC 内部请求 `POST /captcha/challenge` 获取 `captchaType/challengeId/payload`,适配为 TAC 需要的 `data.type/data.id/backgroundImage/templateImage`。 + 7. 用户拖动完成后,TAC 请求 `POST /captcha/verify`,提交 `challengeId/providerCode/captchaType/payload.data.trackList`。 + 8. 验证成功后从返回 `data.validToken` 取票据,写回表单隐藏字段 `validToken`,再提交 PC 登录接口。 +- PC/H5 注册使用 `WEB_H5_REGISTER`,找回密码和找回密码短信验证码使用 `WEB_H5_FORGOT_PASSWORD`;换绑手机、注销账号暂未看到明确 PC/H5 场景编码,仍待后端补充后再接。 + +## 编号计划 + +| 编号 | 状态 | 页面范围 | 接口范围 | 计划与验证 | +| --- | --- | --- | --- | --- | +| PC-000 | 已完成 | 全项目 | `genealogy-pc-openapi.yaml`、`https://test-genealogy-api.ddxcjp.cn` | 2026-07-09 15:14:58 +08:00 完成。确认旧 APP 接口依据错误,停止继续按 APP 路径推进;确认 PC YAML 当前只明确认证、文件、地区接口。 | +| PC-001 | 已完成 | 全项目配置层 | `config.js`、`https://test-genealogy-api.ddxcjp.cn` | 2026-07-09 15:57:36 +08:00 修正完成。根目录 `config.js` 作为开发/生产环境切换的唯一入口;开发环境基础地址为 `https://test-genealogy-api.ddxcjp.cn`;PC `clientid` 按用户最新截图改为 `ced7e5f0498645c6ec642dcf450b036f`,客户端 Key 为 `web_pc`。验证:`node tests\config.test.js` 输出 `config tests passed`,`node --check config.js` 通过。 | +| PC-002 | 已完成 | `utils` 公共工具层、`public/js/api-client.js` | PC `clientid`、通用请求、token、表单、存储、PC YAML paths | 2026-07-09 15:51:34 +08:00 完成。新增 `utils/StorageUtil.js`、`utils/FormUtil.js`、`utils/RequestUtil.js`;重写 `public/js/api-client.js`,真实请求只保留 PC YAML 中的 `/captcha/*`、`/auth/code`、`/genealogy/pc/auth/*`、`/genealogy/pc/files/*`、`/genealogy/region/*`,PC YAML 未覆盖业务方法改为抛 `PC_API_NOT_AVAILABLE`,不再发旧 APP 请求。验证:`node tests\utils.test.js`、`node tests\api-client.test.js` 均通过;`Select-String -Path public\js\api-client.js -Pattern '/genealogy/app/'` 无输出;`node --check public\js\api-client.js` 通过。 | +| PC-003 | 已完成 | `login.html`、`register.html`、`forgot-password.html`、`profile-security.html`、`profile-data.html`、`profile.html` | `/genealogy/pc/auth/*`、`/captcha/*` | 2026-07-09 16:00:22 +08:00 完成。`login.html` 使用 `WEB_H5_LOGIN`;`register.html` 使用 `WEB_H5_REGISTER`;`forgot-password.html` 使用 `WEB_H5_FORGOT_PASSWORD`;认证/资料/安全页面均已补齐 `config.js`、`utils/*`、`api-client.js` 脚本顺序。资料提交字段按 PC YAML 使用 `provinceCode/cityCode/districtCode`。验证:`node tests\auth-pages.test.js`、`node tests\captcha-pages.test.js`、`node tests\profile-pages.test.js`、`node tests\security-pages.test.js` 均通过;相关 JS `node --check` 通过;旧 `PC_LOGIN/PC_REGISTER/PC_PASSWORD_RESET/APP_*` 场景扫描无输出。 | +| PC-004 | 已完成 | 文件上传相关页面 | `/genealogy/pc/files/*` | 2026-07-09 16:03:23 +08:00 完成。`api-client.js` 文件上传、分片上传、文件引用绑定均走 `/genealogy/pc/files/*`;`profile-album.html`、`profile-article-edit.html`、`profile-feed-edit.html`、`profile-growth-edit.html`、`profile-memo-edit.html`、`profile-gift-edit.html` 已补齐 `config.js`、`utils/*`、`api-client.js`、`upload-pages.js` 顺序。验证:`node tests\upload-pages.test.js`、`node tests\api-client.test.js` 通过;`Select-String -Path public\js\api-client.js,public\js\upload-pages.js -Pattern '/genealogy/app/files'` 无输出。 | +| PC-005 | 已完成 | 使用地区选择的页面 | `/genealogy/region/*` | 2026-07-09 16:05:12 +08:00 完成。`create-genealogy.html`、`profile-create-family.html`、`profile-data.html` 地区选择页面已补齐 `config.js`、`utils/*`、`api-client.js`、`region-pages.js` 顺序;地区请求只走 `/genealogy/region/children`、`/genealogy/region/path/{regionCode}`、`/genealogy/region/search`、`/genealogy/region/{regionCode}`。验证:`node tests\region-pages.test.js`、`node tests\profile-pages.test.js` 通过;`node --check public\js\region-pages.js` 通过;地区旧 APP 路径扫描无输出。 | +| PC-006 | 已修正 | 全项目页面覆盖矩阵 | PC YAML 现有接口覆盖范围与缺口 | 2026-07-09 15:34:26 +08:00 完成修正。该项只作为文档清单任务,不做业务代码。输出页面级覆盖矩阵:完整可闭环、接口存在但验证码待确认、只能保留公共能力、文件能力可切换但主业务待确认、PC YAML 未覆盖。避免把“页面里某个公共接口可用”误标为“整页已可对接”。 | +| PC-007 | 已完成 | 详细实施规划 | `genealogy-pc-openapi.yaml` 的 `paths`、`docs/superpowers/plans/2026-07-09-pc-api-replan.md` | 2026-07-09 15:41:40 +08:00 完成修正。重写详细实施计划:只以 YAML `paths` 为真实接口来源;`api-client.js` 不能保留 APP 路径 fallback;PC YAML 未覆盖页面改为受控“接口待确认/PC_API_NOT_AVAILABLE”;补充资料字段 `provinceCode/cityCode/districtCode`、TAC 登录场景 `WEB_H5_LOGIN`、文件/地区局部能力边界。 | +| PC-008 | 已完成 | PC YAML 未覆盖业务页面 | `PC_API_NOT_AVAILABLE`、旧 APP 网络路径清理 | 2026-07-09 16:06:35 +08:00 完成。`api-client.js` 中家谱主体、成员、字辈、世系、动态、文章、相册、祭祀、族务、VIP、通知、反馈、帮助、推广等 PC YAML 未覆盖业务方法保留方法名但统一抛 `PC_API_NOT_AVAILABLE`,不再发 `/genealogy/app/` 请求。验证:`Get-ChildItem -Path tests -Filter *.test.js | Sort-Object Name | ForEach-Object { node $_.FullName }` 全部通过;`Select-String -Path public\js\api-client.js -Pattern '/genealogy/app/'` 无输出。 | +| PC-009 | 已完成 | `login.html`、`register.html`、`forgot-password.html` | `/genealogy/pc/auth/login`、`/genealogy/pc/auth/login/sms`、`/genealogy/pc/auth/register`、`/genealogy/pc/auth/sms/code`、`/genealogy/pc/auth/password/reset`、`/captcha/*` | 2026-07-09 16:32:17 +08:00 开始,2026-07-09 16:40:02 +08:00 完成。专项计划见 `docs/superpowers/plans/2026-07-09-pc-auth-three-pages.md`。`login.html` 已补齐密码登录/短信登录两种模式,短信登录使用 `WEB_H5_LOGIN` 发送验证码并调用 `/genealogy/pc/auth/login/sms`;注册页继续使用 `WEB_H5_REGISTER` 和 `/genealogy/pc/auth/register`;忘记密码页继续使用 `WEB_H5_FORGOT_PASSWORD`、`/genealogy/pc/auth/sms/code`、`/genealogy/pc/auth/password/reset`。修改文件:`login.html`、`public/css/login.css`、`public/js/auth-pages.js`、`tests/auth-pages.test.js`、`tests/api-client.test.js`。验证:`node tests\auth-pages.test.js`、`node tests\api-client.test.js`、`node tests\captcha-pages.test.js` 通过;全量 `tests/*.test.js` 通过;`node --check public\js\auth-pages.js`、`node --check public\js\api-client.js`、`node --check public\js\captcha-pages.js` 通过;内联样式和旧验证码场景扫描无输出。 | +| PC-010 | 已完成 | `config.js`、`public/js/api-client.js` | 开发环境接口基础地址、`/captcha/require`、`/captcha/challenge` | 2026-07-09 16:45:35 +08:00 开始,2026-07-09 16:48:05 +08:00 完成。根因:浏览器和 `curl.exe` 复现 `https://test-genealogy-api.ddxcjp.cn` TLS 握手失败,错误为 `ERR_SSL_UNRECOGNIZED_NAME_ALERT`/`SEC_E_ILLEGAL_MESSAGE`;同一 `/captcha/require` 请求改用 `http://test-genealogy-api.ddxcjp.cn` 返回 `200 OK`。处理:开发环境基础地址改为 `http://test-genealogy-api.ddxcjp.cn`;`config.js` 对旧的 `https://test-genealogy-api.ddxcjp.cn` 本地覆盖值做归一,避免浏览器 localStorage 残留继续走坏地址;`api-client.js` fallback 默认地址同步改为 HTTP。修改文件:`config.js`、`public/js/api-client.js`、`tests/config.test.js`、`tests/api-client.test.js`。验证:`node tests\config.test.js`、`node tests\api-client.test.js`、`node --check config.js`、`node --check public\js\api-client.js` 通过。 | +| PC-011 | 已完成 | `login.html`、`register.html`、`forgot-password.html`、`public/css/public.css`、`public/js/captcha-pages.js` | `public/tac` 弹窗挂载、`/captcha/challenge`、`/captcha/verify` | 2026-07-09 16:56:22 +08:00 开始,2026-07-09 17:02:35 +08:00 完成。根因:`public/tac/css/tac.css` 中 `#tianai-captcha-parent` 是相对定位,会在绑定元素内渲染;此前把 `[data-captcha-box]` 放进表单内部,导致验证码块直接占据卡片内容区域。处理:三张认证页移除表单内部挂载点,新增页面级 `.auth-captcha-modal[data-captcha-box]`;`public/css/public.css` 增加固定遮罩、居中和移动端宽度约束;`captcha-pages.js` 优先使用页面级挂载点;未修改 `public/tac/js/tac.min.js`。修改文件:`login.html`、`register.html`、`forgot-password.html`、`public/css/public.css`、`public/js/captcha-pages.js`、`tests/auth-page-structure.test.js`、`tests/captcha-pages.test.js`。验证:`node tests\auth-page-structure.test.js`、`node tests\captcha-pages.test.js` 通过;全量 `tests/*.test.js` 通过;`node --check public\js\captcha-pages.js`、`node --check public\js\auth-pages.js` 通过;表单内部 `data-captcha-box` 扫描无输出。 | + +## PC 页面覆盖矩阵草案 + +| 分类 | 页面/模块 | 当前判断 | 处理方式 | +| --- | --- | --- | --- | +| 完整可闭环 | `login.html` | PC YAML 有 `/genealogy/pc/auth/login`,后台验证码配置明确 `WEB_H5_LOGIN`,TAC 可完整串联。 | 可作为第一批执行页面。 | +| 接口存在但部分验证码场景待确认 | `register.html`、`forgot-password.html`、`profile-security.html` 中的换绑/注销相关动作 | PC YAML 有对应认证接口;注册场景为 `WEB_H5_REGISTER`,找回密码场景为 `WEB_H5_FORGOT_PASSWORD`,换绑和注销暂未提供 PC/H5 场景。 | 注册和找回密码可接 TAC;换绑、注销涉及验证码的提交先记录待确认,不能套用 `APP_*` 场景。 | +| 资料接口可对接 | `profile.html`、`profile-data.html` 中用户资料读取/修改 | PC YAML 有 `/genealogy/pc/auth/profile`。 | 只处理用户资料读写;页面里其他非资料业务不因此视为完成。 | +| 公共地区能力可保留 | `create-genealogy.html`、`profile-create-family.html`、`profile-data.html` 中地区选择控件 | PC YAML 有 `/genealogy/region/*`。 | 只保留和验证地区选择;创建家谱等主业务接口 PC YAML 未覆盖,仍待确认。 | +| 文件能力可切换 | 文章、动态、相册、成长、备忘、祭祀等页面里的上传控件 | PC YAML 有 `/genealogy/pc/files/*`。 | 只把上传、分片、文件引用切到 PC 文件接口;文章/动态/相册等主业务接口仍待确认。 | +| PC YAML 未覆盖 | 家谱主体、成员、字辈、世系、动态、文章、相册、祭祀、族务、VIP、通知、反馈、帮助、推广、姓氏、搜索等主业务页面 | 当前 PC YAML 没有这些业务路径。 | 不套用 APP 路径;等待补充 PC 接口后再逐页规划。 | + +## 流程记录 + +- 2026-07-09 15:14:58 +08:00:收到用户纠正,停止 APP 接口继续推进;读取 `genealogy-pc-openapi.yaml`,确认 PC 路径前缀为 `/genealogy/pc`,基础地址改用 `https://test-genealogy-api.ddxcjp.cn`。 +- 2026-07-09 15:14:58 +08:00:创建本 PC 专用追踪表,PC-001 标记为“进行中”。 +- 2026-07-09 15:17:57 +08:00:收到用户补充要求,重新规划 `config.js` 环境切换和 `utils` 公共 JS 分层;停止直接执行旧 PC-001,把 PC-001 到 PC-006 重排为待执行计划。 +- 2026-07-09 15:24:02 +08:00:收到用户补充验证码要求;记录 `public/tac` 调用链,确认 PC/H5 登录使用 `WEB_H5_LOGIN`,其他未配置 PC/H5 场景的认证验证码先待确认。 +- 2026-07-09 15:31:14 +08:00:根据用户质疑修正 PC-006;不再把“公共地区/文件接口可用”写成“整页可对接”,改为页面覆盖矩阵和缺口清单。 +- 2026-07-09 15:34:26 +08:00:同步修正详细规划文件 `docs/superpowers/plans/2026-07-09-pc-api-replan.md` 的 Task 6,并将 PC-006 标记为“已修正”。 +- 2026-07-09 15:41:40 +08:00:按最新 YAML 分析结果重写 `docs/superpowers/plans/2026-07-09-pc-api-replan.md`,新增 PC-007;明确旧 APP 路径不能作为 PC fallback,缺失业务接口统一降级为接口待确认。 +- 2026-07-09 15:44:18 +08:00:开始执行 PC-001/PC-002,先按 TDD 创建 `config.js` 和 `utils` 公共工具测试,再实现最小配置与工具层。 +- 2026-07-09 15:47:17 +08:00:完成 PC-001 和 PC-002 的公共工具层部分;`config.js` 位于根目录,`utils` 只放跨页面公共 JS,页面业务仍留在 `public/js`。 +- 2026-07-09 15:51:34 +08:00:完成 PC-002 的 `api-client.js` 切换;旧 APP 路径不再作为网络请求路径,未覆盖业务统一抛 `PC_API_NOT_AVAILABLE`。 +- 2026-07-09 15:55:28 +08:00:PC-003 认证页验证码阶段完成;登录使用 `WEB_H5_LOGIN`,未确认 PC/H5 场景的注册和找回密码不调用验证中心。 +- 2026-07-09 15:57:36 +08:00:收到用户补充截图,确认 PC `clientid` 为 `ced7e5f0498645c6ec642dcf450b036f`、客户端 Key 为 `web_pc`,新增注册场景 `WEB_H5_REGISTER` 和找回密码场景 `WEB_H5_FORGOT_PASSWORD`;同步修正配置、计划和认证页场景映射。 +- 2026-07-09 16:00:22 +08:00:PC-003 完成;认证页、资料页、安全页的 PC auth/captcha 接入和脚本顺序已验证。 +- 2026-07-09 16:03:23 +08:00:PC-004 完成;上传公共能力切换到 PC 文件接口,相关上传页已补齐公共层脚本顺序。 +- 2026-07-09 16:05:12 +08:00:PC-005 完成;地区选择只保留公共行政区划能力,创建家谱等主业务仍按 PC YAML 未覆盖处理。 +- 2026-07-09 16:06:35 +08:00:PC-008 完成;PC YAML 未覆盖业务不会继续发送旧 APP 请求,全量 Node 测试通过。 +- 2026-07-09 16:07:42 +08:00:收尾验证完成;全量 `tests/*.test.js` 通过,`public/js` 非压缩业务脚本 `node --check` 通过,旧 `/genealogy/app/`、旧验证码场景、旧 PC clientId 扫描无输出;`git diff --check` 因当前目录不是 Git 仓库无法执行。 +- 2026-07-09 16:09:55 +08:00:开始登录、注册、找回密码三页专项核对;按 PC YAML 的 `PasswordLoginBody`、`PasswordRegisterBody`、`SmsCodeBody`、`PasswordResetBody` 修正页面字段、输入类型、验证码场景声明和文案。 +- 2026-07-09 16:12:10 +08:00:登录、注册、找回密码三页专项完成;三页表单已显式声明 `data-captcha-scene`,手机号输入改为 `type="tel"`、`inputmode="numeric"`、`maxlength="11"`,找回密码文案改为只支持注册手机号;`auth-pages.js` 优先读取页面 `data-captcha-scene`。验证:`node tests\auth-pages.test.js`、`node tests\captcha-pages.test.js`、`node tests\api-client.test.js` 通过;三页字段/脚本顺序扫描通过;旧邮箱文案和旧验证码场景扫描无输出。 +- 2026-07-09 16:19:11 +08:00:根据用户反馈“接口文档少了很多东西”重新核对 `genealogy-pc-openapi.yaml` 的真实 `paths`;确认当前 YAML 只有验证中心、认证登录、文件上传、行政区划四类共 25 个操作,虽然声明了家谱、成员、字辈、世系、家族圈、文章、相册、祭祀、族务、VIP、消息、反馈等标签和模型,但没有对应可调用路径。结论:这些业务页面不能继续视为已对接,只能保留 `PC_API_NOT_AVAILABLE`,等待后端补充 PC 接口文档。 +- 2026-07-09 16:48:05 +08:00:处理注册页滑动验证请求失败。确认失败不是注册字段问题,而是测试域名 HTTPS 握手失败;开发环境接口基础地址修正为 HTTP,并兼容清理旧 HTTPS 本地覆盖值。 +- 2026-07-09 16:56:22 +08:00:处理 TAC 验证码显示位置问题。确认不是后端验证码类型问题,也不是 `tac.min.js` 被改;问题来自前端挂载点在表单中,导致 TAC 相对定位弹窗被卡片布局吞进去。 +- 2026-07-09 17:02:35 +08:00:TAC 弹窗挂载修复完成。三张认证页改为页面级固定遮罩容器承载 TAC,表单内部不再放验证码挂载点;新增结构测试防止回退。 +- 2026-07-09 17:04:21 +08:00:复现用户反馈的 `503 Service Unavailable`。`curl.exe` 请求 `/captcha/require` 和域名根路径 `/` 均返回 `Server: SakuraFrp`、`503 Service Unavailable`,响应页提示检查 SakuraFrp 隧道节点、隧道配置、frpc 是否运行、隧道是否在线。结论:这是测试接口域名/内网穿透服务当前不可用,不是前端字段、TAC 样式或请求参数导致。 +## PC-012 认证页 layui 提示与 TAC 弹窗样式修正 + +- 状态:已完成 +- 计划开始时间:2026-07-09 17:14:08 +08:00 +- 计划文件:`docs/superpowers/plans/2026-07-09-auth-layer-message.md` +- 页面范围:`login.html`、`register.html`、`forgot-password.html` +- 接口范围:不新增接口,只修正 `/captcha/*` 流程中的前端提示方式和弹窗展示方式。 +- 完成时间:2026-07-09 17:20:24 +08:00 +- 修改文件:`login.html`、`register.html`、`forgot-password.html`、`utils/MessageUtil.js`、`public/js/auth-pages.js`、`public/js/captcha-pages.js`、`public/css/public.css`、`tests/auth-page-structure.test.js`、`tests/auth-message.test.js`、`docs/superpowers/plans/2026-07-09-auth-layer-message.md` +- 验证结果: + - `node tests\auth-page-structure.test.js` 通过。 + - `node tests\auth-message.test.js` 通过。 + - `node tests\auth-pages.test.js` 通过。 + - `node tests\captcha-pages.test.js` 通过。 + - `node --check public\js\auth-pages.js` 通过。 + - `node --check public\js\captcha-pages.js` 通过。 + - `node --check utils\MessageUtil.js` 通过。 + - `Get-ChildItem -Path tests -Filter *.test.js | Sort-Object Name | ForEach-Object { node $_.FullName }` 全部通过。 +- 剩余风险:测试域名当前仍可能返回 SakuraFrp `503 Service Unavailable`,这是后端/隧道可用性问题,不是本次提示和样式改动导致。 +- 执行计划: + 1. 已完成失败测试:`tests/auth-page-structure.test.js`、`tests/auth-message.test.js`。 + 2. 已完成:三页加载 layui 样式和脚本。 + 3. 已完成:`auth-pages.js`、`captcha-pages.js` 提示统一走 `MessageUtil`/`layui.layer.msg`,不再使用浏览器原生 `alert`。 + 4. 已完成:`public/css/public.css` 增加认证页 layer 提示皮肤。 + 5. 已完成:焦点测试、相关测试、语法检查和全量 Node 测试通过。 diff --git a/docs/superpowers/plans/2026-07-09-auth-layer-message.md b/docs/superpowers/plans/2026-07-09-auth-layer-message.md new file mode 100644 index 0000000..bf1de1e --- /dev/null +++ b/docs/superpowers/plans/2026-07-09-auth-layer-message.md @@ -0,0 +1,128 @@ +# Auth Layer Message Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 让登录、注册、忘记密码三页的提示统一使用 layui `layer.msg`,不再出现浏览器原生 alert,并补好认证页提示和 TAC 弹窗样式。 + +**Architecture:** 页面加载 `public/layui/css/layui.css` 和 `public/layui/layui.js`;`auth-pages.js` 与 `captcha-pages.js` 只负责调用提示,不写样式;提示皮肤与 TAC 弹窗样式写入 `public/css/public.css`。 + +**Tech Stack:** 原生 HTML/CSS/JavaScript、layui layer、public/tac、Node 断言测试。 + +## Global Constraints + +- 计划开始时间:2026-07-09 17:14:08 +08:00。 +- 只处理登录、注册、忘记密码三页的提示与弹窗样式问题。 +- 不修改 `public/tac/js/tac.min.js`。 +- 不使用 HTML 原生 `alert` 作为用户提示。 +- 样式写在 CSS 文件里,JS 只切 class 或调用组件。 +- 新增或修改的 HTML、CSS、JS 注释使用中文,代码保持可读。 + +--- + +### Task 1: 补充失败测试 + +**Files:** +- Modify: `tests/auth-page-structure.test.js` +- Create: `tests/auth-message.test.js` + +**Interfaces:** +- Consumes: 当前三页 HTML 与认证脚本。 +- Produces: 能捕获缺少 layui 和原生 alert 兜底的测试。 + +- [x] **Step 1: 添加页面结构断言** + +检查 `login.html`、`register.html`、`forgot-password.html` 必须加载 `public/layui/css/layui.css` 和 `public/layui/layui.js`,且 `layui.js` 在 `auth-pages.js` 前面。 + +- [x] **Step 2: 添加提示脚本断言** + +检查 `public/js/auth-pages.js` 与 `public/js/captcha-pages.js` 不再包含 `root.alert`,并检查 `public/css/public.css` 提供 `.auth-layer-message`。 + +- [x] **Step 3: 验证红灯** + +运行: + +```powershell +node tests\auth-page-structure.test.js +node tests\auth-message.test.js +``` + +预期:两个测试均失败,分别提示缺少 layui 和仍存在 `root.alert`。 + +### Task 2: 接入 layui 并替换原生提示 + +**Files:** +- Modify: `login.html` +- Modify: `register.html` +- Modify: `forgot-password.html` +- Modify: `public/js/auth-pages.js` +- Modify: `public/js/captcha-pages.js` + +**Interfaces:** +- Consumes: `layui.layer.msg(message, options)`。 +- Produces: `showMessage(message)` 统一使用 layer,缺少 layui 时只输出控制台警告,不再弹原生 alert。 + +- [x] **Step 1: 三页加载 layui 样式和脚本** + +在三页 `` 中加入 `public/layui/css/layui.css`,在 `auth-pages.js` 前加入 `public/layui/layui.js`。 + +- [x] **Step 2: 修改认证脚本提示** + +把 `auth-pages.js` 的 `showMessage` 改为优先调用 `layui.layer.msg(message, { skin: 'auth-layer-message' })`,没有 layui 时使用 `console.warn`。 + +- [x] **Step 3: 修改验证码脚本提示** + +把 `captcha-pages.js` 的 `showMessage` 同步改为同样的 layer 提示策略。 + +### Task 3: 补齐公共样式 + +**Files:** +- Modify: `public/css/public.css` + +**Interfaces:** +- Consumes: layui layer 生成的 `.auth-layer-message`。 +- Produces: 认证页统一提示皮肤和移动端更稳的 TAC 弹窗尺寸。 + +- [x] **Step 1: 添加 layui 消息皮肤** + +在公共 CSS 中添加 `.auth-layer-message`,控制圆角、背景、文字和阴影。 + +- [x] **Step 2: 微调 TAC 弹窗移动端尺寸** + +继续使用 `.auth-captcha-modal` 页面级挂载,限制 TAC 宽度不超过视口,避免盖层内容顶到边缘。 + +### Task 4: 验证并记录完成 + +**Files:** +- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` + +**Interfaces:** +- Consumes: Task 1-3 的实现结果。 +- Produces: PC-012 完成记录。 + +- [x] **Step 1: 跑焦点测试** + +运行: + +```powershell +node tests\auth-page-structure.test.js +node tests\auth-message.test.js +``` + +预期:全部通过。 + +- [x] **Step 2: 跑相关测试和语法检查** + +运行: + +```powershell +node tests\auth-pages.test.js +node tests\captcha-pages.test.js +node --check public\js\auth-pages.js +node --check public\js\captcha-pages.js +``` + +预期:全部通过。 + +- [x] **Step 3: 更新追踪记录** + +把 PC-012 标记为已完成,写入完成时间、修改文件、验证命令和剩余风险。 diff --git a/docs/superpowers/plans/2026-07-09-pc-api-replan.md b/docs/superpowers/plans/2026-07-09-pc-api-replan.md new file mode 100644 index 0000000..d6c4d40 --- /dev/null +++ b/docs/superpowers/plans/2026-07-09-pc-api-replan.md @@ -0,0 +1,762 @@ +# PC API Replan Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 按最新 `genealogy-pc-openapi.yaml` 重新整理 PC/H5 页面接口对接,只接入 `paths` 中真实存在的验证码、PC 认证、PC 文件和行政区划接口,并清除旧 APP 路径作为 PC 合同的风险。 + +**Architecture:** 根目录 `config.js` 是接口基础地址、客户端 ID、租户、token key 的唯一配置入口;`utils` 只放跨页面公共 JS;`public/js/api-client.js` 只暴露 PC YAML `paths` 中存在的接口和受控的“PC 接口未提供”错误。页面业务 JS 继续放在 `public/js`,页面只能调用 PC 已确认方法;PC YAML 未覆盖的页面保留静态/本地展示或显示接口待确认状态。 + +**Tech Stack:** 静态 HTML、原生 JS、jQuery/Layui、Node 测试脚本、`public/tac` 天爱验证码静态资源。 + +## Global Constraints + +- 接口依据只使用 `genealogy-pc-openapi.yaml` 的 `paths`。 +- 开发环境接口基础地址固定为 `https://test-genealogy-api.ddxcjp.cn`。 +- PC 默认 `clientid` 使用用户最新截图确认值:`ced7e5f0498645c6ec642dcf450b036f`,客户端 Key 为 `web_pc`。 +- 登录后请求使用 `Authorization` header,PC 认证和文件请求同时携带 `clientid` header。 +- 样式写入 CSS 文件,不用 JS 注入样式。 +- 新增或修改的 HTML、JS、CSS 注释使用中文。 +- `utils` 只放跨页面公共 JS;页面业务 JS 继续放 `public/js`。 +- PC YAML 没有明确接口的页面不套用 APP 路径,不把 schema/requestBodies 当成可调用接口。 +- 验证码只使用后台已配置的 PC/H5 场景;当前明确场景为 `WEB_H5_LOGIN`、`WEB_H5_REGISTER`、`WEB_H5_FORGOT_PASSWORD`。 +- 换绑手机、注销账号的 PC/H5 验证码场景未确认,不套用 `APP_*` 场景。 + +--- + +## File Structure + +- Create or Modify: `config.js` + - 唯一负责环境切换、开发接口地址、PC clientid、tenantId、token key、token header name。 +- Create or Modify: `utils/StorageUtil.js` + - 负责 localStorage 安全读写。 +- Create or Modify: `utils/FormUtil.js` + - 负责表单读取、空值清理、数字转换、布尔转换。 +- Create or Modify: `utils/RequestUtil.js` + - 负责 URL 拼接、header 合并、FormData 判断、JSON 解包、业务错误对象。 +- Modify: `public/js/api-client.js` + - 只把 PC YAML `paths` 中存在的接口作为真实请求方法;旧 APP 路径不得作为 fallback。 +- Modify: `public/js/auth-pages.js` + - 登录使用 `WEB_H5_LOGIN`;注册使用 `WEB_H5_REGISTER`;找回密码使用 `WEB_H5_FORGOT_PASSWORD`。 +- Modify: `public/js/captcha-pages.js` + - 按 `/captcha/require`、`/captcha/challenge`、`/captcha/verify` 组织 TAC 调用。 +- Modify: `public/js/profile-pages.js` + - 用户资料提交字段以 `ProfileUpdateBody` schema 为准:`provinceCode/cityCode/districtCode`。 +- Modify: `public/js/security-pages.js` + - 修改密码、换绑手机、注销账号只调用 PC 认证接口;涉及验证码的动作保留 PC/H5 场景待确认记录。 +- Modify: `public/js/upload-pages.js` + - 上传、分片上传、文件引用只调用 `/genealogy/pc/files/*`。 +- Modify: `public/js/region-pages.js` + - 地区选择只调用 `/genealogy/region/*`。 +- Modify: related HTML files + - API 页面按顺序加载 `config.js`、`utils/*.js`、`public/js/api-client.js`、页面业务脚本。 +- Modify: `tests/*.test.js` + - 测试只验证 PC YAML 已确认接口;旧 APP 路径测试改为“不可作为 PC 合同”。 +- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` + - 每项开始/完成都回填时间、文件、接口、验证结果。 + +--- + +### Task 1: 配置入口与公共工具合同 + +**Files:** +- Create or Modify: `config.js` +- Create or Modify: `utils/StorageUtil.js` +- Create or Modify: `utils/FormUtil.js` +- Create or Modify: `utils/RequestUtil.js` +- Test: `tests/config.test.js` +- Test: `tests/utils.test.js` + +**Interfaces:** +- Produces: `GenealogyConfig.getConfig(): { env, apiBaseUrl, clientId, tenantId, tokenKey, tokenHeaderName }` +- Produces: `GenealogyConfig.getApiBaseUrl(): string` +- Produces: `GenealogyConfig.getClientId(): string` +- Produces: `GenealogyConfig.getTenantId(): string` +- Produces: `GenealogyConfig.getTokenKey(): string` +- Produces: `StorageUtil.read(storage, key): string|null` +- Produces: `StorageUtil.write(storage, key, value): void` +- Produces: `StorageUtil.remove(storage, key): void` +- Produces: `FormUtil.trimOrUndefined(value): string|undefined` +- Produces: `FormUtil.toNumberOrUndefined(value): number|undefined` +- Produces: `FormUtil.toBoolean(value): boolean` +- Produces: `FormUtil.getFormValues(form): object` +- Produces: `RequestUtil.createRequester(options)(method, path, requestOptions): Promise` + +- [ ] **Step 1: Write config test** + +Create or replace `tests/config.test.js` with: + +```js +const assert = require('assert'); +const configFactory = require('../config.js'); + +const store = {}; +const config = configFactory({ + localStorage: { + getItem: (key) => Object.prototype.hasOwnProperty.call(store, key) ? store[key] : null, + setItem: (key, value) => { store[key] = String(value); } + } +}); + +assert.strictEqual(config.getApiBaseUrl(), 'https://test-genealogy-api.ddxcjp.cn'); +assert.strictEqual(config.getClientId(), 'ced7e5f0498645c6ec642dcf450b036f'); +assert.strictEqual(config.getTenantId(), '000000'); +assert.strictEqual(config.getTokenKey(), 'genealogy_auth_token'); +assert.strictEqual(config.getConfig().tokenHeaderName, 'Authorization'); + +store.genealogy_api_base_url = 'https://custom.example.test'; +assert.strictEqual(config.getApiBaseUrl(), 'https://custom.example.test'); + +console.log('config tests passed'); +``` + +- [ ] **Step 2: Run config test** + +Run: `node tests\config.test.js` +Expected before implementation: FAIL if `config.js` is absent or lacks these methods. +Expected after implementation: PASS with `config tests passed`. + +- [ ] **Step 3: Implement `config.js`** + +Use this implementation: + +```js +(function (root, factory) { + // 全局配置同时支持浏览器页面和 Node 测试。 + if (typeof module === 'object' && module.exports) { + module.exports = factory; + return; + } + + root.GenealogyConfig = factory(root); +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var DEFAULT_ENV = 'development'; + var API_BASE_URLS = { + development: 'https://test-genealogy-api.ddxcjp.cn', + production: '' + }; + var CLIENT_ID = 'ced7e5f0498645c6ec642dcf450b036f'; + var TENANT_ID = '000000'; + var TOKEN_KEY = 'genealogy_auth_token'; + var TOKEN_HEADER_NAME = 'Authorization'; + var ENV_KEY = 'genealogy_env'; + var BASE_URL_KEY = 'genealogy_api_base_url'; + + function readStorage(key) { + try { + return root.localStorage && root.localStorage.getItem(key); + } catch (error) { + return null; + } + } + + function getEnv() { + return readStorage(ENV_KEY) || DEFAULT_ENV; + } + + function getApiBaseUrl() { + return readStorage(BASE_URL_KEY) || API_BASE_URLS[getEnv()] || API_BASE_URLS.development; + } + + function getConfig() { + return { + env: getEnv(), + apiBaseUrl: getApiBaseUrl(), + clientId: CLIENT_ID, + tenantId: TENANT_ID, + tokenKey: TOKEN_KEY, + tokenHeaderName: TOKEN_HEADER_NAME + }; + } + + return { + getConfig: getConfig, + getApiBaseUrl: getApiBaseUrl, + getClientId: function () { return CLIENT_ID; }, + getTenantId: function () { return TENANT_ID; }, + getTokenKey: function () { return TOKEN_KEY; }, + getTokenHeaderName: function () { return TOKEN_HEADER_NAME; } + }; +}); +``` + +- [ ] **Step 4: Write utilities test** + +Create or replace `tests/utils.test.js` with focused utility assertions: + +```js +const assert = require('assert'); +const StorageUtil = require('../utils/StorageUtil.js'); +const FormUtil = require('../utils/FormUtil.js'); +const RequestUtil = require('../utils/RequestUtil.js'); + +assert.strictEqual(FormUtil.trimOrUndefined(' 张三 '), '张三'); +assert.strictEqual(FormUtil.trimOrUndefined(' '), undefined); +assert.strictEqual(FormUtil.toNumberOrUndefined('12'), 12); +assert.strictEqual(FormUtil.toNumberOrUndefined(''), undefined); +assert.strictEqual(FormUtil.toBoolean('on'), true); +assert.strictEqual(FormUtil.toBoolean('0'), false); + +const store = {}; +const storage = { + getItem: (key) => Object.prototype.hasOwnProperty.call(store, key) ? store[key] : null, + setItem: (key, value) => { store[key] = String(value); }, + removeItem: (key) => { delete store[key]; } +}; +StorageUtil.write(storage, 'token', 'abc'); +assert.strictEqual(StorageUtil.read(storage, 'token'), 'abc'); +StorageUtil.remove(storage, 'token'); +assert.strictEqual(StorageUtil.read(storage, 'token'), null); + +const calls = []; +const requester = RequestUtil.createRequester({ + baseUrl: 'https://test-genealogy-api.ddxcjp.cn', + clientId: 'pc-client', + tokenHeaderName: 'Authorization', + getToken: () => 'token-x', + fetchImpl: async (url, options) => { + calls.push({ url, options }); + return { + ok: true, + status: 200, + json: async () => ({ code: 200, data: { ok: true } }) + }; + } +}); + +requester('GET', '/genealogy/pc/auth/profile', { + query: { keyword: '汤 氏' } +}).then((data) => { + assert.deepStrictEqual(data, { ok: true }); + assert.strictEqual(calls[0].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/profile?keyword=%E6%B1%A4+%E6%B0%8F'); + assert.strictEqual(calls[0].options.headers.clientid, 'pc-client'); + assert.strictEqual(calls[0].options.headers.Authorization, 'Bearer token-x'); + console.log('utils tests passed'); +}); +``` + +- [ ] **Step 5: Implement utility files** + +Implement the utilities with Chinese comments around storage try/catch, FormData detection, JSON response parsing and `code !== 200` error creation. `RequestUtil.createRequester` must omit `Authorization` when `requestOptions.auth === false`. + +- [ ] **Step 6: Run utility tests** + +Run: `node tests\utils.test.js` +Expected: PASS with `utils tests passed`. + +--- + +### Task 2: API 客户端收紧到 PC YAML `paths` + +**Files:** +- Modify: `public/js/api-client.js` +- Test: `tests/api-client.test.js` + +**Interfaces:** +- Consumes: `GenealogyConfig` +- Consumes: `StorageUtil` +- Consumes: `RequestUtil.createRequester` +- Produces: `GenealogyApi.createClient(options)` +- Produces: `GenealogyApi.defaultClient` +- Produces PC methods: + - `login(body)`, `register(body)`, `loginBySms(body)`, `sendSmsCode(body)` + - `getProfile()`, `updateProfile(body)`, `changePassword(body)`, `resetPassword(body)`, `changePhone(body)`, `deactivateAccount(body)`, `logout()` + - `uploadFile(formData)`, `initResumableUpload(body)`, `uploadChunk(formData)`, `completeResumableUpload(body)`, `bindFileReference(body)`, `releaseFileReference(query)` + - `getRegionChildren(parentCode)`, `getRegionPath(regionCode)`, `searchRegions(query)`, `getRegion(regionCode)` + - `captchaRequire(query)`, `captchaChallenge(body)`, `captchaVerify(body)`, `legacyCaptcha()` +- Produces unsupported method behavior: methods for PC YAML 未覆盖业务 throw `Error` with `code === 'PC_API_NOT_AVAILABLE'` and no network request. + +- [ ] **Step 1: Replace API client test** + +Rewrite `tests/api-client.test.js` so it verifies only PC-confirmed paths and unsupported APP-era methods: + +```js +const assert = require('assert'); +const GenealogyApi = require('../public/js/api-client.js'); + +const calls = []; +const client = GenealogyApi.createClient({ + baseUrl: 'https://test-genealogy-api.ddxcjp.cn', + clientId: 'client-x', + tenantId: 'tenant-x', + tokenKey: 'token-key', + tokenHeaderName: 'Authorization', + storage: { getItem: () => 'abc-token' }, + fetchImpl: async (url, options) => { + calls.push({ url, options }); + return { + ok: true, + status: 200, + json: async () => ({ code: 200, data: { url } }) + }; + } +}); + +(async () => { + await client.login({ tenantId: 'tenant-x', phone: '13800000000', password: 'md5' }); + assert.strictEqual(calls[0].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/login'); + assert.strictEqual(calls[0].options.headers.clientid, 'client-x'); + assert.strictEqual(calls[0].options.headers.Authorization, undefined); + + await client.getProfile(); + assert.strictEqual(calls[1].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/profile'); + assert.strictEqual(calls[1].options.headers.Authorization, 'Bearer abc-token'); + + await client.updateProfile({ nickName: '张三', provinceCode: '510000', cityCode: '510100', districtCode: '510104' }); + assert.strictEqual(calls[2].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/profile'); + assert.strictEqual(calls[2].options.method, 'PUT'); + + await client.uploadFile(new FormData()); + assert.strictEqual(calls[3].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/files/upload'); + + await client.getRegionChildren('51'); + assert.strictEqual(calls[4].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/region/children?parentCode=51'); + + await client.captchaRequire({ sceneCode: 'WEB_H5_LOGIN', subject: '13800000000' }); + assert.strictEqual(calls[5].url, 'https://test-genealogy-api.ddxcjp.cn/captcha/require?tenantId=tenant-x&clientId=client-x&sceneCode=WEB_H5_LOGIN&subject=13800000000'); + assert.strictEqual(calls[5].options.headers.Authorization, undefined); + + assert.throws( + () => client.listGenealogies(), + (error) => error.code === 'PC_API_NOT_AVAILABLE' + ); + + assert.strictEqual(calls.length, 6); + console.log('api-client tests passed'); +})(); +``` + +- [ ] **Step 2: Run API client test** + +Run: `node tests\api-client.test.js` +Expected before implementation: FAIL while old APP paths or old scene tests remain. +Expected after implementation: PASS with `api-client tests passed`. + +- [ ] **Step 3: Implement confirmed PC methods** + +In `public/js/api-client.js`, remove APP auth/files paths. Map methods exactly: + +```js +login: function (body) { + return request('POST', '/genealogy/pc/auth/login', { body: body, auth: false }); +}, +register: function (body) { + return request('POST', '/genealogy/pc/auth/register', { body: body, auth: false }); +}, +sendSmsCode: function (body) { + return request('POST', '/genealogy/pc/auth/sms/code', { body: body, auth: false }); +}, +loginBySms: function (body) { + return request('POST', '/genealogy/pc/auth/login/sms', { body: body, auth: false }); +}, +getProfile: function () { + return request('GET', '/genealogy/pc/auth/profile'); +}, +updateProfile: function (body) { + return request('PUT', '/genealogy/pc/auth/profile', { body: body }); +} +``` + +Add the remaining PC auth/files/region/captcha methods with the same direct mapping from YAML `paths`. + +- [ ] **Step 4: Implement unsupported method guard** + +For old business methods whose PC paths do not exist, keep the method name only if current pages call it, but route it to: + +```js +function createUnavailableMethod(name) { + return function () { + var error = new Error('PC 接口文档未提供该业务接口:' + name); + error.code = 'PC_API_NOT_AVAILABLE'; + throw error; + }; +} +``` + +Use a Chinese comment above the mapping: `// PC YAML 未提供这些业务 paths,保留方法名只用于阻止旧 APP 路径继续发请求。` + +- [ ] **Step 5: Verify no APP auth/files network paths remain** + +Run: `Select-String -Path public\js\api-client.js -Pattern '/genealogy/app/auth|/genealogy/app/files'` +Expected: no matches. + +- [ ] **Step 6: Run API client test again** + +Run: `node tests\api-client.test.js` +Expected: PASS with `api-client tests passed`. + +--- + +### Task 3: 登录验证码与 `public/tac` 调用 + +**Files:** +- Modify: `public/js/auth-pages.js` +- Modify: `public/js/captcha-pages.js` +- Modify: `login.html` +- Modify: `register.html` +- Modify: `forgot-password.html` +- Test: `tests/auth-pages.test.js` +- Test: `tests/captcha-pages.test.js` + +**Interfaces:** +- Consumes: `public/tac/css/tac.css` +- Consumes: `public/tac/js/tac.min.js` +- Consumes: `GenealogyApi.defaultClient.captchaRequire/challenge/verify` +- Produces: `AuthPages.getCaptchaScene('login') === 'WEB_H5_LOGIN'` +- Produces: `AuthPages.getCaptchaScene('register') === 'WEB_H5_REGISTER'` +- Produces: `AuthPages.getCaptchaScene('password-reset') === 'WEB_H5_FORGOT_PASSWORD'` +- Produces: `CaptchaPages.buildChallengeBody(options, api)` +- Produces: `CaptchaPages.buildVerifyBody(options, api)` +- Produces: `CaptchaPages.ensureToken(form, options)` + +- [ ] **Step 1: Replace auth scene tests** + +In `tests/auth-pages.test.js`, require these expectations: + +```js +assert.strictEqual(AuthPages.getCaptchaScene('login'), 'WEB_H5_LOGIN'); +assert.strictEqual(AuthPages.getCaptchaScene('register'), 'WEB_H5_REGISTER'); +assert.strictEqual(AuthPages.getCaptchaScene('password-reset'), 'WEB_H5_FORGOT_PASSWORD'); +``` + +- [ ] **Step 2: Replace captcha tests** + +In `tests/captcha-pages.test.js`, all challenge/verify body tests must use `WEB_H5_LOGIN`: + +```js +assert.deepStrictEqual(CaptchaPages.buildChallengeBody({ + sceneCode: 'WEB_H5_LOGIN', + subject: '13800000000' +}, fakeApi), { + tenantId: 'tenant-x', + clientId: 'client-x', + sceneCode: 'WEB_H5_LOGIN', + subject: '13800000000' +}); +``` + +- [ ] **Step 3: Run auth and captcha tests** + +Run: + +```powershell +node tests\auth-pages.test.js +node tests\captcha-pages.test.js +``` + +Expected before implementation: FAIL if `PC_LOGIN` or `PC_REGISTER` remains. +Expected after implementation: PASS. + +- [ ] **Step 4: Implement scene mapping** + +In `public/js/auth-pages.js`: + +```js +function getCaptchaScene(formType) { + // PC/H5 认证场景来自后台验证码配置,客户端 Key 为 web_pc。 + var map = { + login: 'WEB_H5_LOGIN', + register: 'WEB_H5_REGISTER', + 'password-reset': 'WEB_H5_FORGOT_PASSWORD' + }; + + return map[formType] || ''; +} +``` + +If `sceneCode` is empty, `ensureCaptcha` must return `true` without calling `/captcha/require` and must not fabricate `validToken`. + +- [ ] **Step 5: Verify TAC assets in auth pages** + +Run: + +```powershell +Select-String -Path login.html,register.html,forgot-password.html -Pattern 'public/tac/css/tac.css|public/tac/js/tac.min.js|public/js/captcha-pages.js|name="validToken"|data-captcha-box' +``` + +Expected: +- `login.html` has TAC CSS, TAC JS, `captcha-pages.js`, hidden `validToken`, and `[data-captcha-box]`. +- `register.html` and `forgot-password.html` may keep TAC assets and hidden fields, but no PC/H5 scene is configured until backend confirms scene code. + +--- + +### Task 4: 认证与资料页面只接 PC auth schema + +**Files:** +- Modify: `public/js/auth-pages.js` +- Modify: `public/js/profile-pages.js` +- Modify: `public/js/security-pages.js` +- Modify: `login.html` +- Modify: `register.html` +- Modify: `forgot-password.html` +- Modify: `profile.html` +- Modify: `profile-data.html` +- Modify: `profile-security.html` +- Test: `tests/auth-pages.test.js` +- Test: `tests/profile-pages.test.js` +- Test: `tests/security-pages.test.js` + +**Interfaces:** +- Consumes: `GenealogyApi.defaultClient` +- Produces request bodies: + - `PasswordLoginBody`: `grantType/tenantId/phone/password/validToken` + - `PasswordRegisterBody`: `grantType/tenantId/phone/password/nickName/registerSource/validToken` + - `PasswordResetBody`: `tenantId/phone/smsCode/newPassword/validToken` + - `ProfileUpdateBody`: `nickName/avatarOssId/sex/birthday/provinceCode/cityCode/districtCode` + +- [ ] **Step 1: Update body-builder tests** + +`tests/auth-pages.test.js` must assert `registerSource: 'PC'` for register body and no `APP_*` scene in any body. + +`tests/profile-pages.test.js` must assert profile update uses `provinceCode/cityCode/districtCode`, not `regionCode/addressDetail`. + +- [ ] **Step 2: Run focused tests** + +Run: + +```powershell +node tests\auth-pages.test.js +node tests\profile-pages.test.js +node tests\security-pages.test.js +``` + +Expected before implementation: FAIL where old scene/body fields remain. +Expected after implementation: PASS. + +- [ ] **Step 3: Implement auth body builders** + +Use PC YAML schema fields only. For register: + +```js +function buildRegisterBody(values, config) { + var body = { + grantType: 'password', + tenantId: config.tenantId, + phone: values.phone, + password: values.password, + registerSource: 'PC' + }; + + if (values.nickName) body.nickName = values.nickName; + if (values.validToken) body.validToken = values.validToken; + return body; +} +``` + +- [ ] **Step 4: Implement profile body builder** + +Use schema fields: + +```js +function buildProfileUpdateBody(values) { + return removeEmpty({ + nickName: values.nickName, + avatarOssId: toNumberOrUndefined(values.avatarOssId), + sex: values.sex, + birthday: values.birthday, + provinceCode: values.provinceCode, + cityCode: values.cityCode, + districtCode: values.districtCode + }); +} +``` + +- [ ] **Step 5: Verify page script order** + +Run: + +```powershell +Select-String -Path login.html,register.html,forgot-password.html,profile.html,profile-data.html,profile-security.html -Pattern 'config.js|utils/StorageUtil.js|utils/FormUtil.js|utils/RequestUtil.js|public/js/api-client.js' +``` + +Expected: every page that uses `GenealogyApi` loads config and utils before `api-client.js`. + +--- + +### Task 5: 文件上传和文件引用只切 PC files + +**Files:** +- Modify: `public/js/upload-pages.js` +- Modify: upload-related page scripts that call `GenealogyApi.defaultClient.uploadFile` +- Test: `tests/upload-pages.test.js` +- Test: `tests/api-client.test.js` + +**Interfaces:** +- Consumes: `GenealogyApi.defaultClient.uploadFile(formData)` +- Consumes: `GenealogyApi.defaultClient.initResumableUpload(body)` +- Consumes: `GenealogyApi.defaultClient.uploadChunk(formData)` +- Consumes: `GenealogyApi.defaultClient.completeResumableUpload(body)` +- Consumes: `GenealogyApi.defaultClient.bindFileReference(body)` +- Consumes: `GenealogyApi.defaultClient.releaseFileReference(query)` + +- [ ] **Step 1: Add upload tests** + +`tests/upload-pages.test.js` must verify: +- single upload calls `/genealogy/pc/files/upload` +- resumable init calls `/genealogy/pc/files/resumable/init` +- file reference body requires `bizType/bizTable/bizId/bizField` + +- [ ] **Step 2: Run upload tests** + +Run: `node tests\upload-pages.test.js` +Expected before implementation: FAIL if old APP file paths remain. +Expected after implementation: PASS. + +- [ ] **Step 3: Implement upload mapping** + +Only map file capability. Do not submit article/feed/album/ceremony/growth/memo business forms unless PC YAML provides their business endpoints. + +- [ ] **Step 4: Verify no APP file path remains** + +Run: `Select-String -Path public\js\api-client.js,public\js\upload-pages.js -Pattern '/genealogy/app/files'` +Expected: no matches. + +--- + +### Task 6: 行政区划接口和资料地区字段 + +**Files:** +- Modify: `public/js/region-pages.js` +- Modify: `public/js/profile-pages.js` +- Test: `tests/region-pages.test.js` +- Test: `tests/profile-pages.test.js` + +**Interfaces:** +- Consumes: `GET /genealogy/region/children` +- Consumes: `GET /genealogy/region/path/{regionCode}` +- Consumes: `GET /genealogy/region/search` +- Consumes: `GET /genealogy/region/{regionCode}` +- Produces profile fields: `provinceCode/cityCode/districtCode` + +- [ ] **Step 1: Add region tests** + +`tests/region-pages.test.js` must assert: +- empty parent loads province list with no required parent code. +- search requires `keyword`. +- selected province/city/district writes `provinceCode/cityCode/districtCode` to the form. + +- [ ] **Step 2: Run region tests** + +Run: `node tests\region-pages.test.js` +Expected before implementation: FAIL if form still expects `regionCode/addressDetail`. +Expected after implementation: PASS. + +- [ ] **Step 3: Implement region mapping** + +Use `GenealogyApi.defaultClient.getRegionChildren`, `getRegionPath`, `searchRegions`, `getRegion`. Do not create genealogy submit behavior here. + +--- + +### Task 7: PC YAML 未覆盖页面降级为接口待确认 + +**Files:** +- Modify: page scripts that currently call APP-era business methods: + - `public/js/genealogy-pages.js` + - `public/js/join-apply-pages.js` + - `public/js/member-admin-pages.js` + - `public/js/article-pages.js` + - `public/js/feed-pages.js` + - `public/js/album-pages.js` + - `public/js/ceremony-pages.js` + - `public/js/growth-pages.js` + - `public/js/memo-pages.js` + - `public/js/generation-pages.js` + - `public/js/lineage-pages.js` + - `public/js/notification-pages.js` + - `public/js/help-pages.js` + - `public/js/promotion-pages.js` + - `public/js/feedback-pages.js` + - `public/js/vip-pages.js` +- Test: `tests/unsupported-pages.test.js` + +**Interfaces:** +- Consumes: `PC_API_NOT_AVAILABLE` errors from `GenealogyApi` +- Produces: visible or console-safe page state that says PC interface is not configured, without issuing APP requests. + +- [ ] **Step 1: Add unsupported-page smoke test** + +Create `tests/unsupported-pages.test.js` to load each page script in Node with a fake client method that throws `{ code: 'PC_API_NOT_AVAILABLE' }`. Assert each script handles the error without rethrowing from its init function. + +- [ ] **Step 2: Run unsupported-page test** + +Run: `node tests\unsupported-pages.test.js` +Expected before implementation: FAIL for scripts that assume APP methods are valid. +Expected after implementation: PASS. + +- [ ] **Step 3: Implement shared page fallback** + +In each affected page script, catch `PC_API_NOT_AVAILABLE` and render a small existing-style empty state. Add a Chinese comment: `// PC YAML 未提供该业务接口,页面先展示接口待确认状态。` + +- [ ] **Step 4: Verify no APP business path is emitted from API client** + +Run: `Select-String -Path public\js\api-client.js -Pattern '/genealogy/app/'` +Expected: no network path matches. + +--- + +### Task 8: 追踪表和执行记录 + +**Files:** +- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` + +**Interfaces:** +- Consumes: Task 1-7 verification results. +- Produces: updated status rows with exact timestamps, changed files, interface paths and verification commands. + +- [ ] **Step 1: Mark task start before edits** + +Before executing each PC task, update the row status from `待开始` to `进行中` with exact time from: + +```powershell +Get-Date -Format "yyyy-MM-dd HH:mm:ss zzz" +``` + +- [ ] **Step 2: Mark task completion after verification** + +After the task's verification commands pass, update status to `已完成` and record: +- completion time +- modified files +- interface paths +- verification commands +- verification result + +- [ ] **Step 3: Keep missing interfaces in coverage matrix** + +Any page whose main business path is absent from `genealogy-pc-openapi.yaml` remains in `PC YAML 未覆盖` or `接口待确认` and must not be promoted to full integration. + +--- + +## Verification Gate + +Run these before marking the replan execution complete: + +```powershell +node tests\config.test.js +node tests\utils.test.js +node tests\api-client.test.js +node tests\auth-pages.test.js +node tests\captcha-pages.test.js +node tests\profile-pages.test.js +node tests\security-pages.test.js +node tests\upload-pages.test.js +node tests\region-pages.test.js +node tests\unsupported-pages.test.js +Get-ChildItem -Path public\js -Filter *.js | Where-Object { $_.Name -notin @('jquery360.js','jquery.min.js') } | Sort-Object Name | ForEach-Object { node --check $_.FullName } +Select-String -Path public\js\api-client.js -Pattern '/genealogy/app/' +Select-String -Path public\js\auth-pages.js,public\js\captcha-pages.js,tests\auth-pages.test.js,tests\captcha-pages.test.js -Pattern 'PC_LOGIN|PC_REGISTER|PC_PASSWORD_RESET|APP_LOGIN|APP_REGISTER|APP_FORGOT_PASSWORD' +git diff --check +``` + +Expected: +- All listed Node tests pass. +- Business JS syntax check passes. +- `api-client.js` contains no `/genealogy/app/` network path. +- Auth and captcha code/tests contain no stale APP/old PC scene defaults. +- `git diff --check` passes, or if this directory is not a Git repository, record the exact output. + +## Self-Review + +- Spec coverage: this plan covers latest PC YAML `paths`, test domain, config.js, utils/public-js separation, CSS/no style injection, Chinese comments, TAC login scene, profile schema mismatch, and PC-missing business interfaces. +- Placeholder scan: no implementation task asks the worker to invent missing PC endpoints or use APP paths as compatibility. +- Type consistency: `GenealogyConfig`, `StorageUtil`, `FormUtil`, `RequestUtil`, `GenealogyApi.createClient`, `AuthPages.getCaptchaScene`, and `CaptchaPages.ensureToken` names are consistent across tasks. diff --git a/docs/superpowers/plans/2026-07-09-pc-auth-three-pages.md b/docs/superpowers/plans/2026-07-09-pc-auth-three-pages.md new file mode 100644 index 0000000..3968db3 --- /dev/null +++ b/docs/superpowers/plans/2026-07-09-pc-auth-three-pages.md @@ -0,0 +1,127 @@ +# PC Auth Three Pages Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 按 `genealogy-pc-openapi.yaml` 和 Apifox 截图,把登录、注册、忘记密码三页与 PC 认证接口、验证中心接口对齐。 + +**Architecture:** 根目录 `config.js` 继续作为环境、`clientid`、`tenantId` 的唯一配置入口;`utils` 继续存放跨页面公共工具;页面业务逻辑集中在 `public/js/auth-pages.js`,接口调用集中在 `public/js/api-client.js`,验证码适配集中在 `public/js/captcha-pages.js`。页面差异样式只写入对应 CSS 文件。 + +**Tech Stack:** 原生 HTML/CSS/JavaScript、`public/tac` 滑动验证、Node 断言测试。 + +## Global Constraints + +- 接口依据只使用 `genealogy-pc-openapi.yaml` 与用户补充的 Apifox 截图,不再使用 `APP.openapi.json`。 +- 开发环境接口地址为 `https://test-genealogy-api.ddxcjp.cn`。 +- PC `clientid` 为 `ced7e5f0498645c6ec642dcf450b036f`,租户 ID 为 `000000`。 +- 登录验证码场景为 `WEB_H5_LOGIN`,注册验证码场景为 `WEB_H5_REGISTER`,忘记密码验证码场景为 `WEB_H5_FORGOT_PASSWORD`。 +- 不在 JS 中注入样式;页面差异样式写入对应 CSS 文件,公共样式写入 `public/css/public.css`。 +- 新增或修改的 HTML、CSS、JS 注释使用中文,代码保持可读,不压缩。 + +--- + +### Task 1: 记录专项计划 + +**Files:** +- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` +- Create: `docs/superpowers/plans/2026-07-09-pc-auth-three-pages.md` + +**Interfaces:** +- Consumes: 用户确认“可以”先做登录、注册、忘记密码三页。 +- Produces: PC-009 专项计划记录。 + +- [x] **Step 1: 写入本计划** + +创建本计划,明确页面、接口、验证码场景、样式和验证约束。 + +- [x] **Step 2: 写入追踪记录** + +在追踪文档中新增 `PC-009`,状态为“进行中”,开始时间为 `2026-07-09 16:32:17 +08:00`。 + +### Task 2: 登录页补齐短信登录 + +**Files:** +- Modify: `login.html` +- Modify: `public/css/login.css` +- Modify: `public/js/auth-pages.js` +- Modify: `tests/auth-pages.test.js` +- Modify: `tests/api-client.test.js` + +**Interfaces:** +- Consumes: `ApiClient.login(body)`、`ApiClient.loginBySms(body)`、`ApiClient.sendSmsCode(body)`、`CaptchaPages.ensureToken(form, options)`。 +- Produces: 登录页同时支持密码登录和短信登录。 + +- [x] **Step 1: 页面增加登录方式切换** + +在 `login.html` 的登录表单内增加模式切换按钮,密码登录区域保留 `phone/password/validToken`,短信登录区域增加 `phone/smsCode/validToken` 和发送验证码按钮。 + +- [x] **Step 2: CSS 写入登录页专属样式** + +在 `public/css/login.css` 增加模式切换、隐藏区域、验证码行样式,不在 JS 中写样式。 + +- [x] **Step 3: JS 支持短信登录提交** + +在 `public/js/auth-pages.js` 增加 `buildSmsLoginBody(values)`,提交时按当前模式调用 `/genealogy/pc/auth/login` 或 `/genealogy/pc/auth/login/sms`。 + +- [x] **Step 4: 发送短信复用场景码** + +登录页短信验证码按钮使用 `data-scene-code="WEB_H5_LOGIN"`,忘记密码页继续使用 `WEB_H5_FORGOT_PASSWORD`。 + +- [x] **Step 5: 更新测试** + +`tests/auth-pages.test.js` 增加短信登录 body 断言;`tests/api-client.test.js` 增加 `loginBySms` 和 `sendSmsCode` 路径/入参断言。 + +### Task 3: 注册和忘记密码复核 + +**Files:** +- Verify: `register.html` +- Verify: `forgot-password.html` +- Verify: `public/js/auth-pages.js` + +**Interfaces:** +- Consumes: `/genealogy/pc/auth/register`、`/genealogy/pc/auth/sms/code`、`/genealogy/pc/auth/password/reset`。 +- Produces: 注册和忘记密码页面确认继续按 PC 字段提交。 + +- [x] **Step 1: 注册页字段复核** + +确认注册页只提交 `phone/password/nickName/validToken`,`grantType/tenantId/clientId/registerSource` 由 `api-client.js` 统一补齐。 + +- [x] **Step 2: 忘记密码页字段复核** + +确认忘记密码页提交 `tenantId/phone/smsCode/newPassword/validToken`,不再出现邮箱找回文案。 + +### Task 4: 专项验证和完成记录 + +**Files:** +- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` + +**Interfaces:** +- Consumes: Task 2 和 Task 3 的实现结果。 +- Produces: PC-009 完成记录和验证结果。 + +- [x] **Step 1: 运行测试** + +运行: + +```powershell +node tests\auth-pages.test.js +node tests\api-client.test.js +node tests\captcha-pages.test.js +``` + +期望全部输出 `passed`。 + +- [x] **Step 2: 运行语法检查** + +运行: + +```powershell +node --check public\js\auth-pages.js +node --check public\js\api-client.js +node --check public\js\captcha-pages.js +``` + +期望无输出且退出码为 0。 + +- [x] **Step 3: 更新追踪文档** + +把 `PC-009` 状态改为“已完成”,写入完成时间、修改文件、接口路径、验证码场景和验证结果。 diff --git a/docs/superpowers/plans/2026-07-09-tac-captcha-auth.md b/docs/superpowers/plans/2026-07-09-tac-captcha-auth.md new file mode 100644 index 0000000..1783a91 --- /dev/null +++ b/docs/superpowers/plans/2026-07-09-tac-captcha-auth.md @@ -0,0 +1,95 @@ +# TAC Captcha Auth Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 将 `public/tac` 滑动验证接入登录、注册、忘记密码三个认证页面,并按 `APP.openapi.json` 的验证中心接口换取 `validToken` 后再提交业务接口。 + +**Architecture:** 新增 `public/js/captcha-pages.js` 作为 TAC 与验证中心接口之间的适配层;`auth-pages.js` 在提交登录、注册、发送短信、重置密码前调用验证码适配层。API 基础地址、clientId、tenantId 继续由 `api-client.js` 统一提供。 + +**Tech Stack:** 静态 HTML、原生 JS、现有 `public/tac/js/tac.min.js`、现有 `public/tac/css/tac.css`、Node 单元测试。 + +## Global Constraints + +- 严格依据 `APP.openapi.json` 的 `/captcha/require`、`/captcha/challenge`、`/captcha/verify` 字段接入。 +- 业务 JS 不压缩,新增注释使用中文。 +- 自有样式写入 CSS 文件,不通过自有 JS 注入样式。 +- 第三方 `tac.min.js` 保持原样,不重写其内部样式和 DOM 生成逻辑。 +- 登录、注册、忘记密码三页都必须有隐藏 `validToken` 字段和验证码挂载容器。 + +--- + +### Task 1: API Client Captcha Helpers + +**Files:** +- Modify: `public/js/api-client.js` +- Modify: `tests/api-client.test.js` + +**Interfaces:** +- Produces: `client.buildApiUrl(path, query)` +- Produces: `client.captchaRequirement({ sceneCode, subject })` + +- [ ] **Step 1: Write failing tests** for captcha requirement URL, query, and public URL building. +- [ ] **Step 2: Run tests** and confirm missing methods fail. +- [ ] **Step 3: Implement minimal API client helpers**. +- [ ] **Step 4: Run tests** and confirm pass. + +### Task 2: TAC Adapter Module + +**Files:** +- Create: `public/js/captcha-pages.js` +- Create: `tests/captcha-pages.test.js` + +**Interfaces:** +- Produces: `CaptchaPages.buildChallengeBody(options, api)` +- Produces: `CaptchaPages.adaptChallengeResponse(response)` +- Produces: `CaptchaPages.buildVerifyBody(requestData, context, api)` +- Produces: `CaptchaPages.extractValidToken(response)` +- Produces: `CaptchaPages.ensureToken(form, options)` + +- [ ] **Step 1: Write failing tests** for OpenAPI-to-TAC mapping and validToken extraction. +- [ ] **Step 2: Run tests** and confirm module missing fails. +- [ ] **Step 3: Implement adapter with Chinese comments**. +- [ ] **Step 4: Run tests** and confirm pass. + +### Task 3: Auth Flow Integration + +**Files:** +- Modify: `public/js/auth-pages.js` +- Modify: `tests/auth-pages.test.js` + +**Interfaces:** +- Consumes: `CaptchaPages.ensureToken(form, { sceneCode, subject })` +- Updates: login/register/password reset/send code flows wait for captcha before API submission. + +- [ ] **Step 1: Write failing tests** for login body validToken and scene code mapping. +- [ ] **Step 2: Run tests** and confirm expected failure. +- [ ] **Step 3: Integrate captcha gate before submit/send code**. +- [ ] **Step 4: Run tests** and confirm pass. + +### Task 4: HTML/CSS Wiring + +**Files:** +- Modify: `login.html` +- Modify: `register.html` +- Modify: `forgot-password.html` +- Modify: `public/css/public.css` + +**Interfaces:** +- Adds: `` +- Adds: hidden `validToken` +- Adds: `data-captcha-box` +- Adds: `public/tac/js/tac.min.js` and `public/js/captcha-pages.js` + +- [ ] **Step 1: Add page hooks** with Chinese HTML comments where helpful. +- [ ] **Step 2: Add CSS-only layout for captcha container**. +- [ ] **Step 3: Run static checks** for scripts, hooks, no self JS style injection. + +### Task 5: Verification And Records + +**Files:** +- Modify: `docs/api-page-integration-2026-07-09.md` + +- [ ] **Step 1: Run all Node tests**. +- [ ] **Step 2: Run `node --check` for all business JS**. +- [ ] **Step 3: Run static checks for HTML hooks and style-injection patterns**. +- [ ] **Step 4: Append dated progress record**. diff --git a/download.html b/download.html new file mode 100644 index 0000000..88fb31e --- /dev/null +++ b/download.html @@ -0,0 +1,104 @@ + + + + + + 下载应用 - 代代相传 + + + + + +
+
+
+
+
Download
+

下载代代相传 APP

+

+ 手机端适合日常上传宣传相册、发布宣传视频、查看家谱和补充资料。 +

+ +
+
+
扫码
+

手机扫码下载

+

支持 iOS 与 Android,实际项目可替换为真实下载二维码。

+
+
+
+
+
+
+

下载方式

+

前台先展示下载入口,后续可以接真实应用商店地址。

+
+ +
+
下载推广内容加载中
+
+
+
+
+ + + + + + diff --git a/family-detail.html b/family-detail.html new file mode 100644 index 0000000..5a6c439 --- /dev/null +++ b/family-detail.html @@ -0,0 +1,138 @@ + + + + + + 四川武胜汤氏族 - 家谱详情 - 代代相传 + + + + + + +
+
+
+
+
Family Detail
+

四川武胜汤氏族

+

+ 公开展示家族源流、世系概览、字辈排行、谱文资料和宣传相册,让访客快速理解这一本家谱的内容结构。 +

+ +
+
+ 公开家谱 +

汤氏族谱

+
+ 128成员 + 6世代 + 18谱文 + 7相册 +
+
+
+
+ +
+
+ +
+
+

家族简介

+

+ 本谱以四川武胜汤氏族人为主体,整理始祖源流、迁徙线索、字辈排行、重要人物与家风故事。公开内容用于展示家族文化,完整成员资料需申请加入后查看。 +

+
+
+
+

世系概览

+

按代际关系梳理成员结构,帮助族人找到自己的位置。

+
+
+

谱文资料

+

收录谱序、家训、修谱说明和公开纪念文章。

+
+
+

宣传相册

+

展示祠堂祖屋、修谱活动、老照片和家族纪念内容。

+
+
+
+
+
+
+ + + + + + + diff --git a/forgot-password.html b/forgot-password.html new file mode 100644 index 0000000..ef58688 --- /dev/null +++ b/forgot-password.html @@ -0,0 +1,77 @@ + + + + + + 找回密码 - 代代相传 + + + + + + +
+ +
+
Password Recovery
+

找回密码

+

输入注册手机号,获取验证码后重设登录密码。

+
+ +
+ + +
+ + + + +
+
+
+ +
+ + + + + + + + + + + + + + diff --git a/genealogy-pc-openapi.yaml b/genealogy-pc-openapi.yaml new file mode 100644 index 0000000..9bbb366 --- /dev/null +++ b/genealogy-pc-openapi.yaml @@ -0,0 +1,1484 @@ +openapi: 3.0.3 +info: + title: Genealogy PC/H5 API + version: 1.0.0 + description: | + 家谱业务 APP/PC 用户侧接口文档,用于 Apifox 导入。 + + 导入方式:Apifox -> 导入 API 数据 -> OpenAPI/Swagger -> 选择本文件。 + + 约定: + - APP 路径前缀为 `/genealogy/app` + - PC/H5 路径前缀为 `/genealogy/pc` + - 后台管理接口不包含在本文件内 + - 登录后接口需要携带 token,且请求 Header 需携带 `clientid` +servers: + - url: http://127.0.0.1:8080 + description: Local Java backend + - url: http://127.0.0.1:9527/dev-api + description: Local frontend proxy +tags: + - name: 验证中心 + description: 统一验证码、天爱行为验证码和系统图形验证码 + - name: 认证登录 + description: APP/PC 登录注册与账号资料 + - name: 文件上传 + description: APP/PC 统一文件上传与业务引用绑定 + - name: 行政区划 + description: 行政区划 + - name: 家谱 + description: 家谱主体、加入申请与概览 + - name: 家谱成员 + description: 家谱成员 + - name: 字辈谱 + description: 字辈谱 + - name: 世系人物 + description: 世系人物与世系树 + - name: 家族圈 + description: 家族圈动态、点赞、评论 + - name: 内容文章 + description: 谱文、官网文章、帮助、推广 + - name: 相册 + description: 相册与照片 + - name: 祭祀 + description: 祭祀与献礼 + - name: 族务记录 + description: 成长、备忘、亲友、功德等记录 + - name: VIP + description: VIP 套餐与订单 + - name: 消息通知 + description: 消息通知 + - name: 意见反馈 + description: 意见反馈 +security: + - SaToken: [] +paths: + /captcha/require: + get: + tags: [验证中心] + summary: 查询当前场景是否需要验证 + description: 用于登录、注册、换绑手机等场景。按租户、客户端和场景编码匹配后台验证策略。 + security: [] + parameters: + - name: tenantId + description: 租户ID,用于区分不同租户的数据和验证策略 + in: query + schema: + type: string + example: "000000" + - name: clientId + description: 客户端ID,对应后台客户端管理中的APP/PC/小程序等终端 + in: query + schema: + type: string + example: 428a8310cd442757ae699df5d894f051 + - name: sceneCode + description: 验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER + in: query + required: true + schema: + type: string + example: APP_LOGIN + - name: subject + description: 验证主体,通常为手机号、用户ID或登录账号 + in: query + schema: + type: string + example: "13800000000" + responses: + '200': + $ref: '#/components/responses/VerificationRequireResult' + /captcha/challenge: + post: + tags: [验证中心] + summary: 生成验证挑战 + description: 统一生成验证码。策略为天爱时返回行为验证数据;策略为系统图形时返回 uuid 和 img。 + security: [] + requestBody: + $ref: '#/components/requestBodies/VerificationChallenge' + responses: + '200': + $ref: '#/components/responses/VerificationChallengeResult' + /captcha/verify: + post: + tags: [验证中心] + summary: 校验验证结果并换取 validToken + description: 验证通过后返回 validToken,登录、注册、发短信等业务接口将 validToken 随请求体一起提交。 + security: [] + requestBody: + $ref: '#/components/requestBodies/VerificationCheck' + responses: + '200': + $ref: '#/components/responses/VerificationCheckResult' + /auth/code: + get: + tags: [验证中心] + summary: 兼容旧系统图形验证码 + description: 老版后台登录图形验证码入口。新的 APP/PC 业务优先使用 /captcha/require + /captcha/challenge + /captcha/verify。 + security: [] + responses: + '200': + $ref: '#/components/responses/LegacyCaptchaResult' + /genealogy/pc/auth/register: + post: + tags: [认证登录] + summary: PC 用户注册 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + $ref: '#/components/requestBodies/PasswordRegister' + responses: + '200': + $ref: '#/components/responses/LoginResult' + /genealogy/pc/auth/login: + post: + tags: [认证登录] + summary: PC 密码登录 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + $ref: '#/components/requestBodies/PasswordLogin' + responses: + '200': + $ref: '#/components/responses/LoginResult' + /genealogy/pc/auth/login/sms: + post: + tags: [认证登录] + summary: PC 短信登录 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + $ref: '#/components/requestBodies/SmsLogin' + responses: + '200': + $ref: '#/components/responses/LoginResult' + /genealogy/pc/auth/sms/code: + post: + tags: [认证登录] + summary: PC 发送短信验证码 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + $ref: '#/components/requestBodies/SmsCode' + responses: + '200': + $ref: '#/components/responses/VoidResult' + /genealogy/pc/auth/profile: + get: + tags: [认证登录] + summary: PC 当前用户资料 + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + responses: + '200': + $ref: '#/components/responses/ObjectResult' + put: + tags: [认证登录] + summary: PC 修改用户资料 + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + $ref: '#/components/requestBodies/ProfileUpdate' + responses: + '200': + $ref: '#/components/responses/ObjectResult' + /genealogy/pc/auth/password: + put: + tags: [认证登录] + summary: PC 修改密码 + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + $ref: '#/components/requestBodies/PasswordChange' + responses: + '200': + $ref: '#/components/responses/VoidResult' + /genealogy/pc/auth/password/reset: + put: + tags: [认证登录] + summary: PC 找回密码 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + $ref: '#/components/requestBodies/PasswordReset' + responses: + '200': + $ref: '#/components/responses/VoidResult' + /genealogy/pc/auth/phone: + put: + tags: [认证登录] + summary: PC 换绑手机号 + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + $ref: '#/components/requestBodies/PhoneChange' + responses: + '200': + $ref: '#/components/responses/ObjectResult' + /genealogy/pc/auth/account/deactivate: + post: + tags: [认证登录] + summary: PC 注销账号 + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + $ref: '#/components/requestBodies/AccountDeactivate' + responses: + '200': + $ref: '#/components/responses/VoidResult' + /genealogy/pc/auth/logout: + delete: + tags: [认证登录] + summary: PC 退出登录 + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + responses: + '200': + $ref: '#/components/responses/VoidResult' + + /genealogy/pc/files/upload: + post: + tags: [文件上传] + summary: PC 单文件上传 + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + $ref: '#/components/requestBodies/FileUpload' + responses: + '200': + $ref: '#/components/responses/FileUploadResult' + /genealogy/pc/files/resumable/init: + post: + tags: [文件上传] + summary: PC 分片上传初始化 + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + $ref: '#/components/requestBodies/ResumableInit' + responses: + '200': + $ref: '#/components/responses/ObjectResult' + /genealogy/pc/files/resumable/chunk: + post: + tags: [文件上传] + summary: PC 上传分片 + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + $ref: '#/components/requestBodies/ChunkUpload' + responses: + '200': + $ref: '#/components/responses/VoidResult' + /genealogy/pc/files/resumable/complete: + post: + tags: [文件上传] + summary: PC 完成分片上传 + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + $ref: '#/components/requestBodies/ResumableComplete' + responses: + '200': + $ref: '#/components/responses/FileUploadResult' + /genealogy/pc/files/reference: + post: + tags: [文件上传] + summary: PC 绑定文件业务引用 + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + $ref: '#/components/requestBodies/FileReference' + responses: + '200': + $ref: '#/components/responses/VoidResult' + delete: + tags: [文件上传] + summary: PC 释放文件业务引用 + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + - $ref: '#/components/parameters/BizTable' + - $ref: '#/components/parameters/BizId' + - $ref: '#/components/parameters/BizField' + responses: + '200': + $ref: '#/components/responses/VoidResult' + + /genealogy/region/children: + get: + tags: [行政区划] + summary: 查询下级行政区划 + security: [] + parameters: + - name: parentCode + description: 父级行政区划编码,不传或传0查省级 + in: query + required: false + schema: + type: string + example: "51" + responses: + '200': + $ref: '#/components/responses/ListResult' + /genealogy/region/path/{regionCode}: + get: + tags: [行政区划] + summary: 查询行政区划路径 + security: [] + parameters: + - $ref: '#/components/parameters/RegionCode' + responses: + '200': + $ref: '#/components/responses/ListResult' + /genealogy/region/search: + get: + tags: [行政区划] + summary: 搜索行政区划 + security: [] + parameters: + - name: keyword + description: 搜索关键词,支持地区名称或编码模糊查询 + in: query + required: true + schema: + type: string + example: 北京 + - name: level + description: 行政区划级别,1省、2市、3区县、4乡镇街道、5村社区 + in: query + required: false + schema: + type: integer + example: 3 + - name: limit + description: 返回数量上限 + in: query + required: false + schema: + type: integer + example: 20 + responses: + '200': + $ref: '#/components/responses/ListResult' + /genealogy/region/{regionCode}: + get: + tags: [行政区划] + summary: 查询行政区划详情 + security: [] + parameters: + - $ref: '#/components/parameters/RegionCode' + responses: + '200': + $ref: '#/components/responses/ObjectResult' + +components: + securitySchemes: + SaToken: + type: apiKey + in: header + name: Authorization + description: 登录后返回的 token。若本地配置使用 sa-token 名称,请按项目实际 token-name 调整。 + parameters: + ClientIdHeader: + name: clientid + description: 客户端ID,请求Header携带,用于区分APP/PC/小程序等终端 + in: header + required: true + schema: + type: string + example: 428a8310cd442757ae699df5d894f051 + PageNum: + name: pageNum + description: 页码,从1开始 + in: query + required: false + schema: + type: integer + default: 1 + PageSize: + name: pageSize + description: 每页条数 + in: query + required: false + schema: + type: integer + default: 10 + GenealogyId: + name: genealogyId + description: 家谱ID + in: path + required: true + schema: + type: integer + format: int64 + example: 900001001 + MemberId: + name: memberId + description: 家谱成员ID + in: path + required: true + schema: + type: integer + format: int64 + PersonId: + name: personId + description: 世系人物ID + in: path + required: true + schema: + type: integer + format: int64 + FeedId: + name: feedId + description: 家族动态ID + in: path + required: true + schema: + type: integer + format: int64 + RegionCode: + name: regionCode + description: 行政区划编码 + in: path + required: true + schema: + type: string + example: "510000" + BizTable: + name: bizTable + description: 业务表名,用于文件引用绑定 + in: query + required: true + schema: + type: string + example: gen_family_feed + BizId: + name: bizId + description: 业务数据ID,用于文件引用绑定 + in: query + required: true + schema: + type: integer + format: int64 + example: 900013001 + BizField: + name: bizField + description: 业务字段名,例如avatar_oss_id、cover_oss_id + in: query + required: true + schema: + type: string + example: media_ids + requestBodies: + JsonObject: + required: true + content: + application/json: + schema: + type: object + additionalProperties: true + PasswordRegister: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PasswordRegisterBody' + example: + grantType: "password" + tenantId: "000000" + phone: "13800000000" + password: "e10adc3949ba59abbe56e057f20f883e" + nickName: "测试用户" + registerSource: "app" + validToken: "captcha-ticket-or-sms-token" + PasswordLogin: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PasswordLoginBody' + example: + grantType: "password" + tenantId: "000000" + phone: "13800000000" + password: "e10adc3949ba59abbe56e057f20f883e" + validToken: "captcha-ticket" + SmsLogin: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SmsLoginBody' + example: + grantType: "sms" + tenantId: "000000" + phone: "13800000000" + smsCode: "123456" + validToken: "captcha-ticket" + SmsCode: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SmsCodeBody' + example: + tenantId: "000000" + phone: "13800000000" + sceneCode: "APP_LOGIN" + validToken: "captcha-ticket" + ProfileUpdate: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProfileUpdateBody' + example: + nickName: "张三" + avatarOssId: 2060000000000000001 + sex: "0" + birthday: "1990-01-01" + regionCode: "110101" + addressDetail: "北京市东城区" + PasswordChange: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PasswordChangeBody' + example: + oldPassword: "e10adc3949ba59abbe56e057f20f883e" + newPassword: "25d55ad283aa400af464c76d713c07ad" + validToken: "captcha-ticket" + PasswordReset: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PasswordResetBody' + example: + tenantId: "000000" + phone: "13800000000" + smsCode: "123456" + newPassword: "25d55ad283aa400af464c76d713c07ad" + validToken: "captcha-ticket" + PhoneChange: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PhoneChangeBody' + example: + newPhone: "13900000000" + smsCode: "123456" + validToken: "captcha-ticket" + AccountDeactivate: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AccountDeactivateBody' + example: + password: "e10adc3949ba59abbe56e057f20f883e" + reason: "user request" + FileUpload: + required: true + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: + type: string + format: binary + ChunkUpload: + required: true + content: + multipart/form-data: + schema: + type: object + required: [uploadId, chunkIndex, chunkMd5, file] + properties: + uploadId: + type: string + chunkIndex: + type: integer + example: 0 + chunkMd5: + type: string + example: d41d8cd98f00b204e9800998ecf8427e + file: + type: string + format: binary + ResumableInit: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ResumableInitBody' + example: + fileName: "cover.jpg" + fileSize: 9532 + fileMd5: "d41d8cd98f00b204e9800998ecf8427e" + contentType: "image/jpeg" + chunkSize: 4194304 + totalChunks: 1 + bizType: "genealogy" + usageScene: "cover" + ResumableComplete: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ResumableCompleteBody' + example: + uploadId: "UPLOAD202607090001" + fileMd5: "d41d8cd98f00b204e9800998ecf8427e" + fileSize: 9532 + FileReference: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/FileReferenceBody' + example: + bizType: "family_feed" + bizName: "family feed image" + bizTable: "gen_family_feed" + bizId: 900013001 + bizField: "media_oss_ids" + ossId: 2060000000000000001 + usageScene: "feed_image" + GenerationPoemBatch: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/GenerationPoemBatchBody' + example: + genealogyId: 900001001 + poemText: "德承家亦\n忠厚传芳" + disableMissing: false + LineagePerson: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/LineagePersonBody' + example: + appUserId: 900000001 + personNo: "P202607090001" + personName: "彭德明" + aliasName: "德明" + sex: "0" + generationNo: 1 + generationName: "德" + avatarOssId: 2060000000000000001 + birthDate: "1950-01-01" + deathDate: null + introduction: "first generation introduction" + FamilyFeed: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/FamilyFeedBody' + example: + content: "今天上传一张老照片。" + mediaOssIds: "2060000000000000001,2060000000000000002" + visibility: "1" + status: "0" + FamilyFeedComment: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/FamilyFeedCommentBody' + example: + parentCommentId: null + replyUserId: null + content: "这张照片很有年代感。" + VerificationChallenge: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/VerificationChallengeBody' + example: + tenantId: "000000" + clientId: "428a8310cd442757ae699df5d894f051" + sceneCode: "APP_LOGIN" + subject: "13800000000" + VerificationCheck: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/VerificationCheckBody' + examples: + tianai_behavior: + summary: 天爱滑块/旋转/滑动还原/文字点选验证 + value: + tenantId: "000000" + clientId: "428a8310cd442757ae699df5d894f051" + sceneCode: "APP_LOGIN" + subject: "13800000000" + challengeId: "CAPTCHA_CHALLENGE_ID" + providerCode: "tianai" + captchaType: "SLIDER" + payload: + id: "tianai-captcha-id" + data: + bgImageWidth: 340 + bgImageHeight: 180 + startSlidingTime: 1720000000000 + endSlidingTime: 1720000001500 + trackList: [] + system_image: + summary: 系统图形验证码 + value: + tenantId: "000000" + clientId: "428a8310cd442757ae699df5d894f051" + sceneCode: "APP_LOGIN" + subject: "13800000000" + challengeId: "CAPTCHA_CHALLENGE_ID" + providerCode: "ruoyi_image" + captchaType: "math" + payload: + uuid: "captcha-uuid" + code: "6" + responses: + VoidResult: + description: 通用成功响应 + content: + application/json: + schema: + $ref: '#/components/schemas/RVoid' + ObjectResult: + description: 通用对象响应 + content: + application/json: + schema: + $ref: '#/components/schemas/RObject' + ListResult: + description: 通用列表响应 + content: + application/json: + schema: + $ref: '#/components/schemas/RList' + PageResult: + description: 通用分页响应 + content: + application/json: + schema: + $ref: '#/components/schemas/PageResult' + LoginResult: + description: 登录结果 + content: + application/json: + schema: + $ref: '#/components/schemas/RLogin' + FileUploadResult: + description: 文件上传结果 + content: + application/json: + schema: + $ref: '#/components/schemas/RFileUpload' + VerificationRequireResult: + description: 验证需求查询结果 + content: + application/json: + schema: + $ref: '#/components/schemas/RVerificationRequire' + VerificationChallengeResult: + description: 验证挑战生成结果 + content: + application/json: + schema: + $ref: '#/components/schemas/RVerificationChallenge' + VerificationCheckResult: + description: 验证校验结果 + content: + application/json: + schema: + $ref: '#/components/schemas/RVerificationCheck' + LegacyCaptchaResult: + description: 兼容旧图形验证码结果 + content: + application/json: + schema: + $ref: '#/components/schemas/RLegacyCaptcha' + schemas: + RVoid: + type: object + properties: + code: + description: 系统图形验证码或响应状态码 + type: integer + example: 200 + msg: + description: 响应消息 + type: string + example: 操作成功 + data: + description: 响应数据主体 + nullable: true + RObject: + type: object + properties: + code: + description: 系统图形验证码或响应状态码 + type: integer + example: 200 + msg: + description: 响应消息 + type: string + example: 操作成功 + data: + description: 响应数据主体 + type: object + additionalProperties: true + RList: + type: object + properties: + code: + description: 系统图形验证码或响应状态码 + type: integer + example: 200 + msg: + description: 响应消息 + type: string + example: 操作成功 + data: + description: 响应数据主体 + type: array + items: + type: object + additionalProperties: true + PageResult: + type: object + properties: + code: + description: 系统图形验证码或响应状态码 + type: integer + example: 200 + msg: + description: 响应消息 + type: string + example: 操作成功 + rows: + description: 分页数据列表 + type: array + items: + type: object + additionalProperties: true + total: + type: integer + example: 0 + RLogin: + type: object + properties: + code: + description: 系统图形验证码或响应状态码 + type: integer + example: 200 + msg: + description: 响应消息 + type: string + example: 操作成功 + data: + description: 响应数据主体 + $ref: '#/components/schemas/LoginVo' + RFileUpload: + type: object + properties: + code: + description: 系统图形验证码或响应状态码 + type: integer + example: 200 + msg: + description: 响应消息 + type: string + example: 操作成功 + data: + description: 响应数据主体 + $ref: '#/components/schemas/FileUploadVo' + LoginVo: + type: object + properties: + token: + type: string + accessToken: + type: string + tokenValue: + type: string + userId: + type: integer + format: int64 + tenantId: + description: 租户ID,用于区分不同租户的数据和验证策略 + type: string + clientId: + description: 客户端ID,对应后台客户端管理中的APP/PC/小程序等终端 + type: string + clientKey: + type: string + FileUploadVo: + type: object + properties: + ossId: + description: OSS文件ID,表单内部使用,用户侧应通过上传组件获取 + type: integer + format: int64 + url: + type: string + thumbnailUrl: + type: string + nullable: true + fileName: + description: 原始文件名 + type: string + originalName: + type: string + PasswordRegisterBody: + type: object + required: [grantType, tenantId, phone, password] + properties: + clientId: + description: 客户端ID,对应后台客户端管理中的APP/PC/小程序等终端 + type: string + grantType: + description: 授权类型,例如password、sms、wechat、apple + type: string + example: password + tenantId: + description: 租户ID,用于区分不同租户的数据和验证策略 + type: string + example: "000000" + phone: + description: 手机号 + type: string + example: "13800000000" + password: + type: string + description: 32 位 MD5 + example: e10adc3949ba59abbe56e057f20f883e + nickName: + description: 用户昵称 + type: string + registerSource: + description: 注册来源,例如APP、PC、H5、MINI_PROGRAM + type: string + validToken: + description: 验证中心通过后返回的票据,用于注册/登录/换绑等二次校验 + type: string + PasswordLoginBody: + type: object + required: [grantType, tenantId, phone, password] + properties: + clientId: + description: 客户端ID,对应后台客户端管理中的APP/PC/小程序等终端 + type: string + grantType: + description: 授权类型,例如password、sms、wechat、apple + type: string + example: password + tenantId: + description: 租户ID,用于区分不同租户的数据和验证策略 + type: string + example: "000000" + phone: + description: 手机号 + type: string + example: "13800000000" + password: + type: string + description: 32 位 MD5 + example: e10adc3949ba59abbe56e057f20f883e + validToken: + description: 验证中心通过后返回的票据,用于注册/登录/换绑等二次校验 + type: string + SmsLoginBody: + type: object + required: [grantType, tenantId, phone, smsCode] + properties: + clientId: + description: 客户端ID,对应后台客户端管理中的APP/PC/小程序等终端 + type: string + grantType: + description: 授权类型,例如password、sms、wechat、apple + type: string + example: sms + tenantId: + description: 租户ID,用于区分不同租户的数据和验证策略 + type: string + example: "000000" + phone: + description: 手机号 + type: string + example: "13800000000" + smsCode: + description: 短信验证码 + type: string + example: "1234" + SmsCodeBody: + type: object + required: [grantType, tenantId, sceneCode, phone, validToken] + properties: + clientId: + description: 客户端ID,对应后台客户端管理中的APP/PC/小程序等终端 + type: string + grantType: + description: 授权类型,例如password、sms、wechat、apple + type: string + example: sms + tenantId: + description: 租户ID,用于区分不同租户的数据和验证策略 + type: string + example: "000000" + sceneCode: + description: 验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER + type: string + example: APP_LOGIN + phone: + description: 手机号 + type: string + example: "13800000000" + validToken: + description: 验证中心通过后返回的票据,用于注册/登录/换绑等二次校验 + type: string + ProfileUpdateBody: + type: object + properties: + nickName: + description: 用户昵称 + type: string + avatarOssId: + description: 头像文件OSS ID + type: integer + format: int64 + sex: + description: 性别,建议使用系统字典值 + type: string + birthday: + description: 生日,格式 yyyy-MM-dd + type: string + format: date + provinceCode: + type: string + cityCode: + type: string + districtCode: + type: string + PasswordChangeBody: + type: object + required: [oldPassword, newPassword] + properties: + oldPassword: + type: string + description: 32 位 MD5 + newPassword: + type: string + description: 32 位 MD5 + PasswordResetBody: + type: object + required: [tenantId, phone, smsCode, newPassword] + properties: + tenantId: + description: 租户ID,用于区分不同租户的数据和验证策略 + type: string + example: "000000" + phone: + description: 手机号 + type: string + smsCode: + description: 短信验证码 + type: string + newPassword: + type: string + description: 32 位 MD5 + validToken: + description: 验证中心通过后返回的票据,用于注册/登录/换绑等二次校验 + type: string + PhoneChangeBody: + type: object + required: [newPhone, smsCode] + properties: + newPhone: + type: string + smsCode: + description: 短信验证码 + type: string + validToken: + description: 验证中心通过后返回的票据,用于注册/登录/换绑等二次校验 + type: string + AccountDeactivateBody: + type: object + required: [password] + properties: + password: + type: string + description: 32 位 MD5 + reason: + description: 操作原因或说明 + type: string + ResumableInitBody: + type: object + required: [fileName, fileSize, fileMd5, chunkSize, totalChunks] + properties: + fileName: + description: 原始文件名 + type: string + fileSize: + description: 文件大小,单位字节 + type: integer + format: int64 + fileMd5: + description: 完整文件MD5,用于秒传和完整性校验 + type: string + contentType: + description: 文件MIME类型,例如 image/jpeg、video/mp4 + type: string + chunkSize: + description: 单个分片大小,单位字节 + type: integer + example: 4194304 + totalChunks: + description: 总分片数 + type: integer + bizType: + description: 业务类型,例如avatar、cover、feed_image + type: string + usageScene: + description: 文件使用场景,便于后续引用统计和清理 + type: string + ResumableCompleteBody: + type: object + required: [uploadId, fileMd5, fileSize] + properties: + uploadId: + description: 分片上传任务ID + type: string + fileMd5: + description: 完整文件MD5,用于秒传和完整性校验 + type: string + fileSize: + description: 文件大小,单位字节 + type: integer + format: int64 + FileReferenceBody: + type: object + required: [bizType, bizTable, bizId, bizField] + properties: + bizType: + description: 业务类型,例如avatar、cover、feed_image + type: string + example: family_feed + bizName: + description: 业务名称,用于后台展示文件引用来源 + type: string + example: 家族圈动态 + bizTable: + description: 业务表名,用于文件引用绑定 + type: string + example: gen_family_feed + bizId: + description: 业务数据ID,用于文件引用绑定 + type: integer + format: int64 + bizField: + description: 业务字段名,例如avatar_oss_id、cover_oss_id + type: string + example: media_ids + ossId: + description: OSS文件ID,表单内部使用,用户侧应通过上传组件获取 + type: integer + format: int64 + ossIds: + type: string + example: "2060001,2060002" + usageScene: + description: 文件使用场景,便于后续引用统计和清理 + type: string + usageName: + type: string + GenerationPoemBatchBody: + type: object + properties: + content: + type: string + example: 德承家亦 + stopMissingOldGeneration: + type: boolean + example: false + LineagePersonBody: + type: object + properties: + appUserId: + type: integer + format: int64 + personNo: + type: string + personName: + type: string + aliasName: + type: string + sex: + description: 性别,建议使用系统字典值 + type: string + example: "0" + generationNo: + description: 世代序号 + type: integer + generationName: + description: 字辈 + type: string + avatarOssId: + description: 头像文件OSS ID + type: integer + format: int64 + birthDate: + type: string + deathDate: + type: string + introduction: + type: string + FamilyFeedBody: + type: object + properties: + content: + type: string + mediaOssIds: + description: 媒体文件OSS ID列表,多个用逗号或数组传递以接口约定为准 + type: string + example: "2060001,2060002" + visibility: + description: 可见范围,例如0私密、1公开、2成员可见 + type: string + example: "1" + status: + description: 状态,建议使用对应字典值 + type: string + example: "0" + FamilyFeedCommentBody: + type: object + required: [content] + properties: + parentCommentId: + type: integer + format: int64 + nullable: true + replyUserId: + type: integer + format: int64 + nullable: true + content: + type: string + VerificationChallengeBody: + type: object + required: [sceneCode] + properties: + tenantId: + type: string + description: 租户 ID + clientId: + type: string + description: 客户端 ID + sceneCode: + type: string + description: 验证场景编码,如 ADMIN_LOGIN/APP_LOGIN/APP_REGISTER + subject: + type: string + description: 验证主体,如手机号/用户名/IP + example: + tenantId: "000000" + clientId: "428a8310cd442757ae699df5d894f051" + sceneCode: "APP_LOGIN" + subject: "13800000000" + VerificationCheckBody: + type: object + required: [sceneCode] + properties: + tenantId: + description: 租户ID,用于区分不同租户的数据和验证策略 + type: string + clientId: + description: 客户端ID,对应后台客户端管理中的APP/PC/小程序等终端 + type: string + sceneCode: + description: 验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER + type: string + subject: + description: 验证主体,通常为手机号、用户ID或登录账号 + type: string + challengeId: + description: 验证挑战ID,由验证挑战接口返回 + type: string + providerCode: + type: string + description: 验证服务,tianai 或 ruoyi_image + captchaType: + type: string + description: SLIDER/ROTATE/CONCAT/WORD_IMAGE_CLICK/math/char + payload: + type: object + additionalProperties: true + description: 前端验证控件返回的轨迹/点击/图形验证码数据 + VerificationRequireVo: + type: object + properties: + required: + description: 当前场景是否需要验证 + type: boolean + providerCode: + description: 第三方服务商编码 + type: string + captchaType: + description: 验证码类型,例如SLIDER、ROTATE、CONCAT、WORD_IMAGE_CLICK + type: string + sceneCode: + description: 验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER + type: string + ttlSeconds: + description: 验证或票据有效秒数 + type: integer + example: + required: true + providerCode: "tianai" + captchaType: "SLIDER" + sceneCode: "APP_LOGIN" + ttlSeconds: 300 + VerificationChallengeVo: + type: object + properties: + required: + description: 当前场景是否需要验证 + type: boolean + providerCode: + description: 第三方服务商编码 + type: string + captchaType: + description: 验证码类型,例如SLIDER、ROTATE、CONCAT、WORD_IMAGE_CLICK + type: string + challengeId: + description: 验证挑战ID,由验证挑战接口返回 + type: string + uuid: + type: string + description: 系统图形验证码 uuid + img: + type: string + description: 系统图形验证码 base64 图片 + payload: + type: object + additionalProperties: true + description: 天爱验证码数据,包括背景图、模板图、尺寸等 + expireSeconds: + description: 验证挑战过期秒数 + type: integer + example: + required: true + providerCode: "tianai" + captchaType: "SLIDER" + challengeId: "CAPTCHA_CHALLENGE_ID" + payload: + backgroundImage: "data:image/png;base64,..." + templateImage: "data:image/png;base64,..." + backgroundImageWidth: 340 + backgroundImageHeight: 180 + expireSeconds: 300 + VerificationCheckVo: + type: object + properties: + passed: + description: 验证是否通过 + type: boolean + validToken: + type: string + description: 验证通过后交给业务接口的票据 + expireSeconds: + description: 验证挑战过期秒数 + type: integer + message: + description: 返回消息 + type: string + example: + passed: true + validToken: "captcha-ticket" + expireSeconds: 300 + message: "验证通过" + LegacyCaptchaVo: + type: object + properties: + captchaEnabled: + type: boolean + uuid: + description: 系统图形验证码UUID + type: string + img: + type: string + description: base64 图形验证码 + example: + captchaEnabled: true + uuid: "captcha-uuid" + img: "data:image/png;base64,..." + RVerificationRequire: + type: object + properties: + code: + description: 系统图形验证码或响应状态码 + type: integer + example: 200 + msg: + description: 响应消息 + type: string + example: "操作成功" + data: + description: 响应数据主体 + $ref: '#/components/schemas/VerificationRequireVo' + RVerificationChallenge: + type: object + properties: + code: + description: 系统图形验证码或响应状态码 + type: integer + example: 200 + msg: + description: 响应消息 + type: string + example: "操作成功" + data: + description: 响应数据主体 + $ref: '#/components/schemas/VerificationChallengeVo' + RVerificationCheck: + type: object + properties: + code: + description: 系统图形验证码或响应状态码 + type: integer + example: 200 + msg: + description: 响应消息 + type: string + example: "操作成功" + data: + description: 响应数据主体 + $ref: '#/components/schemas/VerificationCheckVo' + RLegacyCaptcha: + type: object + properties: + code: + description: 系统图形验证码或响应状态码 + type: integer + example: 200 + msg: + description: 响应消息 + type: string + example: "操作成功" + data: + description: 响应数据主体 + $ref: '#/components/schemas/LegacyCaptchaVo' diff --git a/genealogy.html b/genealogy.html new file mode 100644 index 0000000..69ec7c1 --- /dev/null +++ b/genealogy.html @@ -0,0 +1,156 @@ + + + + + + 数字家谱 - 代代相传 + + + + + + +
+
+
+
+
Digital Genealogy
+

把成员关系整理成清晰可查的数字家谱

+

+ 从始祖、分支、排行到个人资料,数字家谱帮助家族建立长期可维护的成员档案和世系关系。 +

+
+
8资料模块
+
3步快速建谱
+
多人协作共修
+
+
+
+
+
始祖
+
+
+ 长房二房三房 +
+
+
+ 第六世第七世第八世第九世 +
+
+
+
+ +
+
+
+

核心能力

+

+ 围绕修谱、查谱和协作维护,把家族资料从零散记录整理成完整档案。 +

+
+
+
+
+

世系图

+

+ 以树谱方式展示成员关系,支持查看个人资料、添加亲属和调整排行。 +

+
+
+
+

字辈谱

+

记录各代字辈与人数,让世代脉络更加清楚。

+
+
+
+

谱文资料

+

沉淀谱序、家训、人物传记和修谱说明。

+
+
+
+

权限管理

+

设置管理员、内容可见范围与入谱审核,保护家族隐私。

+
+
+
+
+ +
+
+
+

适合多人共修

+

+ 家族成员可以分别补充照片、成员资料、谱文和故事,再由管理员统一审核,避免资料越传越散。 +

+ 开始创建 +
+
+

修谱流程

+
+

1创建家谱并填写姓氏、地区、堂号

+

2录入第一批成员,建立上下辈关系

+

3邀请亲人加入,共同完善资料

+
+
+
+
+
+ + + + + diff --git a/help.html b/help.html new file mode 100644 index 0000000..9874410 --- /dev/null +++ b/help.html @@ -0,0 +1,126 @@ + + + + + + 帮助中心 - 代代相传 + + + + + + +
+
+
+
+
Help Center
+

从创建到协作,快速解决常见问题

+

+ 覆盖注册登录、创建家谱、邀请成员、资料审核、隐私设置和内容发布等流程。 +

+
+ 创建家谱邀请成员隐私权限 +
+
+
+
+
?
+

需要人工协助?

+

+ 遇到资料导入、权限或账号问题,可以提交说明,客服会协助处理。 +

+ 提交工单 +
+
+
+ +
+
+
+

常见问题

+

把复杂流程拆成清楚步骤,便于用户第一次使用时快速上手。

+
+
+
+ 如何创建第一本家谱? +

+ 进入创建家谱页面,填写家族名称、姓氏、地区和简介,即可生成家谱空间。 +

+
+
+ 如何邀请亲人一起维护? +

在家谱管理中生成邀请码,亲人可通过加入家谱页面提交申请。

+
+
+ 公开家谱会展示哪些内容? +

仅展示管理员允许公开的家族介绍、动态和部分资料。

+
+
+ 资料填错了怎么办? +

管理员可以编辑成员资料、调整世系关系,也可以设置审核流程。

+
+
+
+
+
+ + + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..977021a --- /dev/null +++ b/index.html @@ -0,0 +1,625 @@ + + + + + + 代代相传家谱_在线修家谱_家谱查询_姓氏寻根_祠堂祭祀 + + + + + + + + +
+
+
+
+
数字家谱与家族文化门户
+

+ 一部会生长的
数字家谱 +

+

+ 把姓氏源流、世系关系、字辈排行、宣传相册、谱文故事与亲人动态整理在一起,让家族的来处清晰可见,让记忆可以继续传下去。 +

+
+
正在记录
+
+
+
寻根问祖
+
字辈排行
+
世系关系
+
宣传相册
+
谱文故事
+
亲人动态
+
寻根问祖
+
字辈排行
+
世系关系
+
宣传相册
+
谱文故事
+
亲人动态
+
+
+
+
+ 1创建家谱2邀请亲人3共同传承 +
+ +
+
家谱广场浏览公开家族
+
姓氏百科了解姓氏源流
+
家族文化阅读传承故事
+
+
+
+
+
+
+ 家族祠堂 +
+ + 宣传影像 + 观看宣传视频 + +
+

查找公开家谱

+ + 输入姓氏、地区或家谱名称搜索 + +
+
+ 四川成都陈氏族共修入 128 人 +
+
+ 湖广刘氏家谱收录 6 代字辈 +
+
+ 岭南张氏宗谱开放谱文 18 篇 +
+
+
+
+
+
+ + + +
+
+
+

数字家谱核心功能

+
+
+
+
+

世系图

+

+ 以树谱方式展示成员关系,支持查看个人资料、添加亲属、调整排行。 +

+
+
+
+

字辈谱

+

记录各代字辈与人数,让家族世代脉络更加清楚。

+
+ +
+

宣传相册

+

按相册归档宣传影像,展示祖屋、活动、老照片和重要时刻。

+
+
+
+

谱文资料

+

沉淀谱序、谱文、家训、恩荣录等文本内容。

+
+
+
+

家族圈

+

发布动态、分享图片、评论互动,让亲人之间保持连接。

+
+
+
+

功德录

+

记录家族贡献人物与事迹,让善行与纪念被看见。

+
+
+
+

贺礼邀请

+

发布婚礼、生日、升学等家族喜事,通知相关成员。

+
+
+
+

权限管理

+

设置管理员、内容可见范围与入谱审核,保护家族隐私。

+
+
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+
+

姓氏百科

+

+ 以姓氏为入口,整理姓氏源流、名人故事、地区分布、字辈参考和相关家谱。 +

+ 进入姓氏百科 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+ +
+ + + +
+
+
+
+

手机端随时记录,电脑端清晰浏览

+

+ 家族成员可以在手机端上传照片、查看家谱、发布动态;PC + 端适合浏览门户内容、查看公开家谱和进入家谱空间。 +

+ +
+
+
+
+ 小程序 +

扫码体验

+
+
+
+ APP 下载 +

手机查看

+
+
+
+
+
+
+ + + + + diff --git a/join-genealogy.html b/join-genealogy.html new file mode 100644 index 0000000..7ccd1b7 --- /dev/null +++ b/join-genealogy.html @@ -0,0 +1,37 @@ + + + + + + 加入家谱 - 代代相传 + + + + +
+ +
+
Join Family
+

加入家谱

+

输入亲人分享的家谱ID或从详情页带入的链接,提交后等待管理员审核。

+
+
+ +
+ + + + + +
+
+
+ + + + diff --git a/login.html b/login.html new file mode 100644 index 0000000..96ffcaa --- /dev/null +++ b/login.html @@ -0,0 +1,107 @@ + + + + + + 登录 - 代代相传 + + + + + + +
+ +
+
Welcome Back
+

登录账号

+

登录后可创建、加入和维护家谱。

+ + + + +
+
+ +
+ + + + + + + + + + + + + + diff --git a/notice-detail.html b/notice-detail.html new file mode 100644 index 0000000..79be977 --- /dev/null +++ b/notice-detail.html @@ -0,0 +1,114 @@ + + + + + + 平台公告详情 - 代代相传 + + + + + +
+
+
+
+
Notice
+

平台内容公开展示规范说明

+

+ 为了保护家族资料安全,平台仅展示管理员确认公开的家谱介绍、文章、宣传相册和宣传视频。 +

+ +
+
+ +

公开有边界,传承更长久。

+
+
+
+
+
+ +
+

+ 平台前台页面主要用于展示家族文化与公开资料。涉及成员联系方式、详细个人信息、未审核资料等内容,不会在公开页面直接呈现。 +

+

公开内容范围

+

+ 可公开内容包括家族简介、姓氏源流、谱文节选、宣传相册、宣传视频、平台公告和管理员允许展示的纪念内容。 +

+

资料维护建议

+

+ 建议由管理员先整理资料,再邀请亲人补充和校对。需要修改公开范围时,可以在权限设置中调整。 +

+
+ +
+
+
+ + + + + + diff --git a/plaza.html b/plaza.html new file mode 100644 index 0000000..590cfdf --- /dev/null +++ b/plaza.html @@ -0,0 +1,145 @@ + + + + + + 家谱广场 - 代代相传 + + + + + + +
+
+
+
+
Family Plaza
+

浏览公开家谱,寻找同宗线索

+

+ 按姓氏、地区、堂号和家谱名称检索公开家谱,先看示例,再创建自己的家族空间。 +

+
+ 128+公开家谱 + 36地区线索 + 18谱文示例 +
+
+
+
+

查找公开家谱

+
+ 搜索 +
+
+ 公开家谱字辈完整多人共修 +
+
+
+
+ +
+ +
+
+ + + + + + + diff --git a/profile-admin-permissions.html b/profile-admin-permissions.html new file mode 100644 index 0000000..7a4d54e --- /dev/null +++ b/profile-admin-permissions.html @@ -0,0 +1,85 @@ + + + + + + 管理员权限 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Permissions
+
+
+

管理员权限

+

+ 分配全权管理员、全系管理员、支系管理员,以及入谱审核等具体权限。 +

+
+
+
+
+
+
+ +
+
+

权限设置

+
+ +
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+
+ + + + + + + + + diff --git a/profile-album.html b/profile-album.html new file mode 100644 index 0000000..7ec4813 --- /dev/null +++ b/profile-album.html @@ -0,0 +1,142 @@ + + + + + + 家族相册 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Album
+
+
+

家族相册

+

+ 管理相册名称、封面、描述和照片,作为个人中心内的独立相册管理页。 +

+
+
+
+
+
+
+ +
+
+

相册列表

+ +
+
相册加载中...
+
+
+
+

新建 / 编辑相册

+
+ +
+ + +
+
+ + +
+
+
+ + + + + +

未选择文件

+
+
+ + +
+
+
+ + +
+
+
+
+

照片管理

+ +
+
请选择相册查看照片
+
+
+ +
+
+ + + + + +

未选择文件

+
+
+ + +
+
+
+ + +
+
+ +
+
+
+
+
+
+
+ + + + + + + + + + + + + + diff --git a/profile-article-edit.html b/profile-article-edit.html new file mode 100644 index 0000000..7dab71d --- /dev/null +++ b/profile-article-edit.html @@ -0,0 +1,129 @@ + + + + + + 编辑谱文 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Article Editor
+
+
+

编辑谱文

+

+ 使用富文本编辑器撰写谱文、谱序、谱论和恩荣录,支持图片、表格与排版。 +

+
+
+
+
+
+
+ +
+
+

谱文内容

+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+
+ + +
+
+ + + + + +

未选择文件

+
+
+ 取消 +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + diff --git a/profile-article.html b/profile-article.html new file mode 100644 index 0000000..9e473eb --- /dev/null +++ b/profile-article.html @@ -0,0 +1,94 @@ + + + + + + 谱文管理 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Article
+
+
+

谱文管理

+

+ 管理谱文、谱序、谱论、恩荣录等家族文献,支持添加内容、图片与落款。 +

+
+
+
+
+
+
+ +
+
+

谱文列表

+ +
+
谱文加载中...
+
+
+
+

新建谱文

+ +
+
+
+
+
+ + + + + + + + + diff --git a/profile-content.html b/profile-content.html new file mode 100644 index 0000000..7d82355 --- /dev/null +++ b/profile-content.html @@ -0,0 +1,126 @@ + + + + + + 内容发布 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Content Studio
+
+
+

内容发布

+

+ 承接 APP + 家谱主页里的谱文、视频、相册、功德录、贺礼邀请、成长日志、动态和备忘录。 +

+
+ 添加谱文 +
+
+
+
+ +
+
+ + + + + + + + + diff --git a/profile-create-family.html b/profile-create-family.html new file mode 100644 index 0000000..d31d71a --- /dev/null +++ b/profile-create-family.html @@ -0,0 +1,137 @@ + + + + + + 创建家谱 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Create Family
+
+
+

创建家谱

+

+ 个人中心专用创建流程,用于录入姓氏、谱名、堂号、地望、祠堂和访问权限。 +

+
+
+
+
+
+
+ +
+
+

基础信息

+ +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ +
+ + + + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + 返回列表 +
+
+
+
+
+
+
+ + + + + + + + + + + + + + diff --git a/profile-data-reminders.html b/profile-data-reminders.html new file mode 100644 index 0000000..7cbaeac --- /dev/null +++ b/profile-data-reminders.html @@ -0,0 +1,78 @@ + + + + + + 资料提醒 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Data Reminders
+
+
+

资料提醒

+

+ 集中查看成员资料缺项,补齐头像、出生日期、亲属关系和联系方式。 +

+
+
+
+
+
+ +
+
+ + + + + + + diff --git a/profile-data.html b/profile-data.html new file mode 100644 index 0000000..79f09a0 --- /dev/null +++ b/profile-data.html @@ -0,0 +1,212 @@ + + + + + + 个人资料 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Profile Data
+
+
+

个人资料

+

+ 对应 APP 的个人资料、亲属、完善资料、绑定账号和成员激活流程。 +

+
+ 编辑资料 +
+
+
+
+
+ +
+
+

编辑资料

+ +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+

基本资料

+ +
+
+

个人信息

+ +
+
+

现居地区

+ +
+
+ + + +
+
+ +
+
+
+
+

亲属

+
+
+
+

张三

+

父亲 · 已绑定成员资料

+
+ 查看 +
+
+
+

张三

+

父亲 · 未绑定账号

+
+ 邀请激活 +
+
+
+
+
+
+
+ + + + + + + + + + + + + + diff --git a/profile-families.html b/profile-families.html new file mode 100644 index 0000000..2bd6aec --- /dev/null +++ b/profile-families.html @@ -0,0 +1,110 @@ + + + + + + 我的家谱 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Personal Center
+
+
+

我的家谱

+

+ 集中管理你创建或加入的家谱,进入家谱主页后可继续处理世系图、相册、家族圈和成员协作。 +

+
+ 创建家谱 +
+
+
+
+ +
+
+ + + + + + + + + diff --git a/profile-family-admin.html b/profile-family-admin.html new file mode 100644 index 0000000..165f3e2 --- /dev/null +++ b/profile-family-admin.html @@ -0,0 +1,119 @@ + + + + + + 家族管理 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Family Admin
+
+
+

家族管理

+

+ 承接 APP + 里的世代管理、管理员权限、入谱审核和邀请绑定等后台能力。 +

+
+ 添加成员 +
+
+
+
+ +
+
+ + + + + + + + + diff --git a/profile-family-home.html b/profile-family-home.html new file mode 100644 index 0000000..0fef349 --- /dev/null +++ b/profile-family-home.html @@ -0,0 +1,114 @@ + + + + + + 家谱主页 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Family Home
+
+
+

四川武胜汤氏族

+

+ 后台家谱主页,用于进入世系、相册、家族圈、内容管理和成员协作。 +

+
+ 管理家谱 +
+
+
+
+ +
+
+ + + + + + + + + diff --git a/profile-feed-edit.html b/profile-feed-edit.html new file mode 100644 index 0000000..54d583d --- /dev/null +++ b/profile-feed-edit.html @@ -0,0 +1,113 @@ + + + + + + 发布动态 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Feed Editor
+
+
+

发布动态

+

面向家族圈发布图文动态,可设置置顶和精华。

+
+
+
+
+
+
+ +
+
+

动态内容

+
+
+ +
+
+ + + + + +

未选择文件

+
+
+
+ + +
+
+ + +
+
+
+ 取消 +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + diff --git a/profile-feed.html b/profile-feed.html new file mode 100644 index 0000000..8d890d3 --- /dev/null +++ b/profile-feed.html @@ -0,0 +1,108 @@ + + + + + + 发布动态 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Family Feed
+
+
+

发布动态

+

向家族圈发布图文动态,可设置置顶和精华。

+
+
+
+
+
+
+ +
+
+

动态草稿

+ +
+
+

最近动态

+ +
+
动态加载中...
+
+
+
+
+
+
+ + + + + + + + + diff --git a/profile-feedback.html b/profile-feedback.html new file mode 100644 index 0000000..fcb25dd --- /dev/null +++ b/profile-feedback.html @@ -0,0 +1,102 @@ + + + + + + 意见反馈 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Feedback
+
+
+

意见反馈

+

提交产品问题、资料异常和改进建议,个人中心内独立跟踪。

+
+
+
+
+
+
+ +
+
+

提交反馈

+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+ +
+
+
+
+

历史反馈

+
+
+
+

资料导入建议

+

已受理 · 2026.05

+
+ 查看 +
+
+
+
+
+
+
+ + + + + + + + + diff --git a/profile-generation.html b/profile-generation.html new file mode 100644 index 0000000..fe165b1 --- /dev/null +++ b/profile-generation.html @@ -0,0 +1,153 @@ + + + + + + 字辈谱 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Generation
+
+
+

字辈谱

+

管理世代数字辈,查看各代人数,并维护同字辈多个用字。

+
+ +
+
+
+
+
+ +
+
+

批量字辈

+ +
+
+ + +
+ +
+ + + +
+
+
+
批量预览结果将在这里显示
+
+
+
+

字辈列表

+
+
+
+

第 1 代:代

+

1 人

+
+ 编辑 +
+
+
+

第 2 代:顺

+

2 人

+
+ 编辑 +
+
+
+

第 3 代:万

+

3 人

+
+ 编辑 +
+
+
+
+
+
+
+ + + + + + + + + diff --git a/profile-gift-edit.html b/profile-gift-edit.html new file mode 100644 index 0000000..ede430c --- /dev/null +++ b/profile-gift-edit.html @@ -0,0 +1,129 @@ + + + + + + 创建贺礼 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Gift Editor
+
+
+

创建贺礼

+

维护贺礼类型、标题、时间地点、通知人员和邀请正文。

+
+
+
+
+
+
+ +
+
+

贺礼信息

+
+
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ + + + + +

未选择文件

+
+
+ 取消 +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + diff --git a/profile-gift.html b/profile-gift.html new file mode 100644 index 0000000..f1c8cd9 --- /dev/null +++ b/profile-gift.html @@ -0,0 +1,98 @@ + + + + + + 贺礼邀请 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Invitation
+
+
+

贺礼邀请

+

+ 管理婚礼、升学、生日和其他家族贺礼邀请,可进入管理模式删除或编辑。 +

+
+
+
+
+
+
+ +
+
+

邀请列表

+ +
+
贺礼邀请加载中...
+
+
+
+

献礼记录

+ +
+
请选择贺礼查看献礼记录
+
+
+ +
+
+ + +
+
+ + +
+
+
+ + +
+
+ +
+
+
+
+
+
+
+ + + + + + + + + diff --git a/profile-growth-edit.html b/profile-growth-edit.html new file mode 100644 index 0000000..ddf5e69 --- /dev/null +++ b/profile-growth-edit.html @@ -0,0 +1,130 @@ + + + + + + 新建成长日志 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Growth Editor
+
+
+

新建成长日志

+

记录成员成长节点、特殊情况、照片附件和补充资料。

+
+
+
+
+
+
+ +
+
+

日志内容

+
+
+
+ +
+
+ +
+
+
+
+ + +
+
+ + +
+
+
+ +
+
+ + + + + +

未选择文件

+
+
+ 取消 +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + diff --git a/profile-growth.html b/profile-growth.html new file mode 100644 index 0000000..0f06d20 --- /dev/null +++ b/profile-growth.html @@ -0,0 +1,86 @@ + + + + + + 成长日志 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Growth
+
+
+

成长日志

+

记录成员成长节点、特殊情况、继承关系和个人资料补充。

+
+
+
+
+
+
+ +
+
+

日志列表

+ +
+
成长记录加载中...
+
+
+
+

新建成长日志

+ +
+
+
+
+
+ + + + + + + + + diff --git a/profile-invite.html b/profile-invite.html new file mode 100644 index 0000000..b92c42b --- /dev/null +++ b/profile-invite.html @@ -0,0 +1,74 @@ + + + + + + 邀请家人 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Invite
+
+
+

邀请家人

+

生成家谱邀请链接或邀请码,引导亲人加入家谱并激活账号。

+
+ 打开加入页 +
+
+
+
+
+ +
+
+

邀请方式

+
+
+ +

邀请码

+

当前邀请码:加载中...

+
+
+ +

邀请链接

+

邀请链接加载中...

+
+
+
+
+
+
+
+ + + + + + + + + diff --git a/profile-join-family.html b/profile-join-family.html new file mode 100644 index 0000000..b456eab --- /dev/null +++ b/profile-join-family.html @@ -0,0 +1,77 @@ + + + + + + 加入家谱 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Join Family
+
+
+

加入家谱

+

+ 输入亲人分享的邀请码,提交后进入个人中心消息流程等待管理员审核。 +

+
+ 提交新申请 +
+
+
+
+ +
+
+ + + + + + + + + diff --git a/profile-join-review.html b/profile-join-review.html new file mode 100644 index 0000000..ce4c67e --- /dev/null +++ b/profile-join-review.html @@ -0,0 +1,106 @@ + + + + + + 入谱审核 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Review
+
+
+

入谱审核

+

+ 个人中心专用审核页,处理加入家谱、绑定账号和成员资料补全申请。 +

+
+
+
+
+
+ +
+
+ + + + + + + + + diff --git a/profile-memo-edit.html b/profile-memo-edit.html new file mode 100644 index 0000000..8a1ee91 --- /dev/null +++ b/profile-memo-edit.html @@ -0,0 +1,118 @@ + + + + + + 添加备忘 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Memo Editor
+
+
+

添加备忘

+

保存人情往来、家族事务、纪念事项和附件图片。

+
+
+
+
+
+
+ +
+
+

备忘内容

+
+
+ +
+
+ +
+
+ + +
+
+
+ + +
+
+ + + + + +

未选择文件

+
+
+
+ 取消 +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + diff --git a/profile-memo.html b/profile-memo.html new file mode 100644 index 0000000..6ef8474 --- /dev/null +++ b/profile-memo.html @@ -0,0 +1,91 @@ + + + + + + 人情薄 / 备忘录 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Memo
+
+
+

人情薄 / 备忘录

+

保存亲友往来、家族事务、纪念事项和待办提醒。

+
+
+
+
+
+
+ +
+
+

备忘录

+ +
+
备忘录加载中...
+
+
+
+

添加备忘

+ +
+
+
+
+
+ + + + + + + + + diff --git a/profile-merit-edit.html b/profile-merit-edit.html new file mode 100644 index 0000000..cbcdac3 --- /dev/null +++ b/profile-merit-edit.html @@ -0,0 +1,123 @@ + + + + + + 添加功德人 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Merit Editor
+
+
+

添加功德人

+

+ 记录功德人姓名、事迹、时间和图片资料,正文使用富文本编辑器。 +

+
+
+
+
+
+
+ +
+
+

功德资料

+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+
+
+ + +
+
+ + +
+
+
+ 取消 +
+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/profile-merit.html b/profile-merit.html new file mode 100644 index 0000000..ecb662d --- /dev/null +++ b/profile-merit.html @@ -0,0 +1,92 @@ + + + + + + 功德录 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Merit
+
+
+

功德录

+

记录功德人、事迹、时间和相关图片,沉淀家族贡献。

+
+
+
+
+
+
+ +
+
+

功德人

+ +
+
功德记录加载中...
+
+
+
+

添加功德人

+ +
+
+
+
+
+ + + + + + + + + diff --git a/profile-messages.html b/profile-messages.html new file mode 100644 index 0000000..f61d794 --- /dev/null +++ b/profile-messages.html @@ -0,0 +1,117 @@ + + + + + + 消息中心 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Messages
+
+
+

消息中心

+

+ 处理入谱申请、贺礼邀请、资料提醒和系统通知,减少家族协作中的遗漏。 +

+
+ 返回个人中心 +
+
+
+
+
+ +
+
+

待处理

+ +
+
+

系统通知

+
+ + +
+
+
+
+
+
+ + + + + + + + + diff --git a/profile-security.html b/profile-security.html new file mode 100644 index 0000000..5b92280 --- /dev/null +++ b/profile-security.html @@ -0,0 +1,159 @@ + + + + + + 安全设置 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Security
+
+
+

安全设置

+

维护登录密码、手机号、微信快捷登录和账号安全提醒。

+
+
+
+
+
+
+ +
+
+

账号安全

+ +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + 等待提交 +
+
+
+
+

换绑手机号

+ +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + 等待验证码 +
+
+
+
+

短信登录校验

+ +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + 等待提交 +
+
+
+
+

注销账号

+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+
+ + + + + + + + + + + + + + diff --git a/profile-services.html b/profile-services.html new file mode 100644 index 0000000..076a7a3 --- /dev/null +++ b/profile-services.html @@ -0,0 +1,167 @@ + + + + + + 帮助与服务 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Services
+
+
+

帮助与服务

+

+ 整合 APP + “我的”页中的应用推广、意见反馈、安全设置、帮助中心和应用分享。 +

+
+ 提交反馈 +
+
+
+
+ +
+
+ + + + + + + + + diff --git a/profile-share.html b/profile-share.html new file mode 100644 index 0000000..d60c259 --- /dev/null +++ b/profile-share.html @@ -0,0 +1,89 @@ + + + + + + 应用分享 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Share
+
+
+

应用分享

+

邀请好友创建和维护家谱,查看应用下载、分享奖励和推广入口。

+
+
+
+
+
+
+ +
+
+

邀请好友奖励

+
+
+ +

每邀请一位好友

+

分享概览加载中...

+
+
+ +

分享二维码

+

分享链接加载中...

+
+
+
+
+

分享记录

+
+
+
+

本月邀请

+

3 人访问 · 1 人完成注册

+
+ 查看 +
+
+
+
+
+
+
+ + + + + + + + + diff --git a/profile-tree.html b/profile-tree.html new file mode 100644 index 0000000..ae58883 --- /dev/null +++ b/profile-tree.html @@ -0,0 +1,169 @@ + + + + + + 世系图 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Tree Mode
+
+
+

世系图

+

+ 个人中心内的树谱管理视图,用于查看成员关系、添加亲属和搜索成员。 +

+
+ 管理世代 +
+
+
+
+
+ +
+
+

树谱操作

+
+ +
+
+
+

世系树

+ +
+
世系树加载中...
+
+
+
+

成员搜索

+
+ + +
+ +
+
成员加载中...
+
+
+
+

成员详情

+ +
+
请选择一个成员查看资料
+
+
+
+

新增成员

+
+ +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+
+
+
+ + + + + + + + + diff --git a/profile-video.html b/profile-video.html new file mode 100644 index 0000000..ef6f4d0 --- /dev/null +++ b/profile-video.html @@ -0,0 +1,96 @@ + + + + + + 家族视频 - 个人中心 - 代代相传 + + + + + + +
+
+
+
Video
+
+
+

家族视频

+

发布和管理家族视频,支持标题、描述、封面和上传状态维护。

+
+
+
+
+
+
+ +
+
+

视频列表

+ +
+
+

发布视频

+ +
+
+
+
+
+ + + + + + + diff --git a/profile.html b/profile.html new file mode 100644 index 0000000..7403ee5 --- /dev/null +++ b/profile.html @@ -0,0 +1,374 @@ + + + + + + 个人中心 - 代代相传 + + + + + + + +
+
+
+
+
+
鹿
+ +
+
+
Personal Center
+

鹿野 Loyel

+

+ 管理家谱、亲属资料、家族内容和协作消息。APP + 里的核心功能都收纳在这里,适合 PC 端集中处理。 +

+
+ 血亲第二代 顺字辈已绑定账号 +
+
+
+
+
2我的家谱
+
7待处理
+
18已发布内容
+
86%资料完善度
+
+
+
+ +
+
+ + +
+
+
+
+

我的家谱

+

+ 对应 APP + 首页和家谱主页,进入后可查看家谱主页、世系图、相册和家族圈。 +

+
+ 查看全部 + +
+ +
+ +
+
+
+

家族管理

+

+ 把 APP 里的世代、管理员、权限、入谱审核集中到一个管理区。 +

+
+ 进入管理 +
+ +
+ +
+
+
+

内容发布

+

+ 对应 APP + 家谱主页中的谱文、视频、相册、功德录、贺礼邀请、成长日志等内容入口。 +

+
+ 发布中心 +
+ +
+ +
+
+
+
+

个人资料

+

来自 APP 的个人资料、亲属、完善资料和绑定账号流程。

+
+ 编辑资料 +
+
+

绑定账号已绑定

+

姓名鹿野 Loyel

+

出生日期待填写

+

现居地区待填写

+

亲属关系父亲 2 人

+

补充资料待完善 4 项

+
+
+
+
+
+

帮助与服务

+

我的页面中的常用功能。

+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + diff --git a/promo-album.html b/promo-album.html new file mode 100644 index 0000000..7ef98d8 --- /dev/null +++ b/promo-album.html @@ -0,0 +1,100 @@ + + + + + + 宣传相册 - 代代相传 + + + + + +
+
+
+
+
Public Album
+

用公开相册展示家族故事

+

+ 宣传相册用于前台展示祖屋、祠堂、家族活动、老照片和公开纪念内容,帮助访客更直观理解家族文化。 +

+
+ 祖屋影像活动纪念公开展示 +
+
+
+
+
+
+
+
+
+
+
+
+
+

相册分类

+

把适合公开展示的影像整理成专题,让家族主页更有内容层次。

+
+
+ +
+
相册加载中...
+
+
+
+
+
+ + + + + + diff --git a/promo-video.html b/promo-video.html new file mode 100644 index 0000000..f1aba6d --- /dev/null +++ b/promo-video.html @@ -0,0 +1,105 @@ + + + + + + 宣传视频 - 代代相传 + + + + + +
+
+
+
+
Public Video
+

用宣传视频讲述家族文化

+

+ 宣传视频用于前台展示家族活动、祠堂风貌、修谱故事和公开纪念内容,让访问者更快进入家族叙事。 +

+
+ 家族介绍活动纪念修谱故事 +
+
+
+
+

四川成都陈氏族宣传片

+

祠堂、谱文、人物与家风的公开展示。

+
+
+
+
+
+
+

视频专题

+

把适合公开传播的视频整理成栏目,增强家谱主页的展示力。

+
+
+
+

家族介绍

+

概述姓氏源流、家族分布和家风传承。

+
+
+

活动纪念

+

展示祭祖、聚会、修谱仪式等重要时刻。

+
+
+

人物故事

+

以影像记录家族人物、贡献与纪念内容。

+
+
+
+
+
+ + + + diff --git a/public/css/about.css b/public/css/about.css new file mode 100644 index 0000000..59484ab --- /dev/null +++ b/public/css/about.css @@ -0,0 +1,187 @@ +.about-hero .container { + min-height: 440px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.about-tags { + display: flex; + gap: 12px; + margin-top: 28px; + flex-wrap: wrap; +} + +.about-tags span { + padding: 10px 14px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 999px; + color: #6d766f; + background: rgba(255,253,248,.74); + box-shadow: 0 14px 32px rgba(72, 57, 38, .06); + font-weight: 800; + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.about-tags span:hover { + color: #fff; + background: var(--green); + transform: translateY(-3px); +} + +.about-seal { + position: relative; + min-height: 260px; + overflow: hidden; + display: grid; + place-items: center; + text-align: center; + color: #fff; + background: linear-gradient(155deg, var(--green-dark), var(--green)); +} + +.about-seal:before { + content: ""; + position: absolute; + inset: 0; + background: + linear-gradient(rgba(255,255,255,.08) 1px, transparent 1px), + linear-gradient(90deg, rgba(255,255,255,.08) 1px, transparent 1px); + background-size: 34px 34px; + opacity: .34; +} + +.seal-orb { + position: absolute; + right: 76px; + top: 40px; + width: 110px; + height: 110px; + border-radius: 50%; + background: rgba(255,255,255,.16); + box-shadow: 0 0 40px rgba(255,255,255,.1); + animation: sealPulse 4s ease-in-out infinite; +} + +.about-seal span { + position: relative; + z-index: 2; + width: 96px; + height: 96px; + margin-bottom: 20px; + border-radius: 50%; + display: grid; + place-items: center; + color: var(--red); + background: rgba(255,255,255,.94); + font-family: "SimSun", "Songti SC", serif; + font-size: 48px; + font-weight: 800; + transition: transform .24s ease, background .24s ease; +} + +.about-seal:hover span { + transform: rotate(-8deg) scale(1.08); +} + +.about-seal p { + position: relative; + z-index: 2; + margin: 0; + color: rgba(255,255,255,.84); + font-size: 22px; +} + +.values-section { + position: relative; + overflow: hidden; +} + +.values-section:before { + content: ""; + position: absolute; + left: 50%; + top: 58px; + width: 520px; + height: 180px; + border-radius: 50%; + background: radial-gradient(circle, rgba(196, 146, 69, .09), transparent 68%); + transform: translateX(-50%); +} + +.value-card { + min-height: 190px; + position: relative; + overflow: hidden; +} + +.value-card:after { + content: ""; + position: absolute; + left: -42%; + top: 0; + width: 36%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,.54), transparent); + transform: skewX(-18deg); + transition: left .56s ease; +} + +.value-card:hover:after { + left: 112%; +} + +.value-icon { + width: 48px; + height: 48px; + margin-bottom: 18px; + border-radius: 14px; + display: grid; + place-items: center; + color: var(--red); + background: #f7e4dc; + font-family: "SimSun", "Songti SC", serif; + font-size: 22px; + font-weight: 900; + transition: transform .26s ease, background .26s ease, color .26s ease; +} + +.value-card:hover .value-icon { + color: #fff; + background: var(--red); + transform: rotate(-6deg) scale(1.08); +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -6px; +} + +@keyframes sealPulse { + 0%, 100% { opacity: .55; transform: scale(.92); } + 50% { opacity: .9; transform: scale(1.08); } +} + +@media (max-width: 720px) { + .hero-copy { padding-left: 0; } + .hero-copy:before { display: none; } +} diff --git a/public/css/app.css b/public/css/app.css new file mode 100644 index 0000000..9fe9bc5 --- /dev/null +++ b/public/css/app.css @@ -0,0 +1,258 @@ +.app-hero .container { + min-height: 500px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.hero-buttons { + display: flex; + gap: 16px; + flex-wrap: wrap; + margin-top: 28px; +} + +.app-stats { + display: flex; + gap: 12px; + margin-top: 24px; + flex-wrap: wrap; +} + +.app-stats span { + padding: 10px 14px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 999px; + color: #6d766f; + background: rgba(255,253,248,.74); + box-shadow: 0 14px 32px rgba(72, 57, 38, .06); + font-weight: 800; + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.app-stats span:hover { + color: #fff; + background: var(--green); + transform: translateY(-3px); +} + +.app-shot-wrap { + position: relative; + min-height: 390px; + display: flex; + align-items: center; + justify-content: center; + gap: 30px; + overflow: hidden; + background: + radial-gradient(circle at 18% 20%, rgba(200,59,50,.12), transparent 24%), + radial-gradient(circle at 80% 24%, rgba(71,111,99,.16), transparent 30%), + linear-gradient(135deg, #f7efe2, #edf4ef); +} + +.app-shot-wrap:before { + content: ""; + position: absolute; + inset: 22px; + border: 1px solid rgba(224, 211, 189, .62); + border-radius: 22px; + pointer-events: none; +} + +.app-orbit { + position: absolute; + right: 64px; + top: 44px; + width: 96px; + height: 96px; + border-radius: 50%; + background: rgba(196, 146, 69, .18); + animation: appOrbit 4.2s ease-in-out infinite; +} + +.phone-shot { + position: relative; + z-index: 2; + width: 185px; + height: 380px; + object-fit: cover; + object-position: top center; + border: 8px solid #252b28; + border-radius: 28px; + box-shadow: 0 26px 60px rgba(33, 28, 22, .2); + transition: transform .3s ease, box-shadow .3s ease; +} + +.phone-shot.first { + animation: phoneFloatA 4s ease-in-out infinite; +} + +.phone-shot.second { + transform: translateY(28px); + animation: phoneFloatB 4s ease-in-out infinite; +} + +.app-shot-wrap:hover .phone-shot { + box-shadow: 0 34px 70px rgba(33, 28, 22, .26); +} + +.app-feature-section { + position: relative; + overflow: hidden; +} + +.app-feature-section:before { + content: ""; + position: absolute; + left: 50%; + top: 58px; + width: 520px; + height: 180px; + border-radius: 50%; + background: radial-gradient(circle, rgba(196, 146, 69, .09), transparent 68%); + transform: translateX(-50%); +} + +.app-feature-card { + min-height: 190px; + position: relative; + overflow: hidden; +} + +.app-feature-card:after { + content: ""; + position: absolute; + left: -42%; + top: 0; + width: 36%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,.54), transparent); + transform: skewX(-18deg); + transition: left .56s ease; +} + +.app-feature-card:hover:after { + left: 112%; +} + +.app-icon { + width: 48px; + height: 48px; + margin-bottom: 18px; + border-radius: 14px; + display: grid; + place-items: center; + color: var(--red); + background: #f7e4dc; + font-family: "SimSun", "Songti SC", serif; + font-size: 22px; + font-weight: 900; + transition: transform .26s ease, background .26s ease, color .26s ease; +} + +.app-feature-card:hover .app-icon { + color: #fff; + background: var(--red); + transform: rotate(-6deg) scale(1.08); +} + +.app-promotion-section { + /* 应用推广列表承载后端推广内容,不在 JS 中写样式 */ + background: #fffdf8; +} + +.promotion-list { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 18px; +} + +.promotion-card { + overflow: hidden; + border: 1px solid var(--line); + border-radius: 8px; + background: #fffaf0; + box-shadow: 0 18px 42px rgba(57, 48, 36, .08); + transition: transform .24s ease, border-color .24s ease, box-shadow .24s ease; +} + +.promotion-card:hover { + border-color: rgba(196, 146, 69, .42); + box-shadow: 0 24px 54px rgba(57, 48, 36, .12); + transform: translateY(-4px); +} + +.promotion-card img { + width: 100%; + aspect-ratio: 16 / 9; + object-fit: cover; + display: block; + background: #f3eadc; +} + +.promotion-card div { + padding: 18px; +} + +.promotion-card h3 { + margin-bottom: 8px; + color: var(--ink); + font-size: 22px; +} + +.promotion-card p { + margin: 0 0 12px; + color: var(--muted); + line-height: 1.75; +} + +.promotion-card span { + color: var(--red); + font-weight: 900; +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -6px; +} + +@keyframes phoneFloatA { + 0%, 100% { transform: translateY(0) rotate(-1deg); } + 50% { transform: translateY(-8px) rotate(1deg); } +} + +@keyframes phoneFloatB { + 0%, 100% { transform: translateY(28px) rotate(1deg); } + 50% { transform: translateY(18px) rotate(-1deg); } +} + +@keyframes appOrbit { + 0%, 100% { opacity: .5; transform: scale(.92); } + 50% { opacity: 1; transform: scale(1.08); } +} + +@media (max-width: 720px) { + .hero-copy { padding-left: 0; } + .hero-copy:before { display: none; } + .phone-shot.second { display: none; } + .promotion-list { grid-template-columns: 1fr; } +} diff --git a/public/css/article-detail.css b/public/css/article-detail.css new file mode 100644 index 0000000..c808121 --- /dev/null +++ b/public/css/article-detail.css @@ -0,0 +1,153 @@ +.article-hero .container { + min-height: 440px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.article-meta { + display: flex; + gap: 12px; + margin-top: 28px; + flex-wrap: wrap; +} + +.article-meta span { + padding: 10px 14px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 999px; + color: #6d766f; + background: rgba(255,253,248,.74); + font-weight: 800; +} + +.article-cover-detail { + position: relative; + overflow: hidden; + display: grid; + place-items: center; + text-align: center; + color: #fff; + background: + radial-gradient(circle at 72% 24%, rgba(255,255,255,.35) 0 9%, transparent 10%), + linear-gradient(135deg, #e7c89e, #b94135 48%, #143d57); +} + +.article-cover-detail:after { + content: ""; + position: absolute; + left: -40%; + top: 0; + width: 34%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,.3), transparent); + transform: skewX(-18deg); + transition: left .58s ease; +} + +.article-cover-detail:hover:after { left: 112%; } + +.article-cover-detail span { + width: 86px; + height: 86px; + border-radius: 50%; + display: grid; + place-items: center; + color: var(--red); + background: rgba(255,253,248,.94); + font-family: "SimSun", "Songti SC", serif; + font-size: 44px; + font-weight: 800; +} + +.article-cover-detail p { + margin: 18px 0 0; + color: rgba(255,255,255,.9); + font-size: 22px; +} + +.article-layout { + display: grid; + grid-template-columns: minmax(0, 820px) 300px; + gap: 28px; + align-items: start; +} + +.article-content, +.article-side { + border: 1px solid var(--line); + border-radius: 18px; + background: rgba(255,253,248,.82); + box-shadow: 0 20px 54px rgba(57, 48, 36, .08); +} + +.article-content { + padding: 42px; +} + +.article-content p { + color: #5f665f; + font-size: 18px; + line-height: 2; +} + +.article-content h2 { + margin-top: 34px; + font-size: 32px; +} + +.article-side { + position: sticky; + top: 108px; + padding: 24px; +} + +.article-side a { + display: block; + margin-top: 12px; + padding: 14px 16px; + border: 1px solid var(--line); + border-radius: 14px; + color: #6f4a37; + background: #fbf7ed; + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.article-side a:hover { + color: #fff; + background: var(--green); + transform: translateX(5px); +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { --lift: -6px; } + +@media (max-width: 980px) { + .article-layout { grid-template-columns: 1fr; } + .article-side { position: static; } +} + +@media (max-width: 720px) { + .hero-copy { padding-left: 0; } + .hero-copy:before { display: none; } + .article-content { padding: 28px; } +} diff --git a/public/css/create-genealogy.css b/public/css/create-genealogy.css new file mode 100644 index 0000000..d55e10f --- /dev/null +++ b/public/css/create-genealogy.css @@ -0,0 +1,210 @@ +.create-hero .container { + min-height: 440px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.create-tags { + display: flex; + gap: 12px; + margin-top: 28px; + flex-wrap: wrap; +} + +.create-tags span { + padding: 10px 14px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 999px; + color: #6d766f; + background: rgba(255,253,248,.74); + box-shadow: 0 14px 32px rgba(72, 57, 38, .06); + font-weight: 800; + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.create-tags span:hover { + color: #fff; + background: var(--green); + transform: translateY(-3px); +} + +.process-card { + position: relative; + overflow: hidden; + display: grid; + align-content: center; +} + +.process-card:before { + content: ""; + position: absolute; + inset: 0; + background: + linear-gradient(rgba(196,146,69,.06) 1px, transparent 1px), + linear-gradient(90deg, rgba(196,146,69,.06) 1px, transparent 1px); + background-size: 34px 34px; + opacity: .72; +} + +.process-card h3, +.process-card .step-list { + position: relative; + z-index: 2; +} + +.process-orb { + position: absolute; + right: -70px; + top: -80px; + width: 220px; + height: 220px; + border-radius: 50%; + background: radial-gradient(circle, rgba(200, 59, 50, .13), transparent 62%); + animation: createPulse 4s ease-in-out infinite; +} + +.step-list { + display: grid; + gap: 16px; + margin-top: 18px; +} + +.step-list p { + display: flex; + gap: 14px; + align-items: center; + margin: 0; + color: var(--muted); + transition: transform .22s ease, color .22s ease; +} + +.step-list p:hover { + color: var(--green-dark); + transform: translateX(6px); +} + +.step-list b { + width: 38px; + height: 38px; + border-radius: 50%; + display: grid; + place-items: center; + color: #fff; + background: var(--green); + flex: 0 0 auto; + box-shadow: 0 12px 28px rgba(71, 111, 99, .18); + transition: transform .22s ease, background .22s ease; +} + +.step-list p:hover b { + background: var(--red); + transform: scale(1.08); +} + +.create-form-section { + position: relative; + overflow: hidden; +} + +.create-form-section:before { + content: ""; + position: absolute; + left: 50%; + top: 58px; + width: 520px; + height: 180px; + border-radius: 50%; + background: radial-gradient(circle, rgba(196, 146, 69, .09), transparent 68%); + transform: translateX(-50%); +} + +.create-form { + max-width: 720px; + position: relative; + overflow: hidden; + padding: 36px; +} + +.form-ornament { + position: absolute; + right: -82px; + top: -88px; + width: 220px; + height: 220px; + border-radius: 50%; + background: radial-gradient(circle, rgba(71, 111, 99, .12), transparent 66%); +} + +.create-form h2, +.create-form .form-note, +.create-form .form-grid { + position: relative; + z-index: 2; +} + +.form-note { + margin-bottom: 22px; + color: var(--muted); + line-height: 1.8; +} + +.create-form .input, +.create-form textarea { + transition: border-color .22s ease, box-shadow .22s ease, background .22s ease, transform .22s ease; +} + +.create-region-picker { + /* 创建家谱页地区选择器占满表单宽度,编码通过隐藏字段提交 */ + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; +} + +.create-form .input:focus, +.create-form textarea:focus { + outline: none; + border-color: rgba(200, 59, 50, .62); + background: #fffdf8; + box-shadow: 0 0 0 4px rgba(200, 59, 50, .08); + transform: translateY(-2px); +} + +.submit-btn { + width: 100%; +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -6px; +} + +@keyframes createPulse { + 0%, 100% { opacity: .5; transform: scale(.92); } + 50% { opacity: 1; transform: scale(1.08); } +} + +@media (max-width: 720px) { + .hero-copy { padding-left: 0; } + .hero-copy:before { display: none; } + .create-region-picker { grid-template-columns: 1fr; } +} diff --git a/public/css/culture.css b/public/css/culture.css new file mode 100644 index 0000000..39db833 --- /dev/null +++ b/public/css/culture.css @@ -0,0 +1,213 @@ +.culture-hero .container { + min-height: 440px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.culture-stats { + display: flex; + gap: 12px; + margin-top: 28px; + flex-wrap: wrap; +} + +.culture-stats span { + padding: 10px 14px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 999px; + color: #6d766f; + background: rgba(255,253,248,.74); + box-shadow: 0 14px 32px rgba(72, 57, 38, .06); + font-weight: 800; + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.culture-stats span:hover { + color: #fff; + background: var(--green); + transform: translateY(-3px); +} + +.culture-quote { + position: relative; + min-height: 260px; + overflow: hidden; + display: flex; + flex-direction: column; + justify-content: center; + color: #fff; + background: + linear-gradient(180deg, rgba(33,63,56,.18), rgba(33,63,56,.86)), + linear-gradient(135deg, #476f63, #c49245 50%, #c83b32); +} + +.culture-quote:before { + content: ""; + position: absolute; + inset: 0; + background: + linear-gradient(rgba(255,255,255,.08) 1px, transparent 1px), + linear-gradient(90deg, rgba(255,255,255,.08) 1px, transparent 1px); + background-size: 34px 34px; + opacity: .34; +} + +.quote-moon { + position: absolute; + right: 72px; + top: 34px; + width: 92px; + height: 92px; + border-radius: 50%; + background: rgba(255,255,255,.32); + box-shadow: 0 0 34px rgba(255,255,255,.12); + animation: moonFloat 4s ease-in-out infinite; +} + +.culture-quote span { + position: relative; + z-index: 2; + width: 64px; + height: 64px; + margin-bottom: 24px; + border-radius: 50%; + display: grid; + place-items: center; + color: var(--red); + background: rgba(255,255,255,.94); + font-family: "SimSun", "Songti SC", serif; + font-size: 30px; + font-weight: 800; + transition: transform .24s ease, background .24s ease; +} + +.culture-quote:hover span { + transform: rotate(-8deg) scale(1.08); +} + +.culture-quote p { + position: relative; + z-index: 2; + margin: 0; + color: rgba(255,255,255,.86); + font-size: 22px; + line-height: 1.7; +} + +.article-section { + position: relative; + overflow: hidden; +} + +.article-section:before { + content: ""; + position: absolute; + left: 50%; + top: 58px; + width: 520px; + height: 180px; + border-radius: 50%; + background: radial-gradient(circle, rgba(196, 146, 69, .09), transparent 68%); + transform: translateX(-50%); +} + +.article-card { + position: relative; + overflow: hidden; + border-radius: 18px; + background: #fff; + border: 1px solid var(--line); + box-shadow: 0 18px 48px rgba(57, 48, 36, .07); + transition: transform .24s ease, box-shadow .24s ease, border-color .24s ease; +} + +.article-card:hover { + border-color: rgba(196, 146, 69, .42); + box-shadow: 0 24px 60px rgba(57, 48, 36, .11); +} + +.article-cover { + position: relative; + height: 190px; + overflow: hidden; + background: + radial-gradient(circle at 74% 28%, rgba(255,255,255,.7) 0 8%, transparent 9%), + linear-gradient(135deg, #e7c89e, #b94135 48%, #143d57); +} + +.article-cover:after { + content: ""; + position: absolute; + left: -42%; + top: 0; + width: 36%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,.34), transparent); + transform: skewX(-18deg); + transition: left .58s ease; +} + +.article-card:hover .article-cover:after { + left: 112%; +} + +.article-cover span { + position: absolute; + left: 22px; + bottom: 20px; + padding: 7px 12px; + border-radius: 999px; + color: var(--green-dark); + background: rgba(255,253,248,.86); + font-weight: 800; + font-size: 13px; +} + +.article-card:nth-child(2) .article-cover { + background: linear-gradient(135deg, #dfd1b5, #6b7f70 48%, #26302c); +} + +.article-card:nth-child(3) .article-cover { + background: linear-gradient(135deg, #f0d8ac, #c49245 45%, #7d342d); +} + +.article-body { padding: 24px; } +.article-body h3 { transition: color .22s ease; } +.article-card:hover .article-body h3 { color: var(--red); } +.article-body p { color: var(--muted); line-height: 1.75; } +.article-body span { color: #9a8f82; font-size: 14px; } + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -6px; +} + +@keyframes moonFloat { + 0%, 100% { transform: translateY(0) scale(1); opacity: .72; } + 50% { transform: translateY(-8px) scale(1.04); opacity: .92; } +} + +@media (max-width: 720px) { + .hero-copy { padding-left: 0; } + .hero-copy:before { display: none; } +} diff --git a/public/css/download.css b/public/css/download.css new file mode 100644 index 0000000..81d2231 --- /dev/null +++ b/public/css/download.css @@ -0,0 +1,86 @@ +.download-hero .container { + min-height: 440px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.detail-actions { + display: flex; + gap: 14px; + margin-top: 30px; + flex-wrap: wrap; +} + +.qr-card { + display: grid; + place-items: center; + text-align: center; +} + +.qr-box { + width: 170px; + height: 170px; + padding: 16px; + border-radius: 24px; + background: + linear-gradient(90deg, var(--green) 12px, transparent 12px) 0 0 / 32px 32px, + linear-gradient(var(--green) 12px, transparent 12px) 0 0 / 32px 32px, + #fffdf8; + border: 1px solid var(--line); + display: grid; + place-items: center; + box-shadow: inset 0 0 0 12px #fffdf8; +} + +.qr-box span { + width: 62px; + height: 62px; + border-radius: 50%; + display: grid; + place-items: center; + color: #fff; + background: var(--red); + font-weight: 900; +} + +.qr-card p, +.download-card p { + color: var(--muted); + line-height: 1.8; +} + +.download-card { + transition: transform .24s ease, border-color .24s ease, box-shadow .24s ease; +} + +.download-card:hover { + border-color: rgba(196, 146, 69, .42); +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { --lift: -6px; } + +@media (max-width: 720px) { + .hero-copy { padding-left: 0; } + .hero-copy:before { display: none; } +} diff --git a/public/css/family-detail.css b/public/css/family-detail.css new file mode 100644 index 0000000..c2d70da --- /dev/null +++ b/public/css/family-detail.css @@ -0,0 +1,160 @@ +.page-family-detail [data-family-detail].is-loading { + /* 详情接口加载时只改变透明度,具体状态由公共 is-loading 类控制 */ + opacity: .82; +} + +.detail-hero .container { + min-height: 440px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.detail-actions { + display: flex; + gap: 14px; + margin-top: 30px; + flex-wrap: wrap; +} + +.family-summary { + position: relative; + overflow: hidden; + display: grid; + align-content: center; + gap: 24px; + color: #fff; + background: + linear-gradient(180deg, rgba(33,63,56,.18), rgba(33,63,56,.88)), + linear-gradient(135deg, #476f63, #c49245 52%, #8f4634); +} + +.family-summary:before { + content: ""; + position: absolute; + inset: 0; + background: + linear-gradient(rgba(255,255,255,.08) 1px, transparent 1px), + linear-gradient(90deg, rgba(255,255,255,.08) 1px, transparent 1px); + background-size: 34px 34px; + opacity: .38; +} + +.family-summary > * { + position: relative; + z-index: 2; +} + +.summary-badge { + width: fit-content; + padding: 8px 14px; + border-radius: 999px; + color: var(--green-dark); + background: rgba(255,253,248,.88); + font-weight: 800; +} + +.summary-grid { + /* 接口统计数据会替换这里的四个格子,固定列数避免加载后跳动 */ + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 12px; +} + +.summary-grid span { + min-height: 84px; + padding: 16px; + border-radius: 16px; + display: grid; + place-items: center; + background: rgba(255,255,255,.13); + border: 1px solid rgba(255,255,255,.18); +} + +.summary-grid b { + display: block; + font-size: 30px; +} + +.detail-layout { + /* 详情页侧栏和正文为本页独有布局,不放入公共样式 */ + display: grid; + grid-template-columns: 330px 1fr; + gap: 26px; + align-items: start; +} + +.detail-panel, +.content-card, +.mini-card { + border: 1px solid var(--line); + border-radius: 18px; + background: rgba(255,253,248,.78); + box-shadow: 0 20px 54px rgba(57, 48, 36, .08); +} + +.detail-panel { + padding: 26px; + position: sticky; + top: 108px; +} + +.detail-panel p, +.content-card p, +.mini-card p { + color: var(--muted); + line-height: 1.8; +} + +.detail-main { + display: grid; + gap: 24px; +} + +.content-card { + padding: 30px; +} + +.mini-card { + min-height: 170px; + padding: 24px; + transition: transform .24s ease, border-color .24s ease, box-shadow .24s ease; +} + +.mini-card:hover { + border-color: rgba(196, 146, 69, .42); + box-shadow: 0 24px 60px rgba(57, 48, 36, .11); +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { --lift: -6px; } + +@media (max-width: 900px) { + .detail-layout { grid-template-columns: 1fr; } + .detail-panel { position: static; } + .summary-grid { grid-template-columns: repeat(2, 1fr); } +} + +@media (max-width: 720px) { + .hero-copy { padding-left: 0; } + .hero-copy:before { display: none; } +} diff --git a/public/css/forgot-password.css b/public/css/forgot-password.css new file mode 100644 index 0000000..8c0861f --- /dev/null +++ b/public/css/forgot-password.css @@ -0,0 +1,27 @@ +/* 找回密码页只保留单页装饰,公共认证卡片样式在 public.css */ +.recover-card { + position: relative; + overflow: hidden; +} + +.recover-card:before { + content: ""; + position: absolute; + right: -80px; + top: -90px; + width: 220px; + height: 220px; + border-radius: 50%; + background: radial-gradient(circle, rgba(200, 59, 50, .13), transparent 64%); +} + +.recover-card > * { + position: relative; + z-index: 2; +} + +@media (max-width: 620px) { + .code-row { + grid-template-columns: 1fr; + } +} diff --git a/public/css/genealogy.css b/public/css/genealogy.css new file mode 100644 index 0000000..239b190 --- /dev/null +++ b/public/css/genealogy.css @@ -0,0 +1,265 @@ +.page-genealogy { + background: #fbf7ed; +} + +.genealogy-hero .container { + min-height: 470px; + padding-top: 76px; + padding-bottom: 76px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 136px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.genealogy-stats { + display: flex; + gap: 12px; + margin-top: 28px; + padding: 8px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 18px; + background: rgba(255,253,248,.74); + box-shadow: 0 16px 36px rgba(72, 57, 38, .07); +} + +.genealogy-stats div { + min-width: 118px; + padding: 12px 16px; + border-radius: 12px; +} + +.genealogy-stats div + div { + border-left: 1px solid rgba(218, 201, 174, .78); +} + +.genealogy-stats strong { + display: block; + color: var(--green); + font-size: 24px; + line-height: 1.1; +} + +.genealogy-stats span { + color: #6d766f; + font-size: 14px; +} + +.tree-preview { + position: relative; + min-height: 300px; + display: grid; + gap: 14px; + align-content: center; + overflow: hidden; + background: + linear-gradient(90deg, rgba(255,253,248,.95), rgba(255,253,248,.78)), + radial-gradient(circle at 78% 22%, rgba(196,146,69,.2), transparent 28%); + transform-style: preserve-3d; +} + +.tree-glow { + position: absolute; + right: -70px; + top: -80px; + width: 210px; + height: 210px; + border-radius: 50%; + background: radial-gradient(circle, rgba(200, 59, 50, .13), transparent 62%); + animation: treePulse 4.2s ease-in-out infinite; +} + +.tree-row { + position: relative; + z-index: 2; + display: flex; + justify-content: center; + gap: 16px; + flex-wrap: wrap; +} + +.tree-row span { + min-width: 110px; + padding: 14px 18px; + border: 1px solid #dac9ae; + border-radius: 14px; + text-align: center; + color: var(--green-dark); + background: rgba(255,250,241,.9); + box-shadow: 0 12px 28px rgba(69, 53, 35, .06); + font-weight: 800; + transition: transform .24s ease, color .24s ease, background .24s ease, box-shadow .24s ease; +} + +.tree-preview:hover .tree-row span { + animation: nodeFloat 1.9s ease-in-out infinite; +} + +.tree-row span:hover { + color: #fff; + background: var(--green); + box-shadow: 0 16px 32px rgba(71, 111, 99, .22); +} + +.tree-row.branch span:nth-child(2) { animation-delay: .08s; } +.tree-row.branch span:nth-child(3) { animation-delay: .16s; } +.tree-row.generation span:nth-child(2) { animation-delay: .08s; } +.tree-row.generation span:nth-child(3) { animation-delay: .16s; } +.tree-row.generation span:nth-child(4) { animation-delay: .24s; } + +.tree-line { + position: relative; + z-index: 1; + width: min(360px, 70%); + height: 1px; + margin: 0 auto; + background: linear-gradient(90deg, transparent, rgba(196, 146, 69, .5), transparent); +} + +.tree-line.short { + width: min(500px, 86%); +} + +.capability-section { + position: relative; + overflow: hidden; +} + +.capability-section:before { + content: ""; + position: absolute; + left: 50%; + top: 58px; + width: 520px; + height: 180px; + border-radius: 50%; + background: radial-gradient(circle, rgba(196, 146, 69, .09), transparent 68%); + transform: translateX(-50%); + pointer-events: none; +} + +.feature-card { + min-height: 210px; + position: relative; + overflow: hidden; +} + +.feature-card:after { + content: ""; + position: absolute; + left: -40%; + top: 0; + width: 36%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,.48), transparent); + transform: skewX(-18deg); + transition: left .56s ease; +} + +.feature-card:hover:after { + left: 110%; +} + +.icon-word { + width: 48px; + height: 48px; + margin-bottom: 18px; + border-radius: 14px; + display: grid; + place-items: center; + color: var(--red); + background: #f7e4dc; + font-size: 22px; + font-weight: 900; + transition: transform .26s ease, background .26s ease, color .26s ease; +} + +.feature-card:hover .icon-word { + color: #fff; + background: var(--red); + transform: rotate(-6deg) scale(1.08); +} + +.coedit-section { + background: + radial-gradient(circle at 18% 20%, rgba(200, 59, 50, .08), transparent 24%), + #f7efe2; +} + +.coedit-card { + min-height: 260px; + padding: 34px; +} + +.coedit-card .btn { + margin-top: 12px; +} + +.process-card { + min-height: 260px; + padding: 34px; +} + +.step-list { + display: grid; + gap: 16px; + margin-top: 20px; +} + +.step-list p { + display: flex; + gap: 14px; + align-items: center; + margin: 0; + color: var(--muted); +} + +.step-list b { + width: 38px; + height: 38px; + border-radius: 50%; + display: grid; + place-items: center; + color: #fff; + background: var(--green); + flex: 0 0 auto; + box-shadow: 0 12px 28px rgba(71, 111, 99, .18); +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -6px; +} + +@keyframes nodeFloat { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-5px); } +} + +@keyframes treePulse { + 0%, 100% { opacity: .5; transform: scale(.92); } + 50% { opacity: 1; transform: scale(1.08); } +} + +@media (max-width: 720px) { + .hero-copy { padding-left: 0; } + .hero-copy:before { display: none; } + .genealogy-stats { flex-direction: column; } + .genealogy-stats div + div { border-left: 0; border-top: 1px solid rgba(218, 201, 174, .78); } +} diff --git a/public/css/help.css b/public/css/help.css new file mode 100644 index 0000000..b700117 --- /dev/null +++ b/public/css/help.css @@ -0,0 +1,211 @@ +.help-hero .container { + min-height: 440px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.help-tags { + display: flex; + gap: 12px; + margin-top: 28px; + flex-wrap: wrap; +} + +.help-tags span { + padding: 10px 14px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 999px; + color: #6d766f; + background: rgba(255,253,248,.74); + box-shadow: 0 14px 32px rgba(72, 57, 38, .06); + font-weight: 800; + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.help-tags span:hover { + color: #fff; + background: var(--green); + transform: translateY(-3px); +} + +.help-card { + position: relative; + overflow: hidden; + display: grid; + align-content: center; + gap: 14px; +} + +.help-card:before { + content: ""; + position: absolute; + inset: 0; + background: + linear-gradient(rgba(196,146,69,.06) 1px, transparent 1px), + linear-gradient(90deg, rgba(196,146,69,.06) 1px, transparent 1px); + background-size: 34px 34px; + opacity: .72; +} + +.help-card h3, +.help-card p, +.help-card .btn, +.help-icon { + position: relative; + z-index: 2; +} + +.help-orb { + position: absolute; + right: -70px; + top: -80px; + width: 220px; + height: 220px; + border-radius: 50%; + background: radial-gradient(circle, rgba(200, 59, 50, .13), transparent 62%); + animation: helpPulse 4s ease-in-out infinite; +} + +.help-icon { + width: 52px; + height: 52px; + border-radius: 16px; + display: grid; + place-items: center; + color: #fff; + background: var(--green); + font-size: 26px; + font-weight: 900; + box-shadow: 0 14px 32px rgba(71, 111, 99, .2); + transition: transform .26s ease, background .26s ease; +} + +.help-card:hover .help-icon { + background: var(--red); + transform: rotate(-8deg) scale(1.08); +} + +.faq-section { + position: relative; + overflow: hidden; +} + +.faq-section:before { + content: ""; + position: absolute; + left: 50%; + top: 58px; + width: 520px; + height: 180px; + border-radius: 50%; + background: radial-gradient(circle, rgba(196, 146, 69, .09), transparent 68%); + transform: translateX(-50%); +} + +.faq-list { + /* 帮助文章接口会替换 FAQ 内容,固定列表容器宽度避免加载后跳动 */ + position: relative; + max-width: 880px; + margin: 0 auto; + display: grid; + gap: 14px; +} + +.faq-list details { + padding: 22px 24px; + border: 1px solid var(--line); + border-radius: 18px; + background: #fffdf8; + box-shadow: 0 12px 34px rgba(57, 48, 36, .06); + transition: transform .24s ease, box-shadow .24s ease, border-color .24s ease, background .24s ease; +} + +.faq-list.is-loading { + min-height: 220px; +} + +.faq-list details:hover, +.faq-list details[open] { + border-color: rgba(196, 146, 69, .42); + box-shadow: 0 22px 54px rgba(57, 48, 36, .1); +} + +.faq-list summary { + position: relative; + cursor: pointer; + color: var(--ink); + font-size: 20px; + font-weight: 800; + list-style: none; + padding-left: 34px; +} + +.faq-list summary::-webkit-details-marker { + display: none; +} + +.faq-list summary:before { + content: ""; + position: absolute; + left: 0; + top: 8px; + width: 0; + height: 0; + border-top: 7px solid transparent; + border-bottom: 7px solid transparent; + border-left: 10px solid var(--green-dark); + transition: transform .24s ease, border-left-color .24s ease; +} + +.faq-list details[open] summary:before { + border-left-color: var(--red); + transform: rotate(90deg); +} + +.faq-list p { + margin: 14px 0 0; + padding-left: 34px; + color: var(--muted); + line-height: 1.8; + animation: faqReveal .24s ease both; +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -6px; +} + +@keyframes helpPulse { + 0%, 100% { opacity: .5; transform: scale(.92); } + 50% { opacity: 1; transform: scale(1.08); } +} + +@keyframes faqReveal { + from { opacity: 0; transform: translateY(-4px); } + to { opacity: 1; transform: translateY(0); } +} + +@media (max-width: 720px) { + .hero-copy { padding-left: 0; } + .hero-copy:before { display: none; } +} diff --git a/public/css/index.css b/public/css/index.css new file mode 100644 index 0000000..85db30f --- /dev/null +++ b/public/css/index.css @@ -0,0 +1,1398 @@ + .hero { + position: relative; + overflow: hidden; + min-height: 760px; + background: + radial-gradient(circle at 82% 12%, rgba(196, 146, 69, .16), transparent 24%), + linear-gradient(125deg, #fffaf0 0%, #f7efe2 48%, #edf4ef 100%); + } + + .hero:before { + content: ""; + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(138, 50, 43, .05) 1px, transparent 1px), + linear-gradient(90deg, rgba(138, 50, 43, .05) 1px, transparent 1px); + background-size: 68px 68px; + mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent); + } + + .hero-inner { + position: relative; + z-index: 2; + min-height: 680px; + display: grid; + grid-template-columns: 0.76fr 1.24fr; + align-items: center; + gap: 34px; + padding: 24px 0 76px; + } + + .eyebrow { + display: inline-flex; + align-items: center; + gap: 10px; + margin-bottom: 28px; + color: #8a5a3b; + font-weight: 800; + } + + .eyebrow:before { + content: ""; + width: 42px; + height: 2px; + background: var(--red); + } + + h1 { + margin: 0; + color: var(--ink); + font-family: "SimSun", "Songti SC", serif; + font-size: 56px; + line-height: 1.16; + letter-spacing: 0; + } + + .lead { + max-width: 560px; + margin: 22px 0 0; + color: #645d52; + font-size: 18px; + line-height: 1.9; + } + + .hero-copy { + position: relative; + padding-top: 10px; + } + + .hero-copy:before { + content: ""; + position: absolute; + left: -28px; + top: 48px; + width: 4px; + height: 156px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); + } + + .hero-title-mark { + display: inline-block; + position: relative; + color: var(--red); + } + + .hero-title-mark:after { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: 7px; + height: 12px; + background: rgba(200, 59, 50, .12); + z-index: -1; + } + + .hero-path { + display: flex; + align-items: center; + gap: 10px; + margin-top: 26px; + color: #766b5e; + font-weight: 800; + } + + .hero-path span { + min-width: 34px; + height: 34px; + border-radius: 50%; + display: inline-grid; + place-items: center; + color: #fff; + background: var(--green); + font-size: 14px; + } + + .hero-path i { + width: 34px; + height: 1px; + background: #dac9ae; + } + + .hero-action-card { + display: inline-flex; + align-items: center; + gap: 18px; + margin-top: 30px; + padding: 14px 16px; + border-radius: 999px; + background: rgba(255,253,248,.72); + border: 1px solid rgba(224, 211, 189, .86); + box-shadow: 0 18px 42px rgba(72, 57, 38, .08); + } + + .cta { + display: flex; + gap: 16px; + margin-top: 0; + } + + .metrics { + display: flex; + align-items: stretch; + max-width: 560px; + margin-top: 22px; + padding: 8px; + border-radius: 18px; + color: #6d766f; + background: rgba(255,253,248,.74); + border: 1px solid rgba(224, 211, 189, .78); + box-shadow: 0 16px 36px rgba(72, 57, 38, .07); + } + + .metric { + flex: 1; + min-width: 0; + min-height: 76px; + padding: 12px 16px; + border-radius: 12px; + } + + .metric + .metric { + border-left: 1px solid rgba(218, 201, 174, .78); + } + + .metric strong { + display: block; + margin-bottom: 5px; + color: var(--green); + font-size: 21px; + line-height: 1.18; + } + + .hero-visual { + position: relative; + min-height: 650px; + margin-left: -42px; + } + + .hero-showcase { + position: absolute; + inset: 18px 18px 20px 0; + display: grid; + grid-template-columns: 1.08fr .92fr; + grid-template-rows: 214px 1fr; + gap: 18px; + } + + .showcase-card { + overflow: hidden; + border-radius: 24px; + background: rgba(255,253,248,.92); + border: 1px solid rgba(224, 211, 189, .88); + box-shadow: 0 24px 70px rgba(69, 53, 35, .12); + } + + .family-preview { + grid-column: 1 / 3; + display: grid; + grid-template-columns: 170px 1fr 238px; + align-items: center; + gap: 24px; + padding: 28px; + background: + linear-gradient(90deg, rgba(255,253,248,.95), rgba(255,253,248,.78)), + radial-gradient(circle at 78% 22%, rgba(196,146,69,.2), transparent 26%); + } + + .preview-book { + width: 132px; + height: 158px; + border-radius: 12px; + position: relative; + background: linear-gradient(155deg, #143d57, #092d42); + box-shadow: 0 18px 42px rgba(11, 41, 61, .22); + } + + .preview-book:before { + content: "汤氏\A家谱"; + white-space: pre; + position: absolute; + left: 24px; + top: 24px; + width: 34px; + padding: 8px 3px; + color: #1c302d; + line-height: 1.2; + text-align: center; + background: #fbf0d8; + border: 1px solid rgba(203, 176, 122, .9); + font-weight: 800; + font-size: 14px; + } + + .family-preview h3 { + margin: 0 0 12px; + font-size: 28px; + } + + .family-preview p { + max-width: 430px; + margin: 0; + color: var(--muted); + line-height: 1.8; + } + + .preview-stats { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 10px; + } + + .stat-pill { + min-height: 66px; + padding: 12px; + border-radius: 14px; + background: #f7efe2; + color: #716454; + } + + .stat-pill strong { + display: block; + color: var(--green); + font-size: 22px; + line-height: 1.1; + } + + .showcase-video { + position: relative; + display: block; + min-height: 286px; + color: #fff; + background: #213f38; + } + + .showcase-video img { + width: 100%; + height: 100%; + min-height: 286px; + display: block; + object-fit: cover; + filter: saturate(.9); + } + + .showcase-video:before { + content: ""; + position: absolute; + inset: 0; + background: linear-gradient(180deg, rgba(20, 35, 31, .05), rgba(20, 35, 31, .78)); + } + + .showcase-video .play { + position: absolute; + left: 28px; + bottom: 84px; + width: 62px; + height: 62px; + border-radius: 50%; + background: rgba(255,255,255,.94); + box-shadow: 0 12px 30px rgba(0,0,0,.18); + } + + .showcase-video .play:after { + content: ""; + position: absolute; + left: 25px; + top: 18px; + width: 0; + height: 0; + border-top: 13px solid transparent; + border-bottom: 13px solid transparent; + border-left: 20px solid var(--red); + } + + .showcase-video h3, + .showcase-video p { + position: absolute; + left: 28px; + right: 28px; + margin: 0; + } + + .showcase-video h3 { + bottom: 44px; + font-size: 26px; + } + + .showcase-video p { + bottom: 22px; + color: rgba(255,255,255,.78); + } + + .showcase-search { + min-height: 286px; + padding: 24px; + } + + .showcase-search h3 { + margin: 0 0 16px; + font-size: 22px; + } + + .showcase-search .search-box { + height: 46px; + } + + .recommend-list { + display: grid; + gap: 10px; + margin-top: 16px; + } + + .recommend-item { + padding: 13px 14px; + border-radius: 14px; + background: #f8f1e5; + color: #6d6357; + line-height: 1.55; + } + + .recommend-item strong { + display: block; + margin-bottom: 4px; + color: var(--ink); + font-size: 16px; + } + + .portal-card { + position: absolute; + inset: 18px 18px 20px 0; + overflow: hidden; + border-radius: 32px; + background: rgba(255,253,248,.92); + border: 1px solid rgba(224, 211, 189, .88); + box-shadow: 0 34px 90px rgba(69, 53, 35, .13); + } + + .portal-cover { + position: relative; + height: 250px; + overflow: hidden; + background: + linear-gradient(180deg, rgba(255,253,248,.08), rgba(31, 63, 55, .66)), + radial-gradient(circle at 78% 18%, rgba(255,255,255,.55) 0 6%, transparent 7%), + linear-gradient(135deg, #e9d1a1 0%, #c4533f 45%, #173f52 100%); + } + + .portal-cover:before { + content: ""; + position: absolute; + left: 50%; + top: 34px; + width: 390px; + height: 190px; + transform: translateX(-50%); + background: + linear-gradient(180deg, #f3d083 0 18%, #be4033 18% 70%, #8e3029 70% 100%); + clip-path: polygon(2% 38%, 20% 13%, 80% 13%, 98% 38%, 88% 38%, 88% 94%, 12% 94%, 12% 38%); + filter: drop-shadow(0 18px 32px rgba(61, 38, 28, .2)); + } + + .portal-cover:after { + content: "代代相传"; + position: absolute; + left: 34px; + bottom: 30px; + color: rgba(255,255,255,.96); + font-size: 28px; + font-weight: 800; + } + + .portal-book { + position: absolute; + left: 34px; + top: 34px; + width: 118px; + height: 164px; + border-radius: 12px; + background: linear-gradient(155deg, #143d57, #092d42); + box-shadow: 0 20px 48px rgba(11, 41, 61, .26); + z-index: 2; + } + + .portal-book:before { + content: "汤氏\A家谱"; + white-space: pre; + position: absolute; + left: 18px; + top: 24px; + width: 32px; + padding: 8px 3px; + color: #1c302d; + line-height: 1.2; + text-align: center; + background: #fbf0d8; + border: 1px solid rgba(203, 176, 122, .9); + font-weight: 800; + font-size: 14px; + } + + .portal-body { + display: grid; + grid-template-columns: 1.18fr .82fr; + gap: 18px; + padding: 22px; + } + + .portal-box { + min-height: 198px; + padding: 22px; + border-radius: 20px; + background: #fffaf1; + border: 1px solid #eadfce; + } + + .portal-box h3 { + margin: 0 0 16px; + font-size: 22px; + } + + .portal-box p { + margin: 0; + color: var(--muted); + line-height: 1.75; + } + + .portal-video { + display: flex; + flex-direction: column; + justify-content: space-between; + color: #fff; + background: + linear-gradient(180deg, rgba(33,63,56,.18), rgba(33,63,56,.86)), + radial-gradient(circle at 78% 24%, rgba(255,255,255,.46) 0 9%, transparent 10%), + linear-gradient(135deg, #476f63, #c49245 50%, #c83b32); + border-color: rgba(255,255,255,.28); + } + + .portal-video p { + color: rgba(255,255,255,.78); + } + + .play-line { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + } + + .play-button { + width: 58px; + height: 58px; + border-radius: 50%; + position: relative; + flex: 0 0 auto; + background: rgba(255,255,255,.94); + box-shadow: 0 12px 28px rgba(30, 30, 30, .18); + } + + .play-button:after { + content: ""; + position: absolute; + left: 23px; + top: 17px; + width: 0; + height: 0; + border-top: 12px solid transparent; + border-bottom: 12px solid transparent; + border-left: 18px solid var(--red); + } + + .public-list { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 10px; + margin-top: 16px; + } + + .public-item { + min-height: 66px; + padding: 9px 12px; + border-radius: 12px; + background: #f5ecd9; + color: #61584d; + font-size: 13px; + line-height: 1.55; + } + + .public-item strong { + display: block; + margin-bottom: 4px; + color: var(--ink); + font-size: 15px; + } + + .archive-stage { + position: absolute; + inset: 14px 0 22px 0; + border-radius: 30px; + background: + linear-gradient(90deg, rgba(255,255,255,.82), rgba(255,255,255,.55)), + radial-gradient(circle at 78% 22%, rgba(180, 51, 42, .12), transparent 26%); + border: 1px solid rgba(196, 146, 69, .28); + box-shadow: 0 34px 90px rgba(69, 53, 35, .13); + } + + .book-cover { + position: absolute; + left: 54px; + top: 44px; + width: 220px; + height: 310px; + border-radius: 18px; + background: linear-gradient(155deg, #143d57, #092d42); + box-shadow: 0 28px 70px rgba(19, 54, 76, .22); + } + + .book-cover:before { + content: "张氏家谱"; + position: absolute; + left: 34px; + top: 34px; + width: 29px; + padding: 12px 4px; + line-height: 1.25; + text-align: center; + color: #1c302d; + background: #fbf0d8; + border: 1px solid rgba(203, 176, 122, .9); + font-weight: 800; + } + + .hall { + position: absolute; + right: 28px; + top: 34px; + width: 342px; + height: 198px; + overflow: hidden; + border-radius: 22px; + background: #fff8ec; + border: 1px solid rgba(224, 211, 189, .9); + box-shadow: 0 24px 60px rgba(93, 72, 45, .14); + } + + .hall:before { + content: none; + } + + .hall:after { + content: none; + } + + .hall img { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: cover; + object-position: center top; + } + + .word-scroll-card { + display: flex; + align-items: center; + gap: 16px; + max-width: 620px; + margin-top: 22px; + padding: 10px 0; + border-radius: 999px; + color: #766b5e; + overflow: hidden; + } + + .word-scroll-card:before { + content: ""; + width: 42px; + height: 2px; + flex: 0 0 auto; + background: linear-gradient(90deg, var(--red), var(--gold)); + } + + .word-scroll-card:after { + content: none; + } + + .word-scroll-label { + color: #8b6c48; + font-size: 14px; + font-weight: 800; + } + + .word-window { + flex: 1; + height: 34px; + overflow: hidden; + mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); + } + + .word-track { + display: flex; + align-items: center; + gap: 24px; + width: max-content; + animation: wordMarquee 16s linear infinite; + } + + .word { + height: 34px; + display: flex; + align-items: center; + color: var(--green-dark); + font-family: "SimSun", "Songti SC", serif; + font-size: 24px; + font-weight: 800; + line-height: 1; + white-space: nowrap; + } + + @keyframes wordMarquee { + from { transform: translateX(0); } + to { transform: translateX(-50%); } + } + + .video-card { + position: absolute; + right: 28px; + top: 244px; + width: 342px; + height: 182px; + overflow: hidden; + border-radius: 20px; + color: #fff; + background: + linear-gradient(180deg, rgba(31, 63, 55, .12), rgba(25, 39, 34, .82)), + radial-gradient(circle at 78% 18%, rgba(255,255,255,.42) 0 8%, transparent 9%), + linear-gradient(135deg, #476f63, #c49245 48%, #c83b32); + box-shadow: 0 22px 56px rgba(45, 55, 50, .18); + } + + .video-card:before { + content: ""; + position: absolute; + left: 50%; + top: 50%; + width: 62px; + height: 62px; + border-radius: 50%; + background: rgba(255,255,255,.92); + transform: translate(-50%, -50%); + box-shadow: 0 12px 28px rgba(30, 30, 30, .18); + } + + .video-card:after { + content: ""; + position: absolute; + left: calc(50% - 8px); + top: calc(50% - 14px); + width: 0; + height: 0; + border-top: 14px solid transparent; + border-bottom: 14px solid transparent; + border-left: 22px solid var(--red); + } + + .video-card span { + position: absolute; + left: 22px; + bottom: 20px; + font-weight: 800; + font-size: 20px; + } + + .video-card small { + position: absolute; + left: 22px; + bottom: 50px; + color: rgba(255,255,255,.78); + font-size: 14px; + } + + .search-panel { + position: absolute; + left: 68px; + right: 28px; + top: 440px; + padding: 18px 20px; + border-radius: 20px; + background: rgba(255,255,255,.92); + border: 1px solid rgba(224, 211, 189, .8); + box-shadow: 0 24px 70px rgba(73, 59, 41, .12); + } + + .search-panel h3 { + margin: 0 0 16px; + font-size: 22px; + } + + .search-box { + display: flex; + height: 48px; + overflow: hidden; + border-radius: 999px; + border: 1px solid #e2d4bf; + background: #fbf8f1; + } + + .search-box span { + flex: 1; + display: flex; + align-items: center; + padding-left: 22px; + color: #9c9488; + } + + .search-box b { + width: 118px; + display: grid; + place-items: center; + color: #fff; + background: var(--red); + } + + .genealogy-list { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 10px; + margin-top: 14px; + } + + .genealogy-item { + min-height: 62px; + padding: 9px 12px; + border-radius: 12px; + background: #f8f1e5; + color: #61584d; + font-size: 14px; + line-height: 1.55; + } + + .genealogy-item strong { + display: block; + margin-bottom: 5px; + color: var(--ink); + font-size: 16px; + } + + .quick-strip { + position: relative; + z-index: 3; + margin-top: -58px; + } + + .quick-inner { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 18px; + } + + .quick-card { + min-height: 104px; + padding: 20px 24px; + border-radius: 12px; + color: var(--ink); + border: 1px solid rgba(218, 201, 174, .72); + background: + radial-gradient(circle at 88% 18%, rgba(255, 253, 248, .66), transparent 30%), + linear-gradient(135deg, #f6f1e7 0%, #e9d8c6 100%); + box-shadow: 0 18px 44px rgba(36, 42, 38, .08); + } + + .quick-card:nth-child(2) { + background: + radial-gradient(circle at 88% 18%, rgba(255, 253, 248, .64), transparent 30%), + linear-gradient(135deg, #f2f5ed 0%, #dbe8d2 100%); + } + + .quick-card:nth-child(3) { + background: + radial-gradient(circle at 88% 18%, rgba(255, 253, 248, .64), transparent 30%), + linear-gradient(135deg, #f8efe9 0%, #ead0c7 100%); + } + + .quick-card:nth-child(4) { + background: + radial-gradient(circle at 88% 18%, rgba(255, 253, 248, .66), transparent 30%), + linear-gradient(135deg, #f4efe4 0%, #e5d1aa 100%); + } + + .quick-card b { + display: block; + margin-bottom: 10px; + font-size: 20px; + color: var(--ink); + } + + .quick-card span { + color: var(--muted); + line-height: 1.7; + } + + section { + padding: 76px 0; + } + + .section-head { + margin-bottom: 34px; + text-align: center; + } + + .section-head h2 { + margin: 0; + font-family: "SimSun", "Songti SC", serif; + font-size: 42px; + line-height: 1.25; + } + + .section-head p { + margin: 14px auto 0; + max-width: 680px; + color: var(--muted); + font-size: 17px; + line-height: 1.9; + } + + .functions { + background: #fffdf8; + padding-bottom: 0; + } + + .function-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 22px; + } + + .function-card { + min-height: 190px; + padding: 24px; + border-radius: 14px; + background: #fbf7ed; + border: 1px solid #eee1cf; + transition: transform .2s ease; + } + + .function-card:hover { transform: translateY(-4px); } + + .icon { + width: 46px; + height: 46px; + margin-bottom: 18px; + border-radius: 14px; + display: grid; + place-items: center; + color: var(--red); + background: #f7e4dc; + font-size: 22px; + font-weight: 900; + } + + .function-card h3 { + margin: 0 0 12px; + font-size: 21px; + } + + .function-card p { + margin: 0; + color: var(--muted); + line-height: 1.8; + } + + .plaza { + background: + linear-gradient(180deg, #fffdf8, #f8f0e4); + } + + .plaza-layout { + display: grid; + grid-template-columns: 1.05fr .95fr; + gap: 34px; + align-items: stretch; + } + + .featured-family { + min-height: 430px; + padding: 34px; + border-radius: 22px; + color: #fff; + background: + linear-gradient(180deg, rgba(28, 48, 45, .18), rgba(21, 55, 51, .84)), + linear-gradient(135deg, #476f63, #a95c3e); + display: flex; + flex-direction: column; + justify-content: flex-end; + box-shadow: 0 24px 70px rgba(42, 65, 59, .15); + } + + .featured-family h3 { + margin: 0 0 16px; + font-size: 34px; + } + + .featured-family p { + max-width: 560px; + margin: 0; + color: rgba(255,255,255,.82); + line-height: 1.9; + font-size: 17px; + } + + .family-cards { + display: grid; + gap: 18px; + } + + .family-card { + padding: 22px; + border-radius: 18px; + background: #fffdf8; + border: 1px solid #eadfce; + box-shadow: 0 16px 42px rgba(57, 48, 36, .07); + } + + .family-card h4 { + margin: 0 0 8px; + font-size: 22px; + } + + .family-card p { + margin: 0 0 18px; + color: var(--muted); + line-height: 1.7; + } + + .tag-row { + display: flex; + gap: 10px; + flex-wrap: wrap; + } + + .tag { + padding: 6px 12px; + border-radius: 999px; + color: var(--green); + background: #eaf0eb; + font-size: 14px; + font-weight: 700; + } + + .culture { + background: #fffdf8; + } + + .article-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24px; + } + + .article-card { + overflow: hidden; + border-radius: 18px; + background: #fff; + border: 1px solid #eadfce; + box-shadow: 0 18px 48px rgba(57, 48, 36, .07); + } + + .article-cover { + height: 190px; + background: + radial-gradient(circle at 74% 28%, rgba(255,255,255,.7) 0 8%, transparent 9%), + linear-gradient(135deg, #e7c89e, #b94135 48%, #143d57); + } + + .article-card:nth-child(2) .article-cover { + background: linear-gradient(135deg, #dfd1b5, #6b7f70 48%, #26302c); + } + + .article-card:nth-child(3) .article-cover { + background: linear-gradient(135deg, #f0d8ac, #c49245 45%, #7d342d); + } + + .article-body { + padding: 24px; + } + + .article-body h3 { + margin: 0 0 12px; + font-size: 22px; + line-height: 1.45; + } + + .article-body p { + margin: 0 0 20px; + color: var(--muted); + line-height: 1.75; + } + + .article-meta { + color: #9a8f82; + font-size: 14px; + } + + .ad-slots { + background: linear-gradient(180deg, #fffdf8, #f8f0e4); + padding: 0; + } + + .ad-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); + gap: 16px; + align-items: start; + } + + .ad-card { + position: relative; + display: block; + aspect-ratio: 2.8 / 1; + padding: 8px; + border-radius: 12px; + overflow: hidden; + border: 1px solid rgba(224, 211, 189, .84); + background: #fffdf8; + box-shadow: 0 12px 30px rgba(57, 48, 36, .06); + } + + .ad-card img { + display: block; + width: 100%; + height: 100%; + object-fit: contain; + transition: transform .32s ease; + } + + .ad-card:hover img { + transform: scale(1.025); + } + + .surname { + background: + radial-gradient(circle at 12% 20%, rgba(200, 59, 50, .09), transparent 24%), + #f8f0e4; + } + + .surname-panel { + display: grid; + grid-template-columns: 360px 1fr; + gap: 34px; + padding: 30px; + border-radius: 24px; + background: rgba(255,255,255,.7); + border: 1px solid rgba(224, 211, 189, .8); + box-shadow: 0 24px 70px rgba(57, 48, 36, .08); + } + + .surname-intro { + padding: 30px; + border-radius: 18px; + color: #fff; + background: linear-gradient(155deg, var(--green-dark), var(--green)); + } + + .surname-intro h2 { + margin: 0 0 16px; + font-family: "SimSun", "Songti SC", serif; + font-size: 38px; + } + + .surname-intro p { + color: rgba(255,255,255,.78); + line-height: 1.9; + } + + .surname-grid { + display: grid; + grid-template-columns: repeat(8, 1fr); + gap: 10px; + align-content: start; + } + + .surname-name { + height: 62px; + border-radius: 12px; + display: grid; + place-items: center; + color: #7c4a33; + background: #fbf7ed; + border: 1px solid #eadfce; + font-size: 24px; + font-family: "SimSun", "Songti SC", serif; + font-weight: 800; + transition: transform .24s ease, color .24s ease, background .24s ease, border-color .24s ease, box-shadow .24s ease; + will-change: transform; + } + + .surname-name:hover { + color: #fff; + background: var(--red); + border-color: rgba(200, 59, 50, .72); + box-shadow: 0 16px 34px rgba(200, 59, 50, .18); + transform: translateY(-6px) scale(1.03); + } + + .surname-panel:hover .surname-name { + animation: surnameFloat 1.9s ease-in-out infinite; + } + + .surname-panel:hover .surname-name:nth-child(2n) { animation-delay: .08s; } + .surname-panel:hover .surname-name:nth-child(3n) { animation-delay: .14s; } + .surname-panel:hover .surname-name:nth-child(4n) { animation-delay: .2s; } + + @keyframes surnameFloat { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-4px); } + } + + .download { + background: #fffdf8; + } + + .recommend { + background: #fffdf8; + } + + .recommend-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); + gap: 18px; + } + + .recommend-card { + min-height: 184px; + padding: 22px; + border-radius: 14px; + border: 1px solid rgba(224, 211, 189, .84); + background: linear-gradient(180deg, #fffdf8, #fbf7ed); + box-shadow: 0 18px 48px rgba(57, 48, 36, .07); + } + + .recommend-card span { + display: inline-flex; + margin-bottom: 18px; + padding: 6px 12px; + border-radius: 999px; + color: var(--green); + background: #eaf0eb; + font-size: 13px; + font-weight: 900; + } + + .recommend-card h3 { + margin: 0 0 12px; + font-size: 24px; + line-height: 1.35; + } + + .recommend-card p { + margin: 0; + color: var(--muted); + line-height: 1.8; + } + + .friend-links { + padding: 0 0; + background: #fffdf8; + } + + .friend-links-panel { + display: flex; + align-items: center; + gap: 24px; + padding: 24px 28px; + border-radius: 18px; + border: 1px solid rgba(224, 211, 189, .84); + background: #fbf7ed; + } + + .friend-links-panel h2 { + flex: 0 0 auto; + margin: 0; + font-family: "SimSun", "Songti SC", serif; + font-size: 30px; + } + + .friend-link-list { + display: flex; + flex-wrap: wrap; + gap: 10px; + } + + .friend-link-list a { + padding: 8px 14px; + border-radius: 999px; + color: #5e665f; + background: rgba(255, 255, 255, .76); + border: 1px solid rgba(224, 211, 189, .78); + transition: color .22s ease, border-color .22s ease, transform .22s ease; + } + + .friend-link-list a:hover { + color: var(--red); + border-color: rgba(200, 59, 50, .32); + transform: translateY(-2px); + } + + .download-panel { + display: grid; + grid-template-columns: 1fr 380px; + gap: 56px; + align-items: center; + padding: 44px; + border-radius: 28px; + background: + radial-gradient(circle at 82% 30%, rgba(200, 59, 50, .12), transparent 28%), + linear-gradient(135deg, #f7efe2, #edf4ef); + border: 1px solid #eadfce; + } + + .download-panel h2 { + margin: 0; + font-family: "SimSun", "Songti SC", serif; + font-size: 42px; + } + + .download-panel p { + margin: 18px 0 30px; + color: var(--muted); + font-size: 18px; + line-height: 1.9; + } + + .qr-wrap { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 18px; + } + + .qr { + min-height: 190px; + padding: 18px; + border-radius: 18px; + text-align: center; + background: rgba(255,255,255,.78); + border: 1px solid #eadfce; + } + + .qr-box { + width: 112px; + height: 112px; + margin: 0 auto 14px; + background: + linear-gradient(90deg, #26302c 10px, transparent 10px) 0 0 / 22px 22px, + linear-gradient(#26302c 10px, transparent 10px) 0 0 / 22px 22px, + #fff; + border: 10px solid #fff; + box-shadow: inset 0 0 0 1px #ddd; + } +@media (max-width: 1120px) { + .nav-links { display: none; } + .hero-inner, + .plaza-layout, + .surname-panel, + .download-panel { grid-template-columns: 1fr; } + .function-grid, + .quick-inner { grid-template-columns: repeat(2, 1fr); } + .article-grid, + .recommend-grid { grid-template-columns: 1fr; } + .friend-links-panel { + align-items: flex-start; + flex-direction: column; + } + } + +.qr-note { margin: 8px 0 0; color: var(--muted); } + + @media (hover: hover) and (pointer: fine) { + .quick-card, .function-card, .family-card, .article-card, .ad-card, .surname-panel, .recommend-card, .download-panel { + cursor: pointer; + } + } + + .quick-card, + .function-card, + .family-card, + .article-card, + .ad-card, + .surname-panel, + .recommend-card, + .download-panel { + transition: transform .24s ease, box-shadow .24s ease, border-color .24s ease, background .24s ease, color .22s ease; + will-change: transform; + } + + .quick-card:hover, + .function-card:hover, + .family-card:hover, + .article-card:hover, + .ad-card:hover, + .recommend-card:hover { + border-color: rgba(196, 146, 69, .42); + box-shadow: 0 24px 60px rgba(57, 48, 36, .11); + } + + .function-card { + position: relative; + overflow: hidden; + } + + .function-card:after { + content: ""; + position: absolute; + left: -42%; + top: 0; + width: 36%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,.54), transparent); + transform: skewX(-18deg); + transition: left .56s ease; + } + + .function-card:hover:after { + left: 112%; + } + + .function-card:hover .icon { + color: #fff; + background: var(--red); + transform: rotate(-6deg) scale(1.08); + } + + .icon { + transition: transform .26s ease, background .26s ease, color .26s ease; + } + + .tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; + will-change: transform; + } + + .tilt-card:hover { + --lift: -6px; + } + + .magnetic { + will-change: transform; + } + + @media (prefers-reduced-motion: reduce) { + *, *:before, *:after { + animation-duration: .01ms !important; + animation-iteration-count: 1 !important; + scroll-behavior: auto !important; + transition-duration: .01ms !important; + } + } diff --git a/public/css/join-genealogy.css b/public/css/join-genealogy.css new file mode 100644 index 0000000..64663ca --- /dev/null +++ b/public/css/join-genealogy.css @@ -0,0 +1,17 @@ +/* 加入家谱页只保留单页差异,公共认证卡片样式在 public.css */ +.page-join-genealogy .auth-card { + width: min(500px, 100%); +} + +.invite-box { + padding: 14px; + border: 1px dashed var(--gold); + border-radius: 22px; + background: #fffaf1; +} + +.page-join-genealogy textarea.input { + /* 申请说明需要多行输入,仍复用公共输入框视觉 */ + min-height: 112px; + resize: vertical; +} diff --git a/public/css/login.css b/public/css/login.css new file mode 100644 index 0000000..c20d251 --- /dev/null +++ b/public/css/login.css @@ -0,0 +1,54 @@ +/* 登录页只保留单页差异,公共认证卡片样式在 public.css */ +.page-login .auth-card { + width: min(440px, 100%); +} + +.login-mode-tabs { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; + margin: 18px 0 16px; + padding: 4px; + border: 1px solid rgba(138, 64, 42, .14); + border-radius: 8px; + background: rgba(255, 255, 255, .72); +} + +/* 登录方式按钮只负责状态呈现,切换逻辑在 auth-pages.js */ +.login-mode-tab { + min-height: 38px; + border: 0; + border-radius: 6px; + background: transparent; + color: var(--muted); + font: inherit; + cursor: pointer; +} + +.login-mode-tab.is-active { + background: var(--brand); + color: #fff; + box-shadow: 0 8px 20px rgba(138, 64, 42, .18); +} + +.login-mode-panel[hidden] { + display: none; +} + +.page-login .code-row { + display: grid; + grid-template-columns: minmax(0, 1fr) 124px; + gap: 10px; +} + +@media (max-width: 720px) { + .page-login .auth-layout { + align-items: start; + } +} + +@media (max-width: 620px) { + .page-login .code-row { + grid-template-columns: 1fr; + } +} diff --git a/public/css/notice-detail.css b/public/css/notice-detail.css new file mode 100644 index 0000000..74d7d17 --- /dev/null +++ b/public/css/notice-detail.css @@ -0,0 +1,126 @@ +.notice-hero .container { + min-height: 420px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.article-meta { + display: flex; + gap: 12px; + margin-top: 28px; + flex-wrap: wrap; +} + +.article-meta span { + padding: 10px 14px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 999px; + color: #6d766f; + background: rgba(255,253,248,.74); + font-weight: 800; +} + +.notice-card { + display: grid; + place-items: center; + text-align: center; + color: #fff; + background: linear-gradient(145deg, var(--green-dark), var(--green)); +} + +.notice-card span { + width: 88px; + height: 88px; + border-radius: 50%; + display: grid; + place-items: center; + color: var(--red); + background: rgba(255,253,248,.94); + font-family: "SimSun", "Songti SC", serif; + font-size: 42px; + font-weight: 900; +} + +.notice-card p { + margin: 18px 0 0; + color: rgba(255,255,255,.88); + font-size: 22px; +} + +.notice-layout { + display: grid; + grid-template-columns: minmax(0, 820px) 300px; + gap: 28px; + align-items: start; +} + +.notice-content, +.notice-side { + border: 1px solid var(--line); + border-radius: 18px; + background: rgba(255,253,248,.82); + box-shadow: 0 20px 54px rgba(57, 48, 36, .08); +} + +.notice-content { + padding: 42px; +} + +.notice-content p { + color: #5f665f; + font-size: 18px; + line-height: 2; +} + +.notice-side { + padding: 24px; +} + +.notice-side a { + display: block; + margin-top: 12px; + padding: 14px 16px; + border: 1px solid var(--line); + border-radius: 14px; + color: #6f4a37; + background: #fbf7ed; + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.notice-side a:hover { + color: #fff; + background: var(--green); + transform: translateX(5px); +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { --lift: -6px; } + +@media (max-width: 900px) { + .notice-layout { grid-template-columns: 1fr; } +} + +@media (max-width: 720px) { + .hero-copy { padding-left: 0; } + .hero-copy:before { display: none; } +} diff --git a/public/css/plaza.css b/public/css/plaza.css new file mode 100644 index 0000000..fb2e924 --- /dev/null +++ b/public/css/plaza.css @@ -0,0 +1,243 @@ +.plaza-hero .container { + min-height: 440px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.plaza-stats { + display: flex; + gap: 12px; + margin-top: 28px; + flex-wrap: wrap; +} + +.plaza-stats span { + padding: 12px 16px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 16px; + color: #6d766f; + background: rgba(255, 253, 248, .74); + box-shadow: 0 14px 32px rgba(72, 57, 38, .06); +} + +.plaza-stats b { + margin-right: 6px; + color: var(--green); + font-size: 22px; +} + +.search-card { + position: relative; + display: grid; + gap: 20px; + align-content: center; + overflow: hidden; +} + +.search-card h3 { + position: relative; + z-index: 2; +} + +.search-ornament { + position: absolute; + right: -70px; + top: -84px; + width: 220px; + height: 220px; + border-radius: 50%; + background: radial-gradient(circle, rgba(200, 59, 50, .13), transparent 62%); + animation: plazaPulse 4s ease-in-out infinite; +} + +.search-toolbar { + position: relative; + z-index: 2; + padding: 8px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 999px; + background: rgba(255, 250, 241, .82); +} + +.search-card .input { + flex: 1 1 280px; + border: 0; + background: transparent; +} + +.search-card .tag-row { + position: relative; + z-index: 2; +} + +.family-card.featured { + min-height: 300px; + position: relative; + overflow: hidden; + display: flex; + flex-direction: column; + justify-content: flex-end; + color: #fff; + background: + linear-gradient(180deg, rgba(28, 48, 45, .18), rgba(21, 55, 51, .84)), + linear-gradient(135deg, #476f63, #a95c3e); + border-radius: 18px; + padding: 28px; + box-shadow: 0 22px 58px rgba(42, 65, 59, .16); +} + +.family-card.featured:before { + content: ""; + position: absolute; + inset: 0; + background: + linear-gradient(rgba(255, 255, 255, .08) 1px, transparent 1px), + linear-gradient(90deg, rgba(255, 255, 255, .08) 1px, transparent 1px); + background-size: 34px 34px; + opacity: .45; +} + +.family-card.featured h3, +.family-card.featured p, +.family-card.featured .tag-row, +.family-badge { + position: relative; + z-index: 2; +} + +.family-card.featured p { + color: rgba(255, 255, 255, .82); +} + +.family-badge { + width: fit-content; + margin-bottom: auto; + padding: 7px 12px; + border-radius: 999px; + color: var(--green-dark); + background: rgba(255, 253, 248, .86); + font-weight: 800; + font-size: 13px; +} + +.family-card-shine { + position: absolute; + left: -40%; + top: 0; + width: 34%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .28), transparent); + transform: skewX(-18deg); + transition: left .6s ease; +} + +.family-card.featured:hover .family-card-shine { + left: 112%; +} + +.family-lite { + min-height: 300px; + position: relative; + overflow: hidden; +} + +.family-lite:after { + content: ""; + position: absolute; + right: -60px; + bottom: -70px; + width: 170px; + height: 170px; + border-radius: 50%; + background: radial-gradient(circle, rgba(196, 146, 69, .13), transparent 68%); + transition: transform .28s ease, opacity .28s ease; +} + +.family-lite:hover:after { + transform: scale(1.25); + opacity: .8; +} + +.family-icon { + width: 52px; + height: 52px; + margin-bottom: 24px; + border-radius: 16px; + display: grid; + place-items: center; + color: var(--red); + background: #f7e4dc; + font-family: "SimSun", "Songti SC", serif; + font-size: 26px; + font-weight: 800; + transition: transform .26s ease, background .26s ease, color .26s ease; +} + +.family-lite:hover .family-icon { + color: #fff; + background: var(--red); + transform: rotate(-6deg) scale(1.08); +} + +.tag { + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.tag:hover { + color: #fff; + background: var(--green); + transform: translateY(-2px); +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -6px; +} + +@keyframes plazaPulse { + + 0%, + 100% { + opacity: .5; + transform: scale(.92); + } + + 50% { + opacity: 1; + transform: scale(1.08); + } +} + +@media (max-width: 720px) { + .hero-copy { + padding-left: 0; + } + + .hero-copy:before { + display: none; + } + + .search-toolbar { + border-radius: 18px; + } +} \ No newline at end of file diff --git a/public/css/profile-module.css b/public/css/profile-module.css new file mode 100644 index 0000000..e82eae2 --- /dev/null +++ b/public/css/profile-module.css @@ -0,0 +1,722 @@ +.page-profile-module { + background: #f8f4ec; +} + +.module-hero { + position: relative; + overflow: hidden; + padding: 58px 0 42px; + background: + radial-gradient(circle at 76% 18%, rgba(196, 146, 69, .16), transparent 25%), + linear-gradient(125deg, #fffaf0 0%, #f5eadb 52%, #eaf2ee 100%); +} + +.module-hero:before { + content: ""; + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(138, 50, 43, .05) 1px, transparent 1px), + linear-gradient(90deg, rgba(138, 50, 43, .05) 1px, transparent 1px); + background-size: 64px 64px; + mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent); +} + +.module-hero .container { + position: relative; + z-index: 2; +} + +.module-kicker { + width: fit-content; + margin-bottom: 18px; + padding: 8px 14px; + border-radius: 999px; + color: var(--red); + background: rgba(248, 229, 223, .92); + font-weight: 900; +} + +.module-title-row { + display: flex; + justify-content: space-between; + gap: 22px; + align-items: flex-end; +} + +.module-title-row h1 { + margin-bottom: 12px; + font-size: 48px; +} + +.module-title-row p { + max-width: 780px; + margin: 0; + color: #636c65; + font-size: 17px; + line-height: 1.9; +} + +.module-layout { + display: grid; + grid-template-columns: 280px minmax(0, 1fr); + gap: 24px; + align-items: start; +} + +.module-nav, +.module-panel, +.module-card { + border: 1px solid rgba(224, 211, 189, .86); + border-radius: 18px; + background: rgba(255,253,248,.88); + box-shadow: 0 22px 58px rgba(57, 48, 36, .08); +} + +.module-nav { + position: sticky; + top: 106px; + display: grid; + gap: 8px; + padding: 16px; +} + +.module-nav a { + padding: 14px 16px; + border-radius: 14px; + color: #5e665f; + font-weight: 900; + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.module-nav a:hover, +.module-nav a.active { + color: var(--red); + background: #f8e5df; + transform: translateX(4px); +} + +.module-main { + display: grid; + gap: 20px; +} + +.module-panel { + padding: 28px; +} + +.module-panel h2 { + margin-bottom: 10px; + font-family: "SimSun", "Songti SC", serif; + font-size: 32px; +} + +.module-panel > p { + color: var(--muted); + line-height: 1.85; +} + +.module-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 18px; +} + +.module-grid.two { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.module-card { + position: relative; + overflow: hidden; + min-height: 170px; + padding: 22px; + transition: transform .22s ease, border-color .22s ease, box-shadow .22s ease; +} + +.module-action-card { + /* 按钮型模块卡片用于消息中心操作,保留卡片视觉但去掉浏览器按钮默认样式 */ + width: 100%; + text-align: left; + cursor: pointer; + font: inherit; +} + +.notification-row.is-unread { + /* 未读消息用轻量背景强调,不新增内联样式 */ + border-color: rgba(200, 59, 50, .28); + background: #fff7f2; +} + +.join-apply-row { + /* 加入申请行复用 module-row,只补充审核业务的按钮对齐 */ + align-items: center; +} + +.join-apply-row .bottom-actions { + justify-content: flex-end; +} + +.generation-row { + /* 字辈谱行复用 module-row,补充按钮型 pill 的对齐和点击反馈 */ + align-items: center; +} + +.generation-row .pill { + border: 0; + cursor: pointer; + font: inherit; +} + +.lineage-row { + /* 世系人物行使用 button 承载点击详情,保留列表卡片视觉 */ + width: 100%; + text-align: left; + cursor: pointer; + font: inherit; +} + +.lineage-row.is-selected { + border-color: rgba(200, 59, 50, .38); + background: #fff6f1; +} + +.lineage-toolbar { + /* 搜索栏只做世系图页面差异布局,不放到 JS 内联样式 */ + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 12px; + margin-bottom: 16px; +} + +.lineage-toolbar input { + min-height: 46px; + padding: 12px 14px; + border: 1px solid var(--line); + border-radius: 8px; + background: #fffdf8; + color: var(--ink); + font: inherit; + outline: none; +} + +.lineage-tree-wrap { + overflow-x: auto; + padding: 18px; + border: 1px solid var(--line); + border-radius: 8px; + background: #fffdf8; +} + +.lineage-tree-wrap.is-compact { + max-height: 420px; +} + +.lineage-tree, +.lineage-tree ul { + /* 树结构只负责层级缩进,避免用 JS 计算位置 */ + display: grid; + gap: 12px; + margin: 0; + padding-left: 24px; + list-style: none; +} + +.lineage-tree { + padding-left: 0; +} + +.lineage-node { + min-width: 168px; + padding: 12px 14px; + border: 1px solid rgba(224, 211, 189, .86); + border-radius: 8px; + background: #fff7f2; + color: var(--ink); + text-align: left; + cursor: pointer; + font: inherit; +} + +.lineage-node strong, +.lineage-node span { + display: block; +} + +.lineage-node span { + margin-top: 4px; + color: var(--muted); + font-size: 13px; +} + +.member-admin-row { + /* 成员管理行保留列表结构,右侧放权限和移除操作 */ + align-items: center; +} + +.row-actions { + display: flex; + gap: 10px; + flex-wrap: wrap; + justify-content: flex-end; +} + +.pill.is-danger { + color: #8a322b; + background: #fde8e2; +} + +.article-row { + /* 谱文列表行复用模块列表视觉,单独保留类名方便后续文章样式调整 */ + align-items: center; +} + +.feed-row { + /* 家族圈动态行右侧承载点赞和评论按钮 */ + align-items: center; +} + +.feed-row small { + display: block; + margin-top: 6px; + color: var(--muted); +} + +.album-row, +.album-photo-row { + /* 相册和照片行使用独立类名,便于后续相册页单独调样式 */ + align-items: center; +} + +.album-photo-form { + margin-top: 18px; +} + +.upload-control { + /* 公共上传按钮使用 label 触发文件选择,避免 JS 注入按钮样式 */ + width: fit-content; + min-height: 42px; + display: inline-flex; + align-items: center; + justify-content: center; + margin-top: 10px; + padding: 10px 14px; + border-radius: 8px; + color: var(--red); + background: #f8e5df; + font-weight: 900; + cursor: pointer; +} + +.upload-input { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip: rect(0 0 0 0); + white-space: nowrap; +} + +.upload-status { + margin: 8px 0 0; + color: var(--muted); + font-size: 14px; +} + +.ceremony-row, +.ceremony-gift-row, +.merit-row { + /* 贺礼、献礼和功德记录行复用模块列表,仅补业务类名方便维护 */ + align-items: center; +} + +.ceremony-gift-form { + margin-top: 18px; +} + +.growth-row { + /* 成长记录行复用模块列表,单独留类名方便后续调整 */ + align-items: center; +} + +.memo-row { + /* 备忘录行复用模块列表,单独留类名方便后续调整 */ + align-items: center; +} + +.vip-package-list { + /* 会员套餐列表用网格呈现,套餐卡片由接口数据渲染 */ + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 16px; + margin: 18px 0; +} + +.vip-package-card { + min-height: 188px; + padding: 20px; + border: 1px solid rgba(224, 211, 189, .86); + border-radius: 8px; + background: #fffdf8; + color: var(--ink); + text-align: left; + cursor: pointer; + font: inherit; + transition: border-color .22s ease, box-shadow .22s ease, transform .22s ease; +} + +.vip-package-card:hover, +.vip-package-card.is-selected { + border-color: rgba(200, 59, 50, .48); + box-shadow: 0 18px 42px rgba(57, 48, 36, .1); + transform: translateY(-2px); +} + +.vip-package-name, +.vip-package-card strong, +.vip-package-card small { + display: block; +} + +.vip-package-name { + margin-bottom: 12px; + color: var(--red); + font-weight: 900; +} + +.vip-package-card strong { + margin-bottom: 6px; + font-size: 30px; +} + +.vip-package-card small { + color: var(--muted); +} + +.vip-package-card p { + margin: 14px 0 0; + color: var(--muted); + line-height: 1.7; +} + +.vip-order-form { + margin-top: 20px; +} + +.vip-order-row { + /* 会员订单行只补业务类名,方便和其他 module-row 分开维护 */ + align-items: center; +} + +.security-form { + /* 安全设置表单复用编辑表单结构,只补模块间距 */ + margin-top: 16px; +} + +.security-danger { + border-color: rgba(138, 50, 43, .28); + background: #fff7f2; +} + +.security-danger .editor-field input, +.security-danger .editor-field textarea { + background: #fffdf8; +} + +.region-profile-form { + /* 个人资料地区表单使用公共地区选择器,样式集中在 CSS */ + margin-top: 16px; +} + +.profile-region-picker { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; +} + +.profile-region-picker select { + min-height: 48px; + padding: 12px 14px; + border: 1px solid var(--line); + border-radius: 8px; + background: #fffdf8; + color: var(--ink); + font: inherit; + outline: none; +} + +.profile-create-family-form { + /* 个人中心创建家谱页复用编辑表单,只保留页面级间距 */ + margin-top: 16px; +} + +.module-card:hover { + border-color: rgba(196, 146, 69, .42); + box-shadow: 0 24px 60px rgba(57, 48, 36, .11); +} + +.module-card.is-selected { + border-color: rgba(200, 59, 50, .5); + background: #fff6f1; + box-shadow: 0 24px 60px rgba(200, 59, 50, .12); +} + +.module-card:after { + content: ""; + position: absolute; + inset: 0 auto 0 -42%; + width: 34%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,.52), transparent); + transform: skewX(-18deg); + transition: left .54s ease; +} + +.module-card:hover:after { + left: 112%; +} + +.module-card .icon { + width: 46px; + height: 46px; + margin-bottom: 18px; + border-radius: 14px; + display: grid; + place-items: center; + color: var(--red); + background: #f8e5df; + font-weight: 900; +} + +.module-card h3 { + margin-bottom: 8px; + font-size: 23px; +} + +.module-card p { + color: var(--muted); + line-height: 1.75; +} + +.module-list { + display: grid; + gap: 14px; +} + +.module-row { + display: grid; + grid-template-columns: 1fr auto; + gap: 16px; + align-items: center; + padding: 18px 20px; + border: 1px solid var(--line); + border-radius: 15px; + background: #fffdf8; +} + +.module-row h3 { + margin-bottom: 4px; + font-size: 22px; +} + +.module-row p { + margin: 0; + color: var(--muted); +} + +.pill { + padding: 7px 12px; + border-radius: 999px; + color: var(--red); + background: #f8e5df; + font-weight: 900; + white-space: nowrap; +} + +.form-like { + display: grid; + overflow: hidden; + border: 1px solid var(--line); + border-radius: 16px; + background: #fffdf8; +} + +.form-like p { + display: grid; + grid-template-columns: 170px 1fr auto; + gap: 16px; + margin: 0; + padding: 18px 20px; + border-bottom: 1px solid var(--line); + align-items: center; +} + +.form-like p:last-child { + border-bottom: 0; +} + +.form-like span { + color: var(--muted); +} + +.form-like b { + color: var(--ink); +} + +.bottom-actions { + display: flex; + gap: 14px; + flex-wrap: wrap; +} + +.editor-form { + display: grid; + gap: 18px; +} + +.profile-data-form { + /* 个人资料编辑表单只补充本页间距,基础控件继续复用 editor-form。 */ + margin-top: 14px; +} + +.editor-field { + display: grid; + gap: 10px; +} + +.editor-field label { + color: var(--ink); + font-weight: 900; +} + +.editor-field input, +.editor-field select, +.editor-field textarea { + width: 100%; + min-height: 48px; + padding: 12px 14px; + border: 1px solid var(--line); + border-radius: 8px; + background: #fffdf8; + color: var(--ink); + font: inherit; + outline: none; + transition: border-color .2s ease, box-shadow .2s ease; +} + +.editor-field textarea { + min-height: 160px; + resize: vertical; +} + +.editor-field input:focus, +.editor-field select:focus, +.editor-field textarea:focus { + border-color: rgba(200, 59, 50, .45); + box-shadow: 0 0 0 4px rgba(200, 59, 50, .08); +} + +.editor-two { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; +} + +.profile-form-actions { + /* 保存状态跟随按钮排列,避免用 JS 写入临时样式。 */ + align-items: center; +} + +.form-status { + color: var(--muted); + font-weight: 800; +} + +.generation-batch-form { + /* 批量字辈表单复用编辑表单,只补充本页预览前的间距。 */ + margin-top: 14px; +} + +.generation-batch-check { + display: flex; + gap: 10px; + align-items: center; + color: var(--muted); + font-weight: 800; +} + +.generation-batch-check input { + width: 18px; + height: 18px; + accent-color: var(--red); +} + +.generation-batch-actions { + align-items: center; +} + +.generation-batch-preview { + margin-top: 18px; +} + +.upload-box { + min-height: 136px; + border: 1px dashed rgba(138, 50, 43, .28); + border-radius: 8px; + display: grid; + place-items: center; + color: var(--red); + background: #fff8f2; + font-weight: 900; + cursor: pointer; +} + +.switch-row { + display: flex; + gap: 18px; + flex-wrap: wrap; + align-items: center; +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -5px; +} + +@media (max-width: 1060px) { + .module-layout, + .module-grid, + .module-grid.two, + .vip-package-list { + grid-template-columns: 1fr; + } + + .module-nav { + position: static; + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 720px) { + .module-title-row { + display: grid; + } + + .module-title-row h1 { + font-size: 38px; + } + + .module-nav { + grid-template-columns: 1fr; + } + + .form-like p, + .module-row, + .editor-two, + .profile-region-picker { + grid-template-columns: 1fr; + } +} diff --git a/public/css/profile.css b/public/css/profile.css new file mode 100644 index 0000000..bd360ca --- /dev/null +++ b/public/css/profile.css @@ -0,0 +1,613 @@ +.page-profile { + background: #f8f4ec; +} + +.profile-hero { + position: relative; + overflow: hidden; + padding: 62px 0 34px; + background: + radial-gradient(circle at 78% 12%, rgba(196, 146, 69, .18), transparent 25%), + linear-gradient(125deg, #fffaf0 0%, #f4eadb 48%, #eaf2ee 100%); +} + +.profile-hero:before { + content: ""; + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(138, 50, 43, .05) 1px, transparent 1px), + linear-gradient(90deg, rgba(138, 50, 43, .05) 1px, transparent 1px); + background-size: 64px 64px; + mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent); +} + +.profile-hero-grid { + position: relative; + z-index: 2; + display: grid; + grid-template-columns: minmax(0, 1fr) 420px; + gap: 24px; + align-items: stretch; +} + +.profile-card, +.profile-stats, +.panel, +.side-card { + border: 1px solid rgba(224, 211, 189, .84); + border-radius: 18px; + background: rgba(255, 253, 248, .86); + box-shadow: 0 22px 58px rgba(57, 48, 36, .08); +} + +.profile-card { + display: grid; + grid-template-columns: 116px 1fr; + gap: 28px; + padding: 34px; + align-items: center; +} + +.avatar-wrap { + position: relative; + width: 108px; + height: 108px; +} + +.avatar { + width: 108px; + height: 108px; + border-radius: 28px; + display: grid; + place-items: center; + color: #fff; + background: + radial-gradient(circle at 30% 24%, rgba(255, 255, 255, .38), transparent 24%), + linear-gradient(145deg, var(--red), #ee8d60 52%, var(--green)); + font-family: "SimSun", "Songti SC", serif; + font-size: 46px; + font-weight: 900; + box-shadow: 0 18px 40px rgba(200, 59, 50, .18); +} + +.avatar-wrap span { + position: absolute; + right: -4px; + bottom: -4px; + width: 28px; + height: 28px; + border: 5px solid #fffdf8; + border-radius: 50%; + background: #31bf6b; +} + +.profile-info h1 { + margin-bottom: 12px; + font-size: 44px; +} + +.profile-info p { + max-width: 720px; + color: #636c65; + font-size: 17px; + line-height: 1.9; +} + +.profile-tags, +.mini-actions { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.profile-tags span, +.mini-actions span { + padding: 7px 12px; + border-radius: 999px; + color: var(--red); + background: #f8e5df; + font-size: 14px; + font-weight: 800; +} + +.profile-stats { + display: grid; + grid-template-columns: repeat(2, 1fr); + padding: 22px; + gap: 14px; +} + +.profile-stats div { + min-height: 116px; + padding: 20px; + border-radius: 14px; + display: grid; + align-content: center; + background: rgba(247, 239, 226, .72); +} + +.profile-stats strong { + color: var(--green); + font-size: 34px; + line-height: 1; +} + +.profile-stats span, +.panel-head p, +.data-list span, +.todo-list span, +.family-card p, +.feature-tile p, +.content-tile p { + color: var(--muted); + line-height: 1.75; +} + +.dashboard-section { + padding-top: 42px; +} + +.dashboard-layout { + display: grid; + grid-template-columns: 300px minmax(0, 1fr); + gap: 24px; + align-items: start; +} + +.dashboard-side { + position: sticky; + top: 106px; + display: grid; + gap: 20px; +} + +.side-card, +.panel { + padding: 26px; +} + +.side-card h2, +.panel h2 { + margin-bottom: 8px; + font-family: "SimSun", "Songti SC", serif; + font-size: 30px; +} + +.user-actions { + display: grid; + gap: 10px; +} + +.user-actions a, +.todo-list a { + display: block; + padding: 15px 16px; + border: 1px solid var(--line); + border-radius: 14px; + background: #fbf7ed; + transition: transform .22s ease, border-color .22s ease, background .22s ease, color .22s ease; +} + +.user-actions a:hover, +.todo-list a:hover { + transform: translateX(5px); + border-color: rgba(71, 111, 99, .28); + background: #eef4ef; +} + +.user-actions span, +.todo-list b { + display: block; + color: var(--ink); + font-weight: 900; +} + +.user-actions b { + display: block; + margin-top: 4px; + color: var(--muted); + font-size: 13px; +} + +.logout-link span { + color: var(--red); +} + +.card-head, +.panel-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 18px; + margin-bottom: 22px; +} + +.card-head span { + padding: 6px 11px; + border-radius: 999px; + color: #fff; + background: var(--red); + font-weight: 800; +} + +.todo-list, +.dashboard-main { + display: grid; + gap: 18px; +} + +.panel-head.compact { + margin-bottom: 18px; +} + +.panel-head h2 { + margin-bottom: 6px; +} + +.family-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 18px; +} + +.family-card { + display: grid; + grid-template-columns: 112px 1fr; + gap: 20px; + min-height: 190px; + padding: 20px; + border: 1px solid var(--line); + border-radius: 16px; + background: #fffdf8; + transition: transform .22s ease, border-color .22s ease, box-shadow .22s ease; +} + +.family-card:hover, +.feature-tile:hover, +.content-tile:hover { + border-color: rgba(196, 146, 69, .42); + box-shadow: 0 22px 52px rgba(57, 48, 36, .1); +} + +.book-cover { + min-height: 150px; + border-radius: 10px; + display: grid; + place-items: center; + color: #fffdf8; + background: + linear-gradient(90deg, transparent calc(100% - 16px), rgba(255, 255, 255, .7) calc(100% - 15px), transparent calc(100% - 14px)), + linear-gradient(135deg, #0f405c, #092c42); + font-family: "SimSun", "Songti SC", serif; + font-size: 22px; + line-height: 1.7; + text-align: center; + box-shadow: inset -12px 0 0 rgba(255, 255, 255, .08); +} + +.book-cover.alt { + background: + linear-gradient(90deg, transparent calc(100% - 16px), rgba(255, 255, 255, .7) calc(100% - 15px), transparent calc(100% - 14px)), + linear-gradient(135deg, #173c35, #785f35); +} + +.family-card h3, +.feature-tile h3, +.content-tile h3 { + margin-bottom: 8px; + font-size: 24px; +} + +.feature-grid, +.content-grid, +.service-grid { + display: grid; + gap: 16px; +} + +.management-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.feature-tile, +.content-tile { + position: relative; + overflow: hidden; + min-height: 170px; + padding: 22px; + border: 1px solid var(--line); + border-radius: 16px; + background: #fffdf8; + transition: transform .22s ease, border-color .22s ease, box-shadow .22s ease; +} + +.feature-tile span, +.content-tile span { + width: 48px; + height: 48px; + margin-bottom: 18px; + border-radius: 14px; + display: grid; + place-items: center; + color: var(--red); + background: #f7e4dc; + font-weight: 900; +} + +.feature-tile:after, +.content-tile:after { + content: ""; + position: absolute; + inset: 0 auto 0 -42%; + width: 34%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .52), transparent); + transform: skewX(-18deg); + transition: left .54s ease; +} + +.feature-tile:hover:after, +.content-tile:hover:after { + left: 112%; +} + +.content-grid { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.content-tile.large { + grid-column: span 2; + color: #fff; + background: linear-gradient(145deg, var(--green-dark), var(--green)); +} + +.content-tile.large p, +.content-tile.large h3 { + color: rgba(255, 255, 255, .86); +} + +.content-tile.large span { + color: var(--green-dark); + background: rgba(255, 253, 248, .9); +} + +.profile-data-layout { + display: grid; + grid-template-columns: 1.15fr .85fr; + gap: 18px; +} + +.link-btn { + flex: 0 0 auto; + padding: 9px 14px; + border-radius: 999px; + color: var(--red); + background: #f8e5df; + font-weight: 900; +} + +.data-list { + display: grid; + border-top: 1px solid var(--line); +} + +.data-list p { + display: grid; + grid-template-columns: 150px 1fr; + gap: 16px; + margin: 0; + padding: 16px 0; + border-bottom: 1px solid var(--line); +} + +.data-list b { + color: var(--ink); +} + +.service-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.service-grid a { + min-height: 88px; + padding: 18px; + border-radius: 14px; + display: grid; + place-items: center; + color: #6f4a37; + background: #fbf7ed; + border: 1px solid var(--line); + font-weight: 900; + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.service-grid a:hover { + color: #fff; + background: var(--green); + transform: translateY(-3px); +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -5px; +} + +.logout-modal { + position: fixed; + inset: 0; + z-index: 80; + display: grid; + place-items: center; + padding: 24px; + opacity: 0; + pointer-events: none; + transition: opacity .22s ease; +} + +.logout-modal.show { + opacity: 1; + pointer-events: auto; +} + +.logout-modal-backdrop { + position: absolute; + inset: 0; + background: rgba(32, 35, 34, .46); + backdrop-filter: blur(10px); +} + +.logout-dialog { + position: relative; + z-index: 1; + width: min(420px, 100%); + padding: 34px; + border-radius: 8px; + background: #fff; + box-shadow: 0 28px 80px rgba(61, 73, 68, .24); + text-align: center; + transform: translateY(12px) scale(.98); + transition: transform .22s ease; +} + +.logout-modal.show .logout-dialog { + transform: translateY(0) scale(1); +} + +.logout-close { + position: absolute; + top: 14px; + right: 14px; + width: 34px; + height: 34px; + border: 0; + border-radius: 50%; + background: #f4f1eb; + color: #7f786f; + font-size: 24px; + line-height: 1; + cursor: pointer; + transition: transform .2s ease, background .2s ease, color .2s ease; +} + +.logout-close:hover { + transform: rotate(90deg); + background: var(--red-soft); + color: var(--red); +} + +.logout-icon { + display: inline-grid; + place-items: center; + width: 58px; + height: 58px; + border-radius: 50%; + margin-bottom: 16px; + background: var(--red-soft); + color: var(--red); + font-weight: 800; +} + +.logout-dialog h2 { + margin: 0 0 10px; + font-size: 24px; + color: var(--ink); +} + +.logout-dialog p { + margin: 0; + color: var(--muted); + line-height: 1.8; +} + +.logout-actions { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; + margin-top: 26px; +} + +.logout-actions .btn { + justify-content: center; +} + +.logout-modal-open { + overflow: hidden; +} + +@media (max-width: 1120px) { + + .profile-hero-grid, + .dashboard-layout, + .profile-data-layout { + grid-template-columns: 1fr; + } + + .dashboard-side { + position: static; + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .content-grid, + .management-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 760px) { + .profile-hero { + padding-top: 36px; + } + + .profile-card { + grid-template-columns: 1fr; + padding: 24px; + } + + .profile-info h1 { + font-size: 36px; + } + + .profile-stats, + .dashboard-side, + .family-grid, + .content-grid, + .management-grid, + .service-grid { + grid-template-columns: 1fr; + } + + .family-card { + grid-template-columns: 88px 1fr; + } + + .book-cover { + min-height: 120px; + font-size: 18px; + } + + .content-tile.large { + grid-column: span 1; + } + + .panel-head, + .card-head { + display: grid; + } + + .data-list p { + grid-template-columns: 1fr; + gap: 4px; + } + + .logout-dialog { + padding: 30px 22px; + } + + .logout-actions { + grid-template-columns: 1fr; + } +} \ No newline at end of file diff --git a/public/css/promo-album.css b/public/css/promo-album.css new file mode 100644 index 0000000..afcb4fd --- /dev/null +++ b/public/css/promo-album.css @@ -0,0 +1,126 @@ +.promo-hero .container { + min-height: 440px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.promo-tags { + display: flex; + gap: 12px; + margin-top: 28px; + flex-wrap: wrap; +} + +.promo-tags span { + padding: 10px 14px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 999px; + color: #6d766f; + background: rgba(255, 253, 248, .74); + font-weight: 800; + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.promo-tags span:hover { + color: #fff; + background: var(--green); + transform: translateY(-3px); +} + +.album-preview { + display: grid; + grid-template-columns: 1.2fr 1fr; + grid-template-rows: repeat(2, 130px); + gap: 14px; + overflow: hidden; +} + +.album-photo { + border-radius: 18px; + background: radial-gradient(circle at 72% 24%, rgba(255, 255, 255, .75) 0 8%, transparent 9%), linear-gradient(135deg, #e7c89e, #b94135 48%, #143d57); + box-shadow: 0 16px 36px rgba(57, 48, 36, .08); + transition: transform .26s ease; +} + +.album-photo.large { + grid-row: 1 / 3; + background: linear-gradient(180deg, rgba(28, 48, 45, .1), rgba(21, 55, 51, .72)), url("../images/ancestral-hall.png") center/cover; +} + +.album-photo.warm { + background: linear-gradient(135deg, #f0d8ac, #c49245 45%, #7d342d); +} + +.album-photo.green { + background: linear-gradient(135deg, #dfd1b5, #6b7f70 48%, #26302c); +} + +.album-preview:hover .album-photo { + transform: scale(1.025); +} + +.album-card { + min-height: 180px; + position: relative; + overflow: hidden; +} + +.album-card:after { + content: ""; + position: absolute; + left: -42%; + top: 0; + width: 36%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .54), transparent); + transform: skewX(-18deg); + transition: left .56s ease; +} + +.album-card:hover:after { + left: 112%; +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -6px; +} + +@media (max-width: 720px) { + .hero-copy { + padding-left: 0; + } + + .hero-copy:before { + display: none; + } + + .album-preview { + grid-template-columns: 1fr; + grid-template-rows: repeat(4, 120px); + } + + .album-photo.large { + grid-row: auto; + } +} \ No newline at end of file diff --git a/public/css/promo-video.css b/public/css/promo-video.css new file mode 100644 index 0000000..c0c63ff --- /dev/null +++ b/public/css/promo-video.css @@ -0,0 +1,140 @@ +.promo-hero .container { + min-height: 440px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.promo-tags { + display: flex; + gap: 12px; + margin-top: 28px; + flex-wrap: wrap; +} + +.promo-tags span { + padding: 10px 14px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 999px; + color: #6d766f; + background: rgba(255, 253, 248, .74); + font-weight: 800; + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.promo-tags span:hover { + color: #fff; + background: var(--green); + transform: translateY(-3px); +} + +.video-preview { + min-height: 300px; + position: relative; + overflow: hidden; + display: flex; + flex-direction: column; + justify-content: flex-end; + color: #fff; + background: linear-gradient(180deg, rgba(31, 63, 55, .1), rgba(25, 39, 34, .86)), url("../images/ancestral-hall.png") center/cover; +} + +.video-preview:before { + content: ""; + position: absolute; + inset: 0; + background: linear-gradient(rgba(255, 255, 255, .08) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, .08) 1px, transparent 1px); + background-size: 34px 34px; + opacity: .35; +} + +.video-preview h3, +.video-preview p, +.play-button { + position: relative; + z-index: 2; +} + +.video-preview p { + color: rgba(255, 255, 255, .8); +} + +.play-button { + width: 64px; + height: 64px; + margin-bottom: 46px; + border-radius: 50%; + background: rgba(255, 255, 255, .94); + box-shadow: 0 14px 34px rgba(0, 0, 0, .2); + transition: transform .24s ease; +} + +.play-button:after { + content: ""; + position: absolute; + left: 26px; + top: 19px; + border-top: 13px solid transparent; + border-bottom: 13px solid transparent; + border-left: 20px solid var(--red); +} + +.video-preview:hover .play-button { + transform: scale(1.08); +} + +.video-card { + min-height: 180px; + position: relative; + overflow: hidden; +} + +.video-card:after { + content: ""; + position: absolute; + left: -42%; + top: 0; + width: 36%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .54), transparent); + transform: skewX(-18deg); + transition: left .56s ease; +} + +.video-card:hover:after { + left: 112%; +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -6px; +} + +@media (max-width: 720px) { + .hero-copy { + padding-left: 0; + } + + .hero-copy:before { + display: none; + } +} \ No newline at end of file diff --git a/public/css/public.css b/public/css/public.css new file mode 100644 index 0000000..daa11fe --- /dev/null +++ b/public/css/public.css @@ -0,0 +1,900 @@ +:root { + --red: #c83b32; + --red-soft: #f4ded8; + --green: #476f63; + --green-dark: #213f38; + --gold: #c49245; + --ink: #26302c; + --muted: #737b76; + --paper: #fbf7ed; + --paper-2: #f7efe2; + --line: #eadfce; + --white: #fffdf8; + --shadow: 0 24px 70px rgba(69, 53, 35, .12); +} + +.auth-layer-message { + /* 认证页 layui 消息提示皮肤,替代浏览器原生 alert。 */ + min-width: 160px; + border-radius: 999px !important; + background: rgba(38, 48, 44, .94) !important; + box-shadow: 0 16px 42px rgba(38, 48, 44, .24) !important; +} + +.auth-layer-message .layui-layer-content { + padding: 10px 18px !important; + color: #fffdf8 !important; + font-size: 14px; + font-weight: 800; + line-height: 1.6; + text-align: center; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + color: var(--ink); + font-family: "Microsoft YaHei", "PingFang SC", Arial, sans-serif; + background: var(--paper); +} + +a { + color: inherit; + text-decoration: none; +} + +img { + max-width: 100%; + display: block; +} + +@media (hover: hover) and (pointer: fine) { + + a, + .btn, + .card, + button, + summary { + cursor: pointer; + } +} + +.container { + width: min(1280px, calc(100% - 72px)); + margin: 0 auto; +} + +.site-header { + position: sticky; + top: 0; + z-index: 20; + background: + linear-gradient(115deg, rgba(110, 38, 32, .96) 0%, rgba(200, 59, 50, .96) 48%, rgba(139, 52, 39, .96) 100%); + border-bottom: 1px solid rgba(255, 253, 248, .2); + box-shadow: + 0 4px 0 rgba(93, 30, 26, .2), + 0 18px 38px rgba(83, 32, 26, .34), + 0 30px 70px rgba(83, 32, 26, .22); + backdrop-filter: blur(18px); +} + +.nav { + height: 82px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 28px; +} + +.brand { + display: flex !important; + align-items: center; + gap: 12px; + padding: 8px 14px; + border-radius: 999px; + background: rgba(255, 253, 248, .92); + box-shadow: 0 10px 28px rgba(63, 28, 22, .18); + font-size: 24px; + font-weight: 800; + white-space: nowrap; +} + +.brand-logo { + display: block; + width: 270px; + height: auto; +} + +.footer .brand-logo { + width: 270px; +} + +.auth-page-brand .brand-logo { + width: 220px; +} + +.brand-logo-mark { + width: 48px; + height: 48px; +} + +.brand-text { + color: var(--ink); + font-weight: 900; +} + +.brand-mark { + width: 40px; + height: 40px; + border-radius: 50%; + display: grid; + place-items: center; + color: #fff; + background: var(--red); + box-shadow: 0 10px 24px rgba(200, 59, 50, .22); + font-weight: 800; +} + +.nav-links { + display: flex; + align-items: center; + gap: 26px; + color: rgba(255, 253, 248, .78); + font-size: 15px; + white-space: nowrap; +} + +.nav-links a.active, +.nav-links a:hover { + color: #fff7d8; +} + +.nav-links a, +.nav-actions a { + position: relative; + transition: color .22s ease, transform .22s ease; +} + +.nav-links a:after { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: -10px; + height: 2px; + border-radius: 999px; + background: linear-gradient(90deg, #f5d99b, #fff3c8); + transform: scaleX(0); + transform-origin: center; + transition: transform .24s ease; +} + +.nav-links a.active:after, +.nav-links a:hover:after { + transform: scaleX(1); +} + +.nav-actions { + display: flex; + align-items: center; + gap: 14px; + color: rgba(255, 253, 248, .86); + font-size: 15px; + white-space: nowrap; +} + +.btn { + min-width: 118px; + height: 44px; + padding: 0 24px; + border-radius: 999px; + display: inline-flex; + align-items: center; + justify-content: center; + border: 1px solid transparent; + font-weight: 800; + transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease, background .22s ease; + will-change: transform; +} + +.btn:hover { + transform: translateY(-2px); +} + +.btn.primary { + color: #fff; + background: var(--red); + box-shadow: 0 14px 30px rgba(200, 59, 50, .24); +} + +.btn.primary:hover { + box-shadow: 0 18px 38px rgba(200, 59, 50, .3); +} + +.site-header .btn.primary { + color: #8f332b; + background: linear-gradient(135deg, #fff8e6, #f1d49a); + box-shadow: 0 12px 28px rgba(64, 26, 21, .22); +} + +.site-header .btn.primary:hover { + box-shadow: 0 16px 34px rgba(64, 26, 21, .3); +} + +.btn.ghost { + color: var(--red); + border-color: rgba(200, 59, 50, .28); + background: rgba(255, 255, 255, .68); +} + +.inner-hero { + position: relative; + overflow: hidden; + background: + radial-gradient(circle at 82% 12%, rgba(196, 146, 69, .16), transparent 24%), + linear-gradient(125deg, #fffaf0 0%, #f7efe2 48%, #edf4ef 100%); +} + +.inner-hero:before { + content: ""; + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(138, 50, 43, .05) 1px, transparent 1px), + linear-gradient(90deg, rgba(138, 50, 43, .05) 1px, transparent 1px); + background-size: 68px 68px; + mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent); +} + +.inner-hero .container { + position: relative; + z-index: 2; + min-height: 410px; + padding: 68px 0 72px; + display: grid; + grid-template-columns: .9fr 1.1fr; + gap: 54px; + align-items: center; +} + +.eyebrow { + display: inline-flex; + align-items: center; + gap: 12px; + margin-bottom: 22px; + color: #8a5a3b; + font-weight: 800; +} + +.eyebrow:before { + content: ""; + width: 42px; + height: 2px; + background: var(--red); +} + +h1, +h2, +h3, +h4, +p { + margin-top: 0; +} + +h1 { + margin-bottom: 20px; + color: var(--ink); + font-family: "SimSun", "Songti SC", serif; + font-size: 52px; + line-height: 1.16; +} + +h2 { + margin-bottom: 14px; + font-family: "SimSun", "Songti SC", serif; + font-size: 42px; + line-height: 1.25; +} + +h3 { + margin-bottom: 12px; + font-size: 23px; +} + +.lead { + max-width: 620px; + margin-bottom: 0; + color: #645d52; + font-size: 18px; + line-height: 1.9; +} + +.hero-card { + min-height: 260px; + padding: 28px; + border: 1px solid rgba(224, 211, 189, .88); + border-radius: 24px; + background: rgba(255, 253, 248, .92); + box-shadow: var(--shadow); +} + +.section { + padding: 76px 0; +} + +.section.alt { + background: #fffdf8; +} + +.section-head { + margin-bottom: 34px; + text-align: center; +} + +.section-head p { + max-width: 720px; + margin: 14px auto 0; + color: var(--muted); + font-size: 17px; + line-height: 1.9; +} + +.grid-2, +.grid-3, +.grid-4 { + display: grid; + gap: 22px; +} + +.grid-2 { + grid-template-columns: repeat(2, 1fr); +} + +.grid-3 { + grid-template-columns: repeat(3, 1fr); +} + +.grid-4 { + grid-template-columns: repeat(4, 1fr); +} + +.card { + padding: 24px; + border-radius: 18px; + background: #fffdf8; + border: 1px solid var(--line); + box-shadow: 0 16px 42px rgba(57, 48, 36, .07); + transition: transform .24s ease, box-shadow .24s ease, border-color .24s ease, background .24s ease; + will-change: transform; +} + +.card:hover { + transform: translateY(-6px); + border-color: rgba(196, 146, 69, .38); + box-shadow: 0 24px 60px rgba(57, 48, 36, .11); +} + +.card.soft { + background: #fbf7ed; +} + +.card.dark { + color: #fff; + background: linear-gradient(155deg, var(--green-dark), var(--green)); +} + +.card.dark p { + color: rgba(255, 255, 255, .78); +} + +.card p, +.muted { + color: var(--muted); + line-height: 1.8; +} + +.tag-row { + display: flex; + gap: 10px; + flex-wrap: wrap; +} + +.tag { + padding: 6px 12px; + border-radius: 999px; + color: var(--green); + background: #eaf0eb; + font-size: 14px; + font-weight: 700; +} + +.toolbar { + display: flex; + gap: 12px; + align-items: center; + flex-wrap: wrap; +} + +.input, +select, +textarea { + width: 100%; + min-height: 48px; + padding: 0 16px; + border: 1px solid #e2d4bf; + border-radius: 999px; + color: var(--ink); + background: #fbf8f1; + font: inherit; +} + +textarea { + min-height: 128px; + padding: 14px 16px; + border-radius: 18px; + resize: vertical; +} + +.form-grid { + display: grid; + gap: 16px; +} + +.form-card { + max-width: 560px; + margin: 0 auto; + padding: 34px; +} + +.auth-layout { + min-height: calc(100vh - 82px); + display: grid; + place-items: center; + padding: 62px 24px; + background: + radial-gradient(circle at 18% 20%, rgba(200, 59, 50, .09), transparent 24%), + linear-gradient(125deg, #fffaf0, #f7efe2 54%, #edf4ef); +} + +/* 认证类页面共用祖堂背景,登录、注册、找回密码只保留自己的差异样式 */ +.auth-scenic-layout { + position: relative; + isolation: isolate; + min-height: 100vh; + padding: 56px 24px 72px; + place-items: center; + background: + linear-gradient(90deg, rgba(255, 253, 248, .86), rgba(255, 253, 248, .46) 45%, rgba(255, 253, 248, .88)), + linear-gradient(135deg, rgba(200, 59, 50, .08), transparent 34%), + url("../images/ancestral-hall-new.png") center / cover no-repeat; +} + +.auth-scenic-layout:before { + content: ""; + position: absolute; + inset: 0; + z-index: -1; + background: + linear-gradient(180deg, rgba(251, 247, 237, .42), rgba(251, 247, 237, .86)), + radial-gradient(circle at 50% 42%, rgba(255, 253, 248, .3), transparent 30%); + backdrop-filter: blur(2px); +} + +.auth-page-brand { + position: absolute; + left: clamp(18px, 4vw, 56px); + top: clamp(18px, 4vw, 42px); + z-index: 3; +} + +/* 认证卡片公共样式,避免登录、注册、找回密码页面重复维护 */ +.auth-card { + width: min(440px, 100%); + padding: 38px; + border-radius: 16px; + border-color: rgba(224, 211, 189, .9); + background: rgba(255, 253, 248, .94); + box-shadow: 0 26px 70px rgba(57, 48, 36, .16); +} + +.auth-card:hover { + transform: none; +} + +.auth-card h1 { + margin-bottom: 12px; + font-size: 40px; +} + +.auth-card .lead { + margin-bottom: 28px; + font-size: 16px; +} + +.auth-card .form-grid { + gap: 14px; +} + +.auth-card .input { + height: 50px; + background: #fffaf1; +} + +.auth-card .btn.primary, +.auth-card .btn.ghost { + position: relative; + overflow: hidden; + height: 50px; +} + +.auth-card .btn.primary { + width: 100%; + margin-top: 2px; +} + +.auth-card .btn.primary:before, +.auth-card .btn.ghost:before { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: -45%; + width: 38%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .34), transparent); + transform: skewX(-18deg); + transition: left .48s ease; +} + +.auth-card .btn.primary:hover, +.auth-card .btn.ghost:hover { + transform: translateY(-3px) scale(1.012); +} + +.auth-card .btn.primary:hover { + box-shadow: 0 18px 42px rgba(200, 59, 50, .34); +} + +.auth-card .btn.ghost:hover { + color: #fff; + border-color: rgba(200, 59, 50, .2); + background: var(--red); + box-shadow: 0 16px 34px rgba(200, 59, 50, .2); +} + +.auth-card .btn.primary:hover:before, +.auth-card .btn.ghost:hover:before { + left: 110%; +} + +.auth-card .btn.primary:active, +.auth-card .btn.ghost:active { + transform: translateY(-1px) scale(.99); +} + +.auth-links { + display: flex; + justify-content: space-between; + gap: 16px; + color: #8a5a3b; + font-weight: 800; +} + +.auth-links a { + transition: color .22s ease, transform .22s ease; +} + +.auth-links a:hover { + color: var(--red); + transform: translateY(-1px); +} + +.auth-captcha-box { + /* 认证页滑动验证挂载点,TAC 内部 DOM 由第三方库生成 */ + display: flex; + justify-content: center; +} + +.auth-captcha-box:empty { + display: none; +} + +.auth-captcha-box #tianai-captcha-parent { + max-width: 100%; +} + +.auth-captcha-modal { + /* 认证页滑动验证弹窗挂载点,TAC 内部 DOM 由第三方库生成 */ + position: fixed; + inset: 0; + z-index: 1000; + display: grid; + place-items: center; + padding: 24px; + background: rgba(38, 48, 44, .32); + backdrop-filter: blur(4px); +} + +.auth-captcha-modal:empty { + display: none; +} + +.auth-captcha-modal #tianai-captcha-parent { + max-width: min(318px, calc(100vw - 32px)); + max-height: calc(100vh - 32px); +} + +/* 验证码输入和按钮的通用双列布局,窄屏在页面 CSS 中改为单列 */ +.code-row { + display: grid; + grid-template-columns: 1fr auto; + gap: 12px; +} + +/* 接口请求期间的公共状态,只由 JS 切换 class,不在 JS 内写样式 */ +.is-loading { + pointer-events: none; + opacity: .72; +} + +/* 接口列表为空时的统一占位样式 */ +.api-empty { + padding: 22px; + border: 1px dashed var(--line); + border-radius: 14px; + color: var(--muted); + background: rgba(255, 253, 248, .7); + text-align: center; + font-weight: 800; +} + +.footer { + padding: 60px 0 36px; + color: rgba(255, 255, 255, .78); + background: #3f3a35; +} + +.footer-grid { + display: grid; + grid-template-columns: 1.2fr repeat(3, 1fr); + gap: 48px; + padding-bottom: 38px; + border-bottom: 1px solid rgba(255, 255, 255, .12); +} + +.footer h4 { + margin: 0 0 18px; + color: #fff; + font-size: 17px; +} + +.footer p, +.footer a { + display: block; + color: inherit; + margin: 10px 0; + line-height: 1.8; +} + +.footer a { + width: fit-content; + transition: color .22s ease, transform .22s ease; +} + +.footer a:hover { + color: #fff; + transform: translateX(4px); +} + +.footer .brand { + padding: 0; + border-radius: 0; + background: transparent; + box-shadow: none; +} + +.footer .brand:hover { + transform: translateX(0) translateY(-2px); +} + +.copyright, +.footer-legal { + padding-top: 28px; + color: rgba(255, 255, 255, .48); + font-size: 14px; +} + +.footer-legal { + display: flex; + align-items: center; + justify-content: flex-start; + gap: 14px; + flex-wrap: wrap; + text-align: left; +} + +.footer-legal a { + display: inline-flex; + width: auto; + margin: 0; + color: inherit; + font-size: inherit; + line-height: inherit; +} + +.footer-legal a:hover { + color: #fff; + transform: none; +} + +.profile-layer-confirm { + border-radius: 8px !important; + overflow: hidden; +} + +.profile-layer-confirm .layui-layer-title { + height: 50px; + line-height: 50px; + border-bottom: 1px solid rgba(224, 211, 189, .72); + background: #fffaf0; + color: var(--ink); + font-weight: 900; +} + +.profile-layer-confirm .layui-layer-content { + color: var(--muted); + line-height: 1.8; +} + +.profile-layer-confirm .layui-layer-btn a { + border-radius: 999px; +} + +.profile-layer-confirm .layui-layer-btn .layui-layer-btn0 { + border-color: var(--red); + background: var(--red); +} + +.pending-layer-dialog { + border-radius: 8px !important; + overflow: hidden; + box-shadow: 0 20px 60px rgba(38, 30, 21, .18) !important; +} + +.pending-layer-dialog .layui-layer-title { + height: 52px; + line-height: 52px; + border-bottom: 1px solid rgba(224, 211, 189, .72); + background: #fffaf0; + color: var(--ink); + font-weight: 900; + font-size: 16px; +} + +.pending-layer-dialog .layui-layer-content { + padding: 28px 28px 16px !important; + color: var(--ink); + font-size: 16px; + line-height: 1.8; + text-align: center; +} + +.pending-layer-dialog .layui-layer-btn { + padding: 0 24px 24px; + text-align: center; +} + +.pending-layer-dialog .layui-layer-btn a { + height: 38px; + min-width: 108px; + padding: 0 24px; + border-radius: 999px; + line-height: 38px; + font-weight: 800; +} + +.pending-layer-dialog .layui-layer-btn .layui-layer-btn0 { + border-color: var(--red); + background: var(--red); +} + +.profile-popup-select { + display: grid; + gap: 10px; + padding: 4px 0; +} + +.profile-popup-select button { + width: 100%; + min-height: 42px; + border: 1px solid var(--line); + border-radius: 8px; + background: #fffdf8; + color: var(--ink); + font: inherit; + font-weight: 900; + cursor: pointer; + transition: border-color .2s ease, color .2s ease, background .2s ease; +} + +.profile-popup-select button:hover { + border-color: rgba(200, 59, 50, .38); + color: var(--red); + background: #fff4ef; +} + +@media (max-width: 1120px) { + .nav-links { + display: none; + } + + .inner-hero .container, + .grid-2 { + grid-template-columns: 1fr; + } + + .grid-3, + .grid-4 { + grid-template-columns: repeat(2, 1fr); + } + + .footer-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 720px) { + .container { + width: calc(100% - 28px); + } + + .nav-actions a:not(.btn) { + display: none; + } + + .inner-hero .container { + padding: 46px 0 54px; + } + + h1 { + font-size: 38px; + } + + h2 { + font-size: 32px; + } + + .grid-3, + .grid-4, + .footer-grid { + grid-template-columns: 1fr; + } + + .auth-scenic-layout { + padding: 36px 14px 48px; + align-items: start; + } + + .auth-card { + padding: 28px 22px; + } + + .auth-card h1 { + font-size: 34px; + } +} + +@media (prefers-reduced-motion: reduce) { + + *, + *:before, + *:after { + animation-duration: .01ms !important; + animation-iteration-count: 1 !important; + scroll-behavior: auto !important; + transition-duration: .01ms !important; + } + +} diff --git a/public/css/register.css b/public/css/register.css new file mode 100644 index 0000000..bd35fe6 --- /dev/null +++ b/public/css/register.css @@ -0,0 +1,10 @@ +/* 注册页只保留单页差异,公共认证卡片样式在 public.css */ +.page-register .auth-links { + justify-content: center; +} + +@media (max-width: 720px) { + .page-register .auth-layout { + align-items: start; + } +} \ No newline at end of file diff --git a/public/css/search-result.css b/public/css/search-result.css new file mode 100644 index 0000000..6336d71 --- /dev/null +++ b/public/css/search-result.css @@ -0,0 +1,119 @@ +.result-hero .container { + min-height: 420px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.result-search { + display: grid; + gap: 18px; + align-content: center; +} + +.result-search .toolbar { + padding: 8px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 999px; + background: rgba(255, 250, 241, .82); +} + +.result-search .input { + flex: 1 1 260px; + border: 0; + background: transparent; +} + +.result-list { + display: grid; + gap: 18px; + max-width: 920px; + margin: 0 auto; +} + +.result-card { + position: relative; + overflow: hidden; + padding: 26px; + border: 1px solid var(--line); + border-radius: 18px; + background: rgba(255, 253, 248, .82); + box-shadow: 0 18px 48px rgba(57, 48, 36, .07); + transition: transform .24s ease, border-color .24s ease, box-shadow .24s ease; +} + +.result-card:after { + content: ""; + position: absolute; + right: -70px; + bottom: -80px; + width: 180px; + height: 180px; + border-radius: 50%; + background: radial-gradient(circle, rgba(196, 146, 69, .13), transparent 68%); + transition: transform .28s ease; +} + +.result-card:hover { + border-color: rgba(196, 146, 69, .42); + box-shadow: 0 24px 60px rgba(57, 48, 36, .11); +} + +.result-card:hover:after { + transform: scale(1.2); +} + +.result-card span { + display: inline-flex; + margin-bottom: 12px; + padding: 7px 12px; + border-radius: 999px; + color: var(--green-dark); + background: #e8f0eb; + font-weight: 800; + font-size: 13px; +} + +.result-card p { + margin: 0; + color: var(--muted); + line-height: 1.8; +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -6px; +} + +@media (max-width: 720px) { + .hero-copy { + padding-left: 0; + } + + .hero-copy:before { + display: none; + } + + .result-search .toolbar { + border-radius: 18px; + } +} \ No newline at end of file diff --git a/public/css/submit-ticket.css b/public/css/submit-ticket.css new file mode 100644 index 0000000..3ffe636 --- /dev/null +++ b/public/css/submit-ticket.css @@ -0,0 +1,111 @@ +.ticket-hero .container { + min-height: 420px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.ticket-tip { + display: grid; + place-items: center; + text-align: center; + background: linear-gradient(145deg, var(--green-dark), var(--green)); + color: #fff; +} + +.ticket-tip span { + width: 88px; + height: 88px; + border-radius: 50%; + display: grid; + place-items: center; + color: var(--red); + background: rgba(255, 253, 248, .94); + font-size: 28px; + font-weight: 900; +} + +.ticket-tip p { + max-width: 360px; + margin: 18px 0 0; + color: rgba(255, 255, 255, .86); + line-height: 1.8; +} + +.ticket-layout { + display: grid; + grid-template-columns: minmax(0, 720px) 320px; + gap: 26px; + align-items: start; +} + +.ticket-form { + max-width: none; +} + +.ticket-form textarea { + min-height: 150px; + padding-top: 16px; + border-radius: 18px; + resize: vertical; +} + +.ticket-side { + padding: 26px; + border: 1px solid var(--line); + border-radius: 18px; + background: rgba(255, 253, 248, .82); + box-shadow: 0 20px 54px rgba(57, 48, 36, .08); +} + +.ticket-side p { + color: var(--muted); + line-height: 1.8; +} + +.ticket-side a { + display: inline-flex; + margin-top: 10px; + color: var(--red); + font-weight: 800; +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -6px; +} + +@media (max-width: 900px) { + .ticket-layout { + grid-template-columns: 1fr; + } +} + +@media (max-width: 720px) { + .hero-copy { + padding-left: 0; + } + + .hero-copy:before { + display: none; + } +} \ No newline at end of file diff --git a/public/css/surname-detail.css b/public/css/surname-detail.css new file mode 100644 index 0000000..1bfceec --- /dev/null +++ b/public/css/surname-detail.css @@ -0,0 +1,169 @@ +.surname-detail-hero .container { + min-height: 440px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.detail-actions { + display: flex; + gap: 14px; + margin-top: 30px; + flex-wrap: wrap; +} + +.surname-emblem { + position: relative; + overflow: hidden; + display: grid; + place-items: center; + text-align: center; + color: #fff; + background: + radial-gradient(circle at 72% 24%, rgba(255, 255, 255, .28) 0 10%, transparent 11%), + linear-gradient(135deg, #476f63, #c49245 54%, #8f4634); +} + +.surname-emblem span { + width: 112px; + height: 112px; + border-radius: 50%; + display: grid; + place-items: center; + color: var(--red); + background: rgba(255, 253, 248, .95); + font-family: "SimSun", "Songti SC", serif; + font-size: 58px; + font-weight: 800; + transition: transform .24s ease; +} + +.surname-emblem:hover span { + transform: rotate(-8deg) scale(1.08); +} + +.surname-emblem p { + margin: 18px 0 0; + color: rgba(255, 255, 255, .9); + font-size: 22px; +} + +.surname-detail-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24px; +} + +.content-card, +.related-panel { + border: 1px solid var(--line); + border-radius: 18px; + background: rgba(255, 253, 248, .82); + box-shadow: 0 20px 54px rgba(57, 48, 36, .08); +} + +.content-card { + padding: 30px; +} + +.content-card:first-child { + grid-column: 1 / -1; +} + +.content-card p { + color: var(--muted); + font-size: 17px; + line-height: 1.9; +} + +.generation-row { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.generation-row span { + width: 54px; + height: 54px; + border-radius: 14px; + display: grid; + place-items: center; + color: #7c4a33; + background: #fbf7ed; + border: 1px solid var(--line); + font-family: "SimSun", "Songti SC", serif; + font-size: 24px; + font-weight: 800; + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.generation-row span:hover { + color: #fff; + background: var(--red); + transform: translateY(-5px); +} + +.related-panel { + padding: 26px; +} + +.related-panel a { + display: block; + margin-top: 12px; + padding: 14px 16px; + border: 1px solid var(--line); + border-radius: 14px; + color: #6f4a37; + background: #fbf7ed; + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.related-panel a:hover { + color: #fff; + background: var(--green); + transform: translateX(5px); +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -6px; +} + +@media (max-width: 860px) { + .surname-detail-grid { + grid-template-columns: 1fr; + } + + .content-card:first-child { + grid-column: auto; + } +} + +@media (max-width: 720px) { + .hero-copy { + padding-left: 0; + } + + .hero-copy:before { + display: none; + } +} \ No newline at end of file diff --git a/public/css/surname.css b/public/css/surname.css new file mode 100644 index 0000000..0c92e6d --- /dev/null +++ b/public/css/surname.css @@ -0,0 +1,229 @@ +.surname-hero .container { + min-height: 440px; + padding-top: 74px; + padding-bottom: 74px; +} + +.hero-copy { + position: relative; + padding-left: 28px; +} + +.hero-copy:before { + content: ""; + position: absolute; + left: 0; + top: 58px; + width: 4px; + height: 132px; + border-radius: 999px; + background: linear-gradient(180deg, var(--red), var(--gold)); +} + +.surname-stats { + display: flex; + gap: 12px; + margin-top: 28px; + flex-wrap: wrap; +} + +.surname-stats span { + padding: 11px 14px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 999px; + color: #6d766f; + background: rgba(255, 253, 248, .74); + box-shadow: 0 14px 32px rgba(72, 57, 38, .06); +} + +.surname-stats b { + margin-right: 6px; + color: var(--green); +} + +.surname-search-card { + position: relative; + overflow: hidden; + display: grid; + gap: 18px; + align-content: center; +} + +.search-ornament { + position: absolute; + right: -72px; + top: -84px; + width: 220px; + height: 220px; + border-radius: 50%; + background: radial-gradient(circle, rgba(200, 59, 50, .13), transparent 62%); + animation: surnamePulse 4s ease-in-out infinite; +} + +.surname-search-card h3, +.search-toolbar, +.hot-query { + position: relative; + z-index: 2; +} + +.search-toolbar { + padding: 8px; + border: 1px solid rgba(224, 211, 189, .78); + border-radius: 999px; + background: rgba(255, 250, 241, .82); +} + +.search-toolbar .input { + flex: 1 1 280px; + border: 0; + background: transparent; +} + +.hot-query { + display: flex; + gap: 10px; + flex-wrap: wrap; +} + +.hot-query span { + width: 38px; + height: 38px; + border-radius: 12px; + display: grid; + place-items: center; + color: #7c4a33; + background: #fbf7ed; + border: 1px solid var(--line); + font-family: "SimSun", "Songti SC", serif; + font-weight: 800; + transition: transform .22s ease, color .22s ease, background .22s ease; +} + +.hot-query span:hover { + color: #fff; + background: var(--red); + transform: translateY(-4px); +} + +.surname-list-section { + position: relative; + overflow: hidden; +} + +.surname-list-section:before { + content: ""; + position: absolute; + left: 50%; + top: 58px; + width: 520px; + height: 180px; + border-radius: 50%; + background: radial-gradient(circle, rgba(196, 146, 69, .09), transparent 68%); + transform: translateX(-50%); +} + +.surname-grid { + position: relative; + display: grid; + grid-template-columns: repeat(8, 1fr); + gap: 12px; + padding: 24px; + border: 1px solid rgba(224, 211, 189, .8); + border-radius: 24px; + background: rgba(255, 253, 248, .72); + box-shadow: 0 22px 60px rgba(57, 48, 36, .08); +} + +.surname-grid a { + height: 64px; + border-radius: 14px; + display: grid; + place-items: center; + color: #7c4a33; + background: #fbf7ed; + border: 1px solid var(--line); + font-family: "SimSun", "Songti SC", serif; + font-size: 24px; + font-weight: 800; + transition: transform .24s ease, color .24s ease, background .24s ease, border-color .24s ease, box-shadow .24s ease; + will-change: transform; +} + +.surname-grid a:hover { + color: #fff; + background: var(--red); + border-color: rgba(200, 59, 50, .72); + box-shadow: 0 16px 34px rgba(200, 59, 50, .18); + transform: translateY(-6px) scale(1.03); +} + +.surname-grid:hover a { + animation: surnameFloat 1.9s ease-in-out infinite; +} + +.surname-grid:hover a:nth-child(2n) { + animation-delay: .08s; +} + +.surname-grid:hover a:nth-child(3n) { + animation-delay: .14s; +} + +.surname-grid:hover a:nth-child(4n) { + animation-delay: .2s; +} + +.tilt-card { + transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0)); + transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease; +} + +.tilt-card:hover { + --lift: -6px; +} + +@keyframes surnameFloat { + + 0%, + 100% { + transform: translateY(0); + } + + 50% { + transform: translateY(-4px); + } +} + +@keyframes surnamePulse { + + 0%, + 100% { + opacity: .5; + transform: scale(.92); + } + + 50% { + opacity: 1; + transform: scale(1.08); + } +} + +@media (max-width: 720px) { + .hero-copy { + padding-left: 0; + } + + .hero-copy:before { + display: none; + } + + .search-toolbar { + border-radius: 18px; + } + + .surname-grid { + grid-template-columns: repeat(4, 1fr); + padding: 14px; + } +} \ No newline at end of file diff --git a/public/images/ad-album-small.png b/public/images/ad-album-small.png new file mode 100644 index 0000000..66c22bc Binary files /dev/null and b/public/images/ad-album-small.png differ diff --git a/public/images/ad-cloud.png b/public/images/ad-cloud.png new file mode 100644 index 0000000..26a4c52 Binary files /dev/null and b/public/images/ad-cloud.png differ diff --git a/public/images/ad-data.png b/public/images/ad-data.png new file mode 100644 index 0000000..9498735 Binary files /dev/null and b/public/images/ad-data.png differ diff --git a/public/images/ad-family-album.png b/public/images/ad-family-album.png new file mode 100644 index 0000000..4b03f0b Binary files /dev/null and b/public/images/ad-family-album.png differ diff --git a/public/images/ad-family-video.png b/public/images/ad-family-video.png new file mode 100644 index 0000000..8d82f09 Binary files /dev/null and b/public/images/ad-family-video.png differ diff --git a/public/images/ad-genealogy-service.png b/public/images/ad-genealogy-service.png new file mode 100644 index 0000000..cf144ac Binary files /dev/null and b/public/images/ad-genealogy-service.png differ diff --git a/public/images/ad-hall.png b/public/images/ad-hall.png new file mode 100644 index 0000000..ab6b420 Binary files /dev/null and b/public/images/ad-hall.png differ diff --git a/public/images/ad-memory.png b/public/images/ad-memory.png new file mode 100644 index 0000000..b75761b Binary files /dev/null and b/public/images/ad-memory.png differ diff --git a/public/images/ad-print.png b/public/images/ad-print.png new file mode 100644 index 0000000..f527271 Binary files /dev/null and b/public/images/ad-print.png differ diff --git a/public/images/ad-research.png b/public/images/ad-research.png new file mode 100644 index 0000000..a588a39 Binary files /dev/null and b/public/images/ad-research.png differ diff --git a/public/images/ad-reunion.png b/public/images/ad-reunion.png new file mode 100644 index 0000000..0131ce9 Binary files /dev/null and b/public/images/ad-reunion.png differ diff --git a/public/images/ad-service.png b/public/images/ad-service.png new file mode 100644 index 0000000..fbb70ab Binary files /dev/null and b/public/images/ad-service.png differ diff --git a/public/images/ad-video-small.png b/public/images/ad-video-small.png new file mode 100644 index 0000000..fe62122 Binary files /dev/null and b/public/images/ad-video-small.png differ diff --git a/public/images/ancestral-hall-new.png b/public/images/ancestral-hall-new.png new file mode 100644 index 0000000..5726f6e Binary files /dev/null and b/public/images/ancestral-hall-new.png differ diff --git a/public/images/ancestral-hall.png b/public/images/ancestral-hall.png new file mode 100644 index 0000000..5726f6e Binary files /dev/null and b/public/images/ancestral-hall.png differ diff --git a/public/images/app-family-home.png b/public/images/app-family-home.png new file mode 100644 index 0000000..2916524 Binary files /dev/null and b/public/images/app-family-home.png differ diff --git a/public/images/app-home.png b/public/images/app-home.png new file mode 100644 index 0000000..4017a54 Binary files /dev/null and b/public/images/app-home.png differ diff --git a/public/images/app-login.png b/public/images/app-login.png new file mode 100644 index 0000000..8cd9c7c Binary files /dev/null and b/public/images/app-login.png differ diff --git a/public/images/logo-light.png b/public/images/logo-light.png new file mode 100644 index 0000000..21a2505 Binary files /dev/null and b/public/images/logo-light.png differ diff --git a/public/images/logo-mark.png b/public/images/logo-mark.png new file mode 100644 index 0000000..53be143 Binary files /dev/null and b/public/images/logo-mark.png differ diff --git a/public/images/logo.png b/public/images/logo.png new file mode 100644 index 0000000..ef11d51 Binary files /dev/null and b/public/images/logo.png differ diff --git a/public/images/mlogo.png b/public/images/mlogo.png new file mode 100644 index 0000000..8f46091 Binary files /dev/null and b/public/images/mlogo.png differ diff --git a/public/js/album-pages.js b/public/js/album-pages.js new file mode 100644 index 0000000..7854217 --- /dev/null +++ b/public/js/album-pages.js @@ -0,0 +1,314 @@ +(function (root, factory) { + // 相册模块同时支持浏览器页面和 Node 单元测试。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.AlbumPages = factory(root); + if (root.document) { + root.document.addEventListener('DOMContentLoaded', function () { + root.AlbumPages.init(); + }); + } +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var documentRef = root.document; + var selectedAlbumId = ''; + + function normalizeList(data) { + // 相册接口列表响应兼容数组、rows、records、list、data。 + if (Array.isArray(data)) return data; + if (!data || typeof data !== 'object') return []; + if (Array.isArray(data.rows)) return data.rows; + if (Array.isArray(data.records)) return data.records; + if (Array.isArray(data.list)) return data.list; + if (Array.isArray(data.data)) return data.data; + return []; + } + + function pick(item, keys, fallback) { + var source = item || {}; + var index; + var value; + + for (index = 0; index < keys.length; index += 1) { + value = source[keys[index]]; + if (value !== undefined && value !== null && value !== '') return value; + } + + return fallback || ''; + } + + function escapeHtml(value) { + // 相册名称和照片说明进入 innerHTML 前统一转义。 + return String(value === undefined || value === null ? '' : value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function getApi() { + return root.GenealogyApi && root.GenealogyApi.defaultClient; + } + + function query(selector, rootNode) { + return (rootNode || documentRef).querySelector(selector); + } + + function queryAll(selector, rootNode) { + return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)); + } + + function showMessage(message) { + if (root.layui && root.layui.layer) { + root.layui.layer.msg(message); + return; + } + + root.alert(message); + } + + function getQueryParam(search, name) { + var params = new URLSearchParams(String(search || '').replace(/^\?/, '')); + return params.get(name) || ''; + } + + function getCurrentGenealogyId() { + var search = root.location && root.location.search; + var page = query('[data-album-page], [data-promo-album-page]'); + + return getQueryParam(search, 'genealogyId') || + (page && page.getAttribute('data-genealogy-id')) || + getQueryParam(search, 'id') || + ''; + } + + function trimOrUndefined(value) { + var text = String(value === undefined || value === null ? '' : value).trim(); + + return text || undefined; + } + + function toNumber(value, fallback) { + var text = String(value === undefined || value === null ? '' : value).trim(); + var number = Number(text); + + return text && Number.isFinite(number) ? number : fallback; + } + + function buildAlbumBody(values) { + // AlbumBody 来自 APP.openapi.json,albumName 必填。 + var source = values || {}; + + return { + albumName: String(source.albumName || '').trim(), + albumDesc: trimOrUndefined(source.albumDesc), + coverOssId: toNumber(source.coverOssId), + sortOrder: toNumber(source.sortOrder, 1), + status: String(source.status || '').trim() || '0' + }; + } + + function buildPhotoBody(values) { + // AlbumPhotoBody 来自 APP.openapi.json,ossId 必填。 + var source = values || {}; + + return { + ossId: toNumber(source.ossId), + photoTitle: trimOrUndefined(source.photoTitle), + photoDesc: trimOrUndefined(source.photoDesc), + photographer: trimOrUndefined(source.photographer), + shootTime: trimOrUndefined(source.shootTime), + sortOrder: toNumber(source.sortOrder, 1), + status: String(source.status || '').trim() || '0' + }; + } + + function getAlbumId(item) { + return pick(item, ['albumId', 'id'], ''); + } + + function buildAlbumRow(item) { + var albumId = getAlbumId(item); + var name = pick(item, ['albumName', 'name'], '未命名相册'); + var photoCount = pick(item, ['photoCount', 'photos'], '0'); + var desc = pick(item, ['albumDesc', 'description'], '暂无说明'); + + return ''; + } + + function buildPhotoRow(item) { + var title = pick(item, ['photoTitle', 'title'], '未命名照片'); + var desc = pick(item, ['photoDesc', 'description'], '暂无说明'); + var photographer = pick(item, ['photographer'], '未记录拍摄人'); + + return '

' + escapeHtml(title) + '

' + escapeHtml(desc) + ' · ' + escapeHtml(photographer) + '

照片
'; + } + + function renderAlbums(items) { + var container = query('[data-album-list], [data-promo-album-list]'); + var list = normalizeList(items); + + if (!container) return; + if (!list.length) { + container.innerHTML = '
暂无相册
'; + return; + } + + container.innerHTML = list.map(buildAlbumRow).join(''); + } + + function renderPhotos(items) { + var container = query('[data-album-photo-list]'); + var list = normalizeList(items); + + if (!container) return; + if (!list.length) { + container.innerHTML = '
暂无照片
'; + return; + } + + container.innerHTML = list.map(buildPhotoRow).join(''); + } + + function getFormValues(form) { + var values = {}; + + queryAll('[name]', form).forEach(function (field) { + values[field.name] = field.value; + }); + + return values; + } + + async function loadAlbums() { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + + if (!api || !genealogyId) return; + + try { + renderAlbums(await api.albums(genealogyId)); + } catch (error) { + showMessage(error.message || '相册加载失败'); + } + } + + async function loadPhotos(albumId) { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + + if (!api || !genealogyId || !albumId) return; + + selectedAlbumId = albumId; + try { + renderPhotos(await api.albumPhotos(genealogyId, albumId)); + } catch (error) { + showMessage(error.message || '照片加载失败'); + } + } + + async function submitAlbum(form) { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var body = buildAlbumBody(getFormValues(form)); + + if (!api || !genealogyId) { + showMessage('请从具体家谱进入相册管理'); + return; + } + + if (!body.albumName) { + showMessage('请填写相册名称'); + return; + } + + try { + if (selectedAlbumId) { + await api.updateAlbum(genealogyId, selectedAlbumId, body); + } else { + await api.createAlbum(genealogyId, body); + } + + form.reset(); + selectedAlbumId = ''; + showMessage('相册已保存'); + loadAlbums(); + } catch (error) { + showMessage(error.message || '保存相册失败'); + } + } + + async function submitPhoto(form) { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var body = buildPhotoBody(getFormValues(form)); + + if (!api || !genealogyId || !selectedAlbumId) { + showMessage('请先选择相册'); + return; + } + + if (!body.ossId) { + showMessage('请填写照片 OSS ID'); + return; + } + + try { + await api.createAlbumPhoto(genealogyId, selectedAlbumId, body); + form.reset(); + showMessage('照片已添加'); + loadPhotos(selectedAlbumId); + } catch (error) { + showMessage(error.message || '添加照片失败'); + } + } + + function bindActions() { + if (!documentRef) return; + + documentRef.addEventListener('click', function (event) { + var albumButton = event.target.closest('[data-album-id]'); + + if (!albumButton) return; + event.preventDefault(); + loadPhotos(albumButton.getAttribute('data-album-id')); + }); + + documentRef.addEventListener('submit', function (event) { + var albumForm = event.target.closest('[data-album-form]'); + var photoForm = event.target.closest('[data-album-photo-form]'); + + if (albumForm) { + event.preventDefault(); + submitAlbum(albumForm); + } + + if (photoForm) { + event.preventDefault(); + submitPhoto(photoForm); + } + }); + } + + function init() { + if (!documentRef) return; + + bindActions(); + loadAlbums(); + } + + return { + normalizeList: normalizeList, + buildAlbumBody: buildAlbumBody, + buildPhotoBody: buildPhotoBody, + buildAlbumRow: buildAlbumRow, + init: init + }; +}); diff --git a/public/js/api-client.js b/public/js/api-client.js new file mode 100644 index 0000000..97c70f6 --- /dev/null +++ b/public/js/api-client.js @@ -0,0 +1,449 @@ +(function (root, factory) { + // 同一份接口客户端同时支持浏览器页面和 Node 测试环境。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.GenealogyApi = factory(root); +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var DEFAULT_BASE_URL = 'http://test-genealogy-api.ddxcjp.cn'; + var DEFAULT_CLIENT_ID = 'ced7e5f0498645c6ec642dcf450b036f'; + var DEFAULT_TENANT_ID = '000000'; + var DEFAULT_TOKEN_KEY = 'genealogy_auth_token'; + var DEFAULT_TOKEN_HEADER_NAME = 'Authorization'; + + function loadNodeModule(path) { + if (typeof require !== 'function') return null; + + try { + return require(path); + } catch (error) { + return null; + } + } + + function getConfigApi() { + var configFactory; + + if (root.GenealogyConfig) return root.GenealogyConfig; + + configFactory = loadNodeModule('../../config.js'); + return configFactory ? configFactory(root) : null; + } + + function getStorageUtil() { + return root.StorageUtil || loadNodeModule('../../utils/StorageUtil.js'); + } + + function getRequestUtil() { + return root.RequestUtil || loadNodeModule('../../utils/RequestUtil.js'); + } + + function getStorage(store) { + if (store) return store; + + try { + return root.localStorage; + } catch (error) { + return null; + } + } + + function getTokenFromResponse(data) { + // 登录响应可能使用不同 token 字段,这里统一收敛为本地登录票据。 + if (!data) return ''; + return data.token || data.accessToken || data.tokenValue || ''; + } + + function ApiError(message, options) { + this.name = 'ApiError'; + this.message = message || '接口请求失败'; + this.status = options && options.status; + this.code = options && options.code; + this.data = options && options.data; + } + + ApiError.prototype = Object.create(Error.prototype); + ApiError.prototype.constructor = ApiError; + + function createUnavailableMethod(name) { + return function () { + var error = new Error('PC 接口文档未提供该业务接口:' + name); + error.code = 'PC_API_NOT_AVAILABLE'; + throw error; + }; + } + + function createFormData(fileOrFormData) { + var FormDataCtor = root.FormData; + var formData; + + if (FormDataCtor && fileOrFormData instanceof FormDataCtor) return fileOrFormData; + if (!FormDataCtor) throw new ApiError('当前环境不支持文件上传'); + + formData = new FormDataCtor(); + formData.append('file', fileOrFormData); + return formData; + } + + function createChunkFormData(body) { + var FormDataCtor = root.FormData; + var source = body || {}; + var formData; + + if (FormDataCtor && body instanceof FormDataCtor) return body; + if (!FormDataCtor) throw new ApiError('当前环境不支持文件上传'); + + formData = new FormDataCtor(); + formData.append('uploadId', source.uploadId); + formData.append('chunkIndex', source.chunkIndex); + formData.append('chunkMd5', source.chunkMd5); + formData.append('file', source.file); + return formData; + } + + function createClient(options) { + var settings = options || {}; + var configApi = getConfigApi(); + var config = configApi && configApi.getConfig ? configApi.getConfig() : {}; + var storageUtil = getStorageUtil(); + var requestUtil = getRequestUtil(); + var store = getStorage(settings.tokenStore || settings.storage); + var clientId = settings.clientId || config.clientId || DEFAULT_CLIENT_ID; + var tenantId = settings.tenantId || config.tenantId || DEFAULT_TENANT_ID; + var tokenKey = settings.tokenKey || config.tokenKey || DEFAULT_TOKEN_KEY; + var tokenHeaderName = settings.tokenHeaderName || config.tokenHeaderName || DEFAULT_TOKEN_HEADER_NAME; + var baseUrl = settings.baseUrl || config.apiBaseUrl || DEFAULT_BASE_URL; + var requester; + + if (!requestUtil || !requestUtil.createRequester) { + throw new ApiError('缺少 RequestUtil.createRequester 公共请求工具'); + } + + function getToken() { + return storageUtil && storageUtil.read ? storageUtil.read(store, tokenKey) || '' : ''; + } + + function setToken(token) { + if (token && storageUtil && storageUtil.write) storageUtil.write(store, tokenKey, token); + } + + function clearToken() { + if (storageUtil && storageUtil.remove) storageUtil.remove(store, tokenKey); + } + + requester = requestUtil.createRequester({ + baseUrl: baseUrl, + clientId: clientId, + tokenHeaderName: tokenHeaderName, + getToken: getToken, + fetchImpl: settings.fetchImpl || root.fetch + }); + + function request(method, path, requestOptions) { + return requester(method, path, requestOptions || {}); + } + + function withTenant(body) { + return Object.assign({ + tenantId: tenantId, + clientId: clientId + }, body || {}); + } + + async function login(body) { + var data = await request('POST', '/genealogy/pc/auth/login', { + auth: false, + body: Object.assign({ + grantType: 'password' + }, withTenant(body)) + }); + + setToken(getTokenFromResponse(data)); + return data; + } + + async function register(body) { + var data = await request('POST', '/genealogy/pc/auth/register', { + auth: false, + body: Object.assign({ + grantType: 'password', + registerSource: 'PC' + }, withTenant(body)) + }); + + setToken(getTokenFromResponse(data)); + return data; + } + + async function loginBySms(body) { + var data = await request('POST', '/genealogy/pc/auth/login/sms', { + auth: false, + body: Object.assign({ + grantType: 'sms' + }, withTenant(body)) + }); + + setToken(getTokenFromResponse(data)); + return data; + } + + function sendSmsCode(body) { + return request('POST', '/genealogy/pc/auth/sms/code', { + auth: false, + body: Object.assign({ + grantType: 'sms' + }, withTenant(body)) + }); + } + + function captchaRequire(body) { + var source = body || {}; + + return request('GET', '/captcha/require', { + auth: false, + query: { + tenantId: source.tenantId || tenantId, + clientId: source.clientId || clientId, + sceneCode: source.sceneCode, + subject: source.subject + } + }); + } + + function buildApiUrl(path, query) { + if (requestUtil.joinUrl && requestUtil.appendQuery) { + return requestUtil.appendQuery(requestUtil.joinUrl(baseUrl, path), query); + } + + return baseUrl + path; + } + + var client = { + clientId: clientId, + tenantId: tenantId, + tokenKey: tokenKey, + getToken: getToken, + setToken: setToken, + clearToken: clearToken, + request: request, + buildApiUrl: buildApiUrl, + get: function (path, query) { + return request('GET', path, { query: query }); + }, + post: function (path, body, reqOptions) { + return request('POST', path, Object.assign({}, reqOptions || {}, { body: body })); + }, + put: function (path, body, reqOptions) { + return request('PUT', path, Object.assign({}, reqOptions || {}, { body: body })); + }, + delete: function (path, reqOptions) { + return request('DELETE', path, reqOptions || {}); + }, + login: login, + register: register, + sendSmsCode: sendSmsCode, + loginBySms: loginBySms, + smsLogin: loginBySms, + getProfile: function () { + return request('GET', '/genealogy/pc/auth/profile'); + }, + currentProfile: function () { + return request('GET', '/genealogy/pc/auth/profile'); + }, + updateProfile: function (body) { + return request('PUT', '/genealogy/pc/auth/profile', { body: body }); + }, + changePassword: function (body) { + return request('PUT', '/genealogy/pc/auth/password', { body: body }); + }, + resetPassword: function (body) { + return request('PUT', '/genealogy/pc/auth/password/reset', { + auth: false, + body: Object.assign({ + tenantId: tenantId + }, body || {}) + }); + }, + changePhone: function (body) { + return request('PUT', '/genealogy/pc/auth/phone', { body: body }); + }, + deactivateAccount: async function (body) { + var data = await request('POST', '/genealogy/pc/auth/account/deactivate', { body: body }); + + clearToken(); + return data; + }, + logout: async function () { + var data = await request('DELETE', '/genealogy/pc/auth/logout'); + + clearToken(); + return data; + }, + uploadFile: function (fileOrFormData) { + return request('POST', '/genealogy/pc/files/upload', { + body: createFormData(fileOrFormData) + }); + }, + initResumableUpload: function (body) { + return request('POST', '/genealogy/pc/files/resumable/init', { body: body }); + }, + uploadChunk: function (body) { + return request('POST', '/genealogy/pc/files/resumable/chunk', { + body: createChunkFormData(body) + }); + }, + uploadResumableChunk: function (body) { + return this.uploadChunk(body); + }, + completeResumableUpload: function (body) { + return request('POST', '/genealogy/pc/files/resumable/complete', { body: body }); + }, + bindFileReference: function (body) { + return request('POST', '/genealogy/pc/files/reference', { body: body }); + }, + releaseFileReference: function (query) { + return request('DELETE', '/genealogy/pc/files/reference', { query: query }); + }, + getRegionChildren: function (parentCode) { + return request('GET', '/genealogy/region/children', { + query: { + parentCode: parentCode + } + }); + }, + regionChildren: function (parentCode) { + return this.getRegionChildren(parentCode); + }, + getRegionPath: function (regionCode) { + return request('GET', '/genealogy/region/path/' + encodeURIComponent(regionCode)); + }, + regionPath: function (regionCode) { + return this.getRegionPath(regionCode); + }, + searchRegions: function (query) { + return request('GET', '/genealogy/region/search', { query: query }); + }, + regionSearch: function (query) { + return this.searchRegions(query); + }, + getRegion: function (regionCode) { + return request('GET', '/genealogy/region/' + encodeURIComponent(regionCode)); + }, + regionDetail: function (regionCode) { + return this.getRegion(regionCode); + }, + captchaRequire: captchaRequire, + captchaRequirement: captchaRequire, + captchaChallenge: function (body) { + return request('POST', '/captcha/challenge', { auth: false, body: body }); + }, + captchaVerify: function (body) { + return request('POST', '/captcha/verify', { auth: false, body: body }); + }, + legacyCaptcha: function () { + return request('GET', '/auth/code', { auth: false }); + } + }; + + // PC YAML 未提供这些业务 paths,保留方法名只用于阻止旧移动端路径继续发请求。 + [ + 'listGenealogies', + 'createGenealogy', + 'publicGenealogies', + 'myGenealogies', + 'genealogyDetail', + 'genealogyOverview', + 'applyJoinGenealogy', + 'myJoinApplies', + 'pendingJoinApplies', + 'auditJoinApply', + 'cancelJoinApply', + 'genealogyMembers', + 'genealogyMemberOptions', + 'updateGenealogyMember', + 'removeGenealogyMember', + 'leaveGenealogy', + 'transferGenealogyOwner', + 'articleCategories', + 'articles', + 'articleDetail', + 'createArticle', + 'updateArticle', + 'feeds', + 'feedsPage', + 'feedDetail', + 'createFeed', + 'updateFeed', + 'likeFeed', + 'unlikeFeed', + 'feedComments', + 'feedCommentsPage', + 'createFeedComment', + 'deleteFeedComment', + 'albums', + 'createAlbum', + 'updateAlbum', + 'albumPhotos', + 'createAlbumPhoto', + 'ceremonies', + 'ceremonyDetail', + 'createCeremony', + 'updateCeremony', + 'ceremonyGifts', + 'createCeremonyGift', + 'meritRecords', + 'createMeritRecord', + 'growthRecords', + 'growthRecordDetail', + 'createGrowthRecord', + 'updateGrowthRecord', + 'memos', + 'memoDetail', + 'createMemo', + 'updateMemo', + 'vipPackages', + 'vipOrders', + 'createVipOrder', + 'generationPoems', + 'createGenerationPoem', + 'updateGenerationPoem', + 'previewGenerationPoems', + 'saveGenerationPoemsBatch', + 'lineageTree', + 'lineagePersons', + 'lineagePersonsPage', + 'lineagePersonOptions', + 'lineagePersonDetail', + 'createLineagePerson', + 'updateLineagePerson', + 'deleteLineagePerson', + 'addLineageRelation', + 'notifications', + 'markNotificationRead', + 'markAllNotificationsRead', + 'helpArticles', + 'helpArticleDetail', + 'promotions', + 'feedbackList', + 'submitFeedback' + ].forEach(function (name) { + client[name] = createUnavailableMethod(name); + }); + + return client; + } + + return { + DEFAULT_BASE_URL: DEFAULT_BASE_URL, + DEFAULT_CLIENT_ID: DEFAULT_CLIENT_ID, + DEFAULT_TENANT_ID: DEFAULT_TENANT_ID, + TOKEN_KEY: DEFAULT_TOKEN_KEY, + ApiError: ApiError, + createClient: createClient, + defaultClient: createClient() + }; +}); diff --git a/public/js/article-pages.js b/public/js/article-pages.js new file mode 100644 index 0000000..8ed406e --- /dev/null +++ b/public/js/article-pages.js @@ -0,0 +1,297 @@ +(function (root, factory) { + // 谱文模块同时支持浏览器页面和 Node 单元测试。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.ArticlePages = factory(root); + if (root.document) { + root.document.addEventListener('DOMContentLoaded', function () { + root.ArticlePages.init(); + }); + } +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var documentRef = root.document; + + function normalizeList(data) { + // 通用列表响应兼容数组、rows、records、list、data。 + if (Array.isArray(data)) return data; + if (!data || typeof data !== 'object') return []; + if (Array.isArray(data.rows)) return data.rows; + if (Array.isArray(data.records)) return data.records; + if (Array.isArray(data.list)) return data.list; + if (Array.isArray(data.data)) return data.data; + return []; + } + + function pick(item, keys, fallback) { + var source = item || {}; + var index; + var value; + + for (index = 0; index < keys.length; index += 1) { + value = source[keys[index]]; + if (value !== undefined && value !== null && value !== '') return value; + } + + return fallback || ''; + } + + function escapeHtml(value) { + // 标题和摘要进入 innerHTML 前统一转义。 + return String(value === undefined || value === null ? '' : value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function getApi() { + return root.GenealogyApi && root.GenealogyApi.defaultClient; + } + + function query(selector, rootNode) { + return (rootNode || documentRef).querySelector(selector); + } + + function queryAll(selector, rootNode) { + return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)); + } + + function showMessage(message) { + if (root.layui && root.layui.layer) { + root.layui.layer.msg(message); + return; + } + + root.alert(message); + } + + function getQueryParam(search, name) { + var params = new URLSearchParams(String(search || '').replace(/^\?/, '')); + return params.get(name) || ''; + } + + function getCurrentGenealogyId() { + var search = root.location && root.location.search; + var page = query('[data-article-page], [data-article-edit-page], [data-article-detail-page], [data-content-page]'); + + return getQueryParam(search, 'genealogyId') || + getQueryParam(search, 'familyId') || + (page && page.getAttribute('data-genealogy-id')) || + getQueryParam(search, 'id') || + ''; + } + + function getCurrentArticleId() { + var search = root.location && root.location.search; + + return getQueryParam(search, 'articleId') || getQueryParam(search, 'id') || ''; + } + + function trimOrUndefined(value) { + var text = String(value === undefined || value === null ? '' : value).trim(); + + return text || undefined; + } + + function toNumber(value, fallback) { + var text = String(value === undefined || value === null ? '' : value).trim(); + var number = Number(text); + + return text && Number.isFinite(number) ? number : fallback; + } + + function formatStatus(status) { + var value = String(status === undefined || status === null ? '' : status); + + if (value === '0' || value === 'published') return '已发布'; + if (value === '1' || value === 'draft') return '草稿'; + return value || '未知'; + } + + function buildArticleBody(values) { + // ArticleBody 来自 APP.openapi.json,标题和正文为核心必填。 + var source = values || {}; + + return { + categoryId: toNumber(source.categoryId), + articleTitle: String(source.articleTitle || '').trim(), + articleSummary: trimOrUndefined(source.articleSummary), + coverOssId: toNumber(source.coverOssId), + articleContent: String(source.articleContent || '').trim(), + authorName: trimOrUndefined(source.authorName), + sortOrder: toNumber(source.sortOrder, 1), + status: String(source.status || '').trim() || '0' + }; + } + + function getArticleId(item) { + return pick(item, ['articleId', 'id'], ''); + } + + function buildArticleRow(item, genealogyId) { + var articleId = getArticleId(item); + var title = pick(item, ['articleTitle', 'title'], '未命名谱文'); + var category = pick(item, ['categoryName', 'categoryTitle', 'typeName'], '谱文'); + var status = formatStatus(pick(item, ['status'], '0')); + var updateTime = pick(item, ['updateTime', 'publishTime', 'createTime'], '未记录时间'); + + return '' + + '

' + escapeHtml(title) + '

' + escapeHtml(category) + ' · ' + escapeHtml(status) + ' · ' + escapeHtml(updateTime) + '

' + + '查看
'; + } + + function buildCategoryOption(item) { + var categoryId = pick(item, ['categoryId', 'id'], ''); + var name = pick(item, ['categoryName', 'name', 'title'], '未命名分类'); + + return ''; + } + + function renderArticles(items, genealogyId) { + var container = query('[data-article-list], [data-recent-content]'); + var list = normalizeList(items); + + if (!container) return; + if (!list.length) { + container.innerHTML = '
暂无谱文内容
'; + return; + } + + container.innerHTML = list.map(function (item) { + return buildArticleRow(item, genealogyId); + }).join(''); + } + + function renderCategories(items) { + var select = query('[data-article-category]'); + var list = normalizeList(items); + + if (!select) return; + select.innerHTML = '' + list.map(buildCategoryOption).join(''); + } + + function renderArticleDetail(article) { + var source = article || {}; + var title = query('[data-article-title]'); + var summary = query('[data-article-summary]'); + var meta = query('[data-article-meta]'); + var content = query('[data-article-content]'); + + if (title) title.textContent = pick(source, ['articleTitle', 'title'], '未命名谱文'); + if (summary) summary.textContent = pick(source, ['articleSummary', 'summary'], '暂无摘要'); + if (meta) { + meta.innerHTML = '' + escapeHtml(pick(source, ['categoryName'], '谱文')) + '' + escapeHtml(pick(source, ['authorName'], '佚名')) + '' + escapeHtml(pick(source, ['updateTime', 'publishTime', 'createTime'], '未记录时间')) + ''; + } + if (content) content.innerHTML = pick(source, ['articleContent', 'content'], '

暂无正文

'); + } + + function fillArticleForm(article) { + var source = article || {}; + + queryAll('[name]').forEach(function (field) { + var value = pick(source, [field.name], ''); + if (value !== '') field.value = value; + }); + } + + function getFormValues(form) { + var values = {}; + + queryAll('[name]', form).forEach(function (field) { + values[field.name] = field.value; + }); + + return values; + } + + async function loadArticles() { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var articleId = getCurrentArticleId(); + + if (!api || !genealogyId) return; + + try { + if (query('[data-article-list], [data-recent-content]')) { + renderArticles(await api.articles(genealogyId), genealogyId); + } + + if (query('[data-article-category]')) { + renderCategories(await api.articleCategories(genealogyId)); + } + + if (articleId && query('[data-article-detail-page], [data-article-edit-page]')) { + if (query('[data-article-detail-page]')) { + renderArticleDetail(await api.articleDetail(genealogyId, articleId)); + } else { + fillArticleForm(await api.articleDetail(genealogyId, articleId)); + } + } + } catch (error) { + showMessage(error.message || '谱文数据加载失败'); + } + } + + async function submitArticle(form) { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var articleId = getCurrentArticleId(); + var body = buildArticleBody(getFormValues(form)); + + if (!api || !genealogyId) { + showMessage('请从具体家谱进入谱文编辑'); + return; + } + + if (!body.articleTitle || !body.articleContent) { + showMessage('请填写谱文标题和正文'); + return; + } + + try { + if (articleId) { + await api.updateArticle(genealogyId, articleId, body); + } else { + await api.createArticle(genealogyId, body); + } + + showMessage('谱文已保存'); + root.location.href = 'profile-article.html?genealogyId=' + encodeURIComponent(genealogyId); + } catch (error) { + showMessage(error.message || '保存谱文失败'); + } + } + + function bindActions() { + if (!documentRef) return; + + documentRef.addEventListener('submit', function (event) { + var form = event.target.closest('[data-article-form]'); + + if (!form) return; + event.preventDefault(); + submitArticle(form); + }); + } + + function init() { + if (!documentRef) return; + + bindActions(); + loadArticles(); + } + + return { + normalizeList: normalizeList, + buildArticleBody: buildArticleBody, + buildArticleRow: buildArticleRow, + formatStatus: formatStatus, + init: init + }; +}); diff --git a/public/js/auth-pages.js b/public/js/auth-pages.js new file mode 100644 index 0000000..259ca9e --- /dev/null +++ b/public/js/auth-pages.js @@ -0,0 +1,342 @@ +(function (root, factory) { + // 认证页脚本同时给浏览器页面和 Node 测试使用。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.AuthPages = factory(root); + if (root.document) { + root.document.addEventListener('DOMContentLoaded', function () { + root.AuthPages.init(); + }); + } +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var documentRef = root.document; + + function hashPassword(value) { + // 接口文档要求密码传 32 位 MD5;没有加载 md5.js 时保留原值,便于测试定位。 + if (root.hex_md5) return root.hex_md5(value); + if (root.md5) return root.md5(value); + return value; + } + + function buildLoginBody(values, hashFn) { + // 这里只组装页面字段,grantType、tenantId、clientId 由 api-client 统一补齐。 + var hash = hashFn || hashPassword; + var body = { + phone: values.phone, + password: hash(values.password) + }; + + if (values.validToken) body.validToken = values.validToken; + return body; + } + + function buildSmsLoginBody(values) { + // 短信登录接口只需要页面字段,grantType、tenantId、clientId 由 api-client 统一补齐。 + var body = { + phone: values.phone, + smsCode: values.smsCode + }; + + if (values.validToken) body.validToken = values.validToken; + return body; + } + + function buildRegisterBody(values, hashFn) { + // 注册验证码当前对应 validToken,可选字段只在有值时提交。 + var hash = hashFn || hashPassword; + var body = { + phone: values.phone, + password: hash(values.password), + nickName: values.nickName || values.phone + }; + + if (values.validToken) body.validToken = values.validToken; + return body; + } + + function buildPasswordResetBody(values, hashFn) { + // 找回密码接口使用 newPassword 字段,页面确认密码只在提交前校验,不传给后端。 + var hash = hashFn || hashPassword; + + return { + phone: values.phone, + smsCode: values.smsCode, + newPassword: hash(values.newPassword), + validToken: values.validToken || '' + }; + } + + function getApi() { + return root.GenealogyApi && root.GenealogyApi.defaultClient; + } + + function getCaptchaScene(formType) { + // PC/H5 认证场景来自后台验证码配置,客户端 Key 为 web_pc。 + var map = { + login: 'WEB_H5_LOGIN', + register: 'WEB_H5_REGISTER', + 'password-reset': 'WEB_H5_FORGOT_PASSWORD' + }; + + return map[formType] || ''; + } + + function getFormCaptchaScene(form, formType) { + // 页面可通过 data-captcha-scene 显式声明后台配置的验证码场景。 + var sceneCode = form && form.getAttribute && form.getAttribute('data-captcha-scene'); + + return sceneCode || getCaptchaScene(formType); + } + + async function ensureCaptcha(form, sceneCode, subject) { + // 未配置 PC/H5 验证场景时不调用验证中心,也不伪造 validToken。 + if (!sceneCode) return true; + + // 验证适配层不存在时不阻塞测试环境;真实页面会加载 captcha-pages.js。 + if (!root.CaptchaPages || !root.CaptchaPages.ensureToken) return true; + + return Boolean(await root.CaptchaPages.ensureToken(form, { + sceneCode: sceneCode, + subject: subject + })); + } + + function query(selector, rootNode) { + return (rootNode || documentRef).querySelector(selector); + } + + function queryAll(selector, rootNode) { + return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)); + } + + function readForm(form) { + // 页面通过 name 属性和接口字段对齐,避免按 placeholder 文案取值。 + var values = {}; + + queryAll('input[name], textarea[name], select[name]', form).forEach(function (field) { + values[field.name] = String(field.value || '').trim(); + }); + + return values; + } + + function setBusy(button, busy) { + // 加载状态只切 class 和 aria,不在 JS 中写样式。 + if (!button) return; + button.classList.toggle('is-loading', busy); + if ('disabled' in button) button.disabled = busy; + button.setAttribute('aria-busy', busy ? 'true' : 'false'); + } + + function showMessage(message) { + if (root.MessageUtil && root.MessageUtil.show) { + root.MessageUtil.show(message); + return; + } + + if (root.console && root.console.warn) root.console.warn(message); + } + + function requireValue(value, message) { + if (value) return true; + showMessage(message); + return false; + } + + function bindSubmit(selector, handler) { + // 所有认证表单统一拦截 submit,避免链接跳转绕过接口请求。 + var form = query(selector); + + if (!form) return; + form.addEventListener('submit', function (event) { + event.preventDefault(); + handler(form, query('[type="submit"]', form)); + }); + } + + function setLoginMode(activeMode) { + // 登录方式切换只更新状态,具体样式由 login.css 控制。 + queryAll('[data-login-mode-tab]').forEach(function (button) { + var isActive = button.getAttribute('data-login-mode-tab') === activeMode; + + button.classList.toggle('is-active', isActive); + button.setAttribute('aria-selected', isActive ? 'true' : 'false'); + }); + + queryAll('[data-login-mode-panel]').forEach(function (panel) { + var isActive = panel.getAttribute('data-login-mode-panel') === activeMode; + + panel.hidden = !isActive; + }); + } + + async function submitLogin(form, button) { + // 登录成功后跳转地址由 HTML 的 data-success-url 控制,页面可单独调整。 + var api = getApi(); + var values = readForm(form); + + if (!api) return; + if (!requireValue(values.phone, '请填写手机号')) return; + if (!requireValue(values.password, '请填写密码')) return; + if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'login'), values.phone)) return; + + values = readForm(form); + + setBusy(button, true); + try { + await api.login(buildLoginBody(values)); + root.location.href = form.getAttribute('data-success-url') || 'profile.html'; + } catch (error) { + showMessage(error.message || '登录失败'); + } finally { + setBusy(button, false); + } + } + + async function submitSmsLogin(form, button) { + // PC 短信登录与密码登录共用 WEB_H5_LOGIN 场景,短信码由同页按钮发送。 + var api = getApi(); + var values = readForm(form); + + if (!api) return; + if (!requireValue(values.phone, '请填写手机号')) return; + if (!requireValue(values.smsCode, '请填写短信验证码')) return; + if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'login'), values.phone)) return; + + values = readForm(form); + + setBusy(button, true); + try { + await api.loginBySms(buildSmsLoginBody(values)); + root.location.href = form.getAttribute('data-success-url') || 'profile.html'; + } catch (error) { + showMessage(error.message || '短信登录失败'); + } finally { + setBusy(button, false); + } + } + + async function submitRegister(form, button) { + // 注册成功后默认进入创建家谱流程。 + var api = getApi(); + var values = readForm(form); + + if (!api) return; + if (!requireValue(values.phone, '请填写手机号')) return; + if (!requireValue(values.password, '请填写密码')) return; + if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'register'), values.phone)) return; + + values = readForm(form); + + setBusy(button, true); + try { + await api.register(buildRegisterBody(values)); + root.location.href = form.getAttribute('data-success-url') || 'create-genealogy.html'; + } catch (error) { + showMessage(error.message || '注册失败'); + } finally { + setBusy(button, false); + } + } + + async function sendCode(button) { + // 当前只接找回密码验证码;后续注册短信可复用 data-scene-code 扩展。 + var api = getApi(); + var form = button.closest('form'); + var values = readForm(form); + var sceneCode = button.getAttribute('data-scene-code') || getFormCaptchaScene(form, 'password-reset'); + + if (!api) return; + if (!requireValue(values.phone, '请填写手机号')) return; + if (!await ensureCaptcha(form, sceneCode, values.phone)) return; + + values = readForm(form); + + setBusy(button, true); + try { + await api.sendSmsCode({ + sceneCode: sceneCode, + phone: values.phone, + validToken: values.validToken || '' + }); + showMessage('验证码已发送'); + } catch (error) { + showMessage(error.message || '验证码发送失败'); + } finally { + setBusy(button, false); + } + } + + async function submitPasswordReset(form, button) { + // 前端先校验两次密码一致,再按接口字段提交重置请求。 + var api = getApi(); + var values = readForm(form); + + if (!api) return; + if (!requireValue(values.phone, '请填写手机号')) return; + if (!requireValue(values.smsCode, '请填写验证码')) return; + if (!requireValue(values.newPassword, '请填写新密码')) return; + if (values.newPassword !== values.confirmPassword) { + showMessage('两次输入的新密码不一致'); + return; + } + if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'password-reset'), values.phone)) return; + + values = readForm(form); + + setBusy(button, true); + try { + await api.resetPassword(buildPasswordResetBody(values)); + showMessage('密码已重设,请重新登录'); + root.location.href = form.getAttribute('data-success-url') || 'login.html'; + } catch (error) { + showMessage(error.message || '重设密码失败'); + } finally { + setBusy(button, false); + } + } + + function bindCodeButtons() { + queryAll('[data-api-send-code]').forEach(function (button) { + button.addEventListener('click', function (event) { + event.preventDefault(); + sendCode(button); + }); + }); + } + + function bindLoginModeTabs() { + queryAll('[data-login-mode-tab]').forEach(function (button) { + button.addEventListener('click', function () { + setLoginMode(button.getAttribute('data-login-mode-tab')); + }); + }); + } + + function init() { + // 每个页面只会命中自己的 data-auth-form,不需要拆多个入口文件。 + if (!documentRef) return; + + bindSubmit('[data-auth-form="login"]', submitLogin); + bindSubmit('[data-auth-form="sms-login"]', submitSmsLogin); + bindSubmit('[data-auth-form="register"]', submitRegister); + bindSubmit('[data-auth-form="password-reset"]', submitPasswordReset); + bindLoginModeTabs(); + bindCodeButtons(); + } + + return { + buildLoginBody: buildLoginBody, + buildSmsLoginBody: buildSmsLoginBody, + buildRegisterBody: buildRegisterBody, + buildPasswordResetBody: buildPasswordResetBody, + getCaptchaScene: getCaptchaScene, + getFormCaptchaScene: getFormCaptchaScene, + init: init + }; +}); diff --git a/public/js/captcha-pages.js b/public/js/captcha-pages.js new file mode 100644 index 0000000..ca78d4d --- /dev/null +++ b/public/js/captcha-pages.js @@ -0,0 +1,261 @@ +(function (root, factory) { + // 验证码适配层同时支持浏览器运行和 Node 单元测试。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.CaptchaPages = factory(root); +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var DEFAULT_SCENE_CODE = 'WEB_H5_LOGIN'; + + function getApi() { + return root.GenealogyApi && root.GenealogyApi.defaultClient; + } + + function query(selector, rootNode) { + return (rootNode || root.document).querySelector(selector); + } + + function showMessage(message) { + if (root.MessageUtil && root.MessageUtil.show) { + root.MessageUtil.show(message); + return; + } + + if (root.console && root.console.warn) root.console.warn(message); + } + + function buildChallengeBody(options, api) { + // 请求体字段严格对应 PC YAML 的 VerificationChallengeBody。 + var settings = options || {}; + + return { + tenantId: settings.tenantId || api.tenantId, + clientId: settings.clientId || api.clientId, + sceneCode: settings.sceneCode || DEFAULT_SCENE_CODE, + subject: settings.subject || '' + }; + } + + function normalizeChallengeData(data) { + // TAC 需要 data.type、data.id 和图片字段;接口文档返回 captchaType、challengeId、payload。 + var source = data || {}; + var payload = source.payload || {}; + + return Object.assign({}, payload, { + type: source.captchaType || payload.type || 'SLIDER', + id: payload.id || source.challengeId, + challengeId: source.challengeId, + providerCode: source.providerCode || 'tianai', + captchaType: source.captchaType || payload.type || 'SLIDER' + }); + } + + function adaptChallengeResponse(response) { + var payload = response || {}; + + if (Number(payload.code) !== 200 || !payload.data) return payload; + + return Object.assign({}, payload, { + data: normalizeChallengeData(payload.data) + }); + } + + function buildVerifyBody(requestData, context, api) { + // requestData 是 TAC 拖动完成后的轨迹数据;context 保存本次挑战的接口字段。 + var settings = context || {}; + var source = requestData || {}; + + return { + tenantId: settings.tenantId || api.tenantId, + clientId: settings.clientId || api.clientId, + sceneCode: settings.sceneCode || DEFAULT_SCENE_CODE, + subject: settings.subject || '', + challengeId: settings.challengeId || source.challengeId || source.id, + providerCode: settings.providerCode || 'tianai', + captchaType: settings.captchaType || 'SLIDER', + payload: { + id: source.id, + data: source.data || {} + } + }; + } + + function normalizeVerifyResponse(response) { + // TAC 只看 code 是否为 200;接口通过 passed 表示业务验证结果。 + var payload = response || {}; + + if (Number(payload.code) === 200 && payload.data && payload.data.passed === false) { + return Object.assign({}, payload, { + code: 4001, + msg: payload.data.message || payload.msg || '验证失败' + }); + } + + return payload; + } + + function extractValidToken(response) { + var payload = response || {}; + var data = payload.data || {}; + + if (Number(payload.code) !== 200) return ''; + if (data.passed === false) return ''; + return data.validToken || payload.validToken || ''; + } + + function getTokenField(form) { + return query('input[name="validToken"]', form); + } + + function getCaptchaBox(form) { + // 优先使用页面级弹窗挂载点,避免 TAC 验证码挤占表单布局。 + return query('.auth-captcha-modal[data-captcha-box]') + || query('[data-captcha-box]', form) + || query('[data-captcha-box]'); + } + + function writeValidToken(form, token) { + var field = getTokenField(form); + + if (field) field.value = token || ''; + } + + function shouldRequireCaptcha(result) { + // /captcha/require 返回 required=false 时允许直接继续业务请求。 + if (!result) return true; + return result.required !== false; + } + + function createTac(form, options, api, resolve, reject) { + var settings = options || {}; + var box = getCaptchaBox(form); + var context = buildChallengeBody(settings, api); + var lastChallenge = {}; + var captcha; + var config; + + if (!root.TAC || !root.CaptchaConfig) { + reject(new Error('滑动验证组件未加载')); + return null; + } + + if (!box) { + reject(new Error('缺少滑动验证容器')); + return null; + } + + config = new root.CaptchaConfig({ + bindEl: box, + requestCaptchaDataUrl: api.buildApiUrl('/captcha/challenge'), + validCaptchaUrl: api.buildApiUrl('/captcha/verify'), + requestHeaders: { + clientid: api.clientId + }, + validSuccess: function (response, captchaControl, captchaInstance) { + var token = extractValidToken(response); + + if (!token) { + reject(new Error('滑动验证未返回有效票据')); + return; + } + + writeValidToken(form, token); + if (captchaInstance && captchaInstance.destroyWindow) captchaInstance.destroyWindow(); + resolve(token); + }, + validFail: function (response, captchaControl, captchaInstance) { + if (captchaInstance && captchaInstance.reloadCaptcha) captchaInstance.reloadCaptcha(); + showMessage((response && (response.msg || response.message)) || '滑动验证失败,请重试'); + }, + btnCloseFun: function (event, captchaInstance) { + if (captchaInstance) captchaInstance.destroyWindow(); + reject(new Error('已取消滑动验证')); + } + }); + + config.addRequestChain({ + preRequest: function (type, request) { + if (type === 'requestCaptchaData') { + request.data = buildChallengeBody(settings, api); + } + + if (type === 'validCaptcha') { + request.data = buildVerifyBody(request.data, Object.assign({}, context, lastChallenge), api); + } + + return true; + }, + postRequest: function (type, request, response) { + if (type === 'requestCaptchaData') { + var adapted = adaptChallengeResponse(response); + + lastChallenge = { + challengeId: adapted.data && adapted.data.challengeId, + providerCode: adapted.data && adapted.data.providerCode, + captchaType: adapted.data && adapted.data.captchaType + }; + + Object.keys(response || {}).forEach(function (key) { + delete response[key]; + }); + Object.assign(response, adapted); + } + + if (type === 'validCaptcha') { + Object.assign(response, normalizeVerifyResponse(response)); + } + + return true; + } + }); + + captcha = new root.TAC(config, { + bgUrl: 'public/tac/images/dun.jpeg', + logoUrl: null + }); + captcha.init(); + return captcha; + } + + async function ensureToken(form, options) { + // 认证页提交前调用:已有 validToken 直接通过,否则按验证中心策略弹出 TAC。 + var api = getApi(); + var field = getTokenField(form); + var settings = options || {}; + var requirement; + + if (!api) return ''; + if (field && field.value) return field.value; + if (!settings.sceneCode) return ''; + + try { + requirement = await api.captchaRequirement(buildChallengeBody(settings, api)); + if (!shouldRequireCaptcha(requirement)) return 'CAPTCHA_NOT_REQUIRED'; + } catch (error) { + // 查询策略失败时仍尝试直接拉起验证,避免认证接口缺少 validToken。 + requirement = null; + } + + return new Promise(function (resolve, reject) { + createTac(form, settings, api, resolve, reject); + }).catch(function (error) { + showMessage(error.message || '请先完成滑动验证'); + return ''; + }); + } + + return { + buildChallengeBody: buildChallengeBody, + adaptChallengeResponse: adaptChallengeResponse, + buildVerifyBody: buildVerifyBody, + getCaptchaBox: getCaptchaBox, + extractValidToken: extractValidToken, + normalizeVerifyResponse: normalizeVerifyResponse, + shouldRequireCaptcha: shouldRequireCaptcha, + ensureToken: ensureToken + }; +}); diff --git a/public/js/ceremony-pages.js b/public/js/ceremony-pages.js new file mode 100644 index 0000000..a2981ea --- /dev/null +++ b/public/js/ceremony-pages.js @@ -0,0 +1,384 @@ +(function (root, factory) { + // 祭祀/贺礼/功德模块同时支持浏览器页面和 Node 单元测试。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.CeremonyPages = factory(root); + if (root.document) { + root.document.addEventListener('DOMContentLoaded', function () { + root.CeremonyPages.init(); + }); + } +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var documentRef = root.document; + var selectedCeremonyId = ''; + + function normalizeList(data) { + // 通用列表响应兼容数组、rows、records、list、data。 + if (Array.isArray(data)) return data; + if (!data || typeof data !== 'object') return []; + if (Array.isArray(data.rows)) return data.rows; + if (Array.isArray(data.records)) return data.records; + if (Array.isArray(data.list)) return data.list; + if (Array.isArray(data.data)) return data.data; + return []; + } + + function pick(item, keys, fallback) { + var source = item || {}; + var index; + var value; + + for (index = 0; index < keys.length; index += 1) { + value = source[keys[index]]; + if (value !== undefined && value !== null && value !== '') return value; + } + + return fallback || ''; + } + + function escapeHtml(value) { + // 标题、姓名和说明进入 innerHTML 前统一转义。 + return String(value === undefined || value === null ? '' : value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function getApi() { + return root.GenealogyApi && root.GenealogyApi.defaultClient; + } + + function query(selector, rootNode) { + return (rootNode || documentRef).querySelector(selector); + } + + function queryAll(selector, rootNode) { + return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)); + } + + function showMessage(message) { + if (root.layui && root.layui.layer) { + root.layui.layer.msg(message); + return; + } + + root.alert(message); + } + + function getQueryParam(search, name) { + var params = new URLSearchParams(String(search || '').replace(/^\?/, '')); + return params.get(name) || ''; + } + + function getCurrentGenealogyId() { + var search = root.location && root.location.search; + var page = query('[data-ceremony-page], [data-ceremony-edit-page], [data-merit-page], [data-merit-edit-page]'); + + return getQueryParam(search, 'genealogyId') || + (page && page.getAttribute('data-genealogy-id')) || + getQueryParam(search, 'id') || + ''; + } + + function getCurrentCeremonyId() { + var search = root.location && root.location.search; + + return getQueryParam(search, 'ceremonyId') || ''; + } + + function trimOrUndefined(value) { + var text = String(value === undefined || value === null ? '' : value).trim(); + + return text || undefined; + } + + function toNumber(value, fallback) { + var text = String(value === undefined || value === null ? '' : value).trim(); + var number = Number(text); + + return text && Number.isFinite(number) ? number : fallback; + } + + function buildCeremonyBody(values) { + // CeremonyBody 来自 APP.openapi.json,ceremonyType 和 ceremonyTitle 必填。 + var source = values || {}; + + return { + ceremonyType: String(source.ceremonyType || '').trim(), + ceremonyTitle: String(source.ceremonyTitle || '').trim(), + ceremonyDesc: trimOrUndefined(source.ceremonyDesc), + ceremonyTime: trimOrUndefined(source.ceremonyTime), + location: trimOrUndefined(source.location), + coverOssId: toNumber(source.coverOssId), + sortOrder: toNumber(source.sortOrder, 1), + status: String(source.status || '').trim() || '0' + }; + } + + function buildGiftBody(values) { + var source = values || {}; + + return { + giverName: trimOrUndefined(source.giverName), + giftAmount: toNumber(source.giftAmount, 0), + giftMessage: trimOrUndefined(source.giftMessage) + }; + } + + function buildMeritBody(values) { + // MeritRecordBody 来自 APP.openapi.json,donorName 和 meritTitle 必填。 + var source = values || {}; + + return { + donorName: String(source.donorName || '').trim(), + meritType: String(source.meritType || '').trim() || 'donation', + meritTitle: String(source.meritTitle || '').trim(), + meritContent: trimOrUndefined(source.meritContent), + amount: toNumber(source.amount, 0), + meritTime: trimOrUndefined(source.meritTime), + sortOrder: toNumber(source.sortOrder, 1), + status: String(source.status || '').trim() || '0' + }; + } + + function getCeremonyId(item) { + return pick(item, ['ceremonyId', 'id'], ''); + } + + function buildCeremonyRow(item) { + var ceremonyId = getCeremonyId(item); + var title = pick(item, ['ceremonyTitle', 'title'], '未命名活动'); + var type = pick(item, ['ceremonyType', 'type'], 'ceremony'); + var time = pick(item, ['ceremonyTime', 'time', 'createTime'], '未设置时间'); + var giftCount = pick(item, ['giftCount', 'gifts'], '0'); + + return ''; + } + + function buildGiftRow(item) { + var name = pick(item, ['giverName', 'memberName', 'name'], '匿名'); + var amount = pick(item, ['giftAmount', 'amount'], '0'); + var message = pick(item, ['giftMessage', 'message'], '暂无留言'); + + return '

' + escapeHtml(name) + '

' + escapeHtml(amount) + ' 元 · ' + escapeHtml(message) + '

献礼
'; + } + + function buildMeritRow(item) { + var name = pick(item, ['donorName', 'name'], '未命名功德人'); + var title = pick(item, ['meritTitle', 'title'], '功德记录'); + var time = pick(item, ['meritTime', 'createTime'], '未记录时间'); + + return '

' + escapeHtml(name) + '

' + escapeHtml(title) + ' · ' + escapeHtml(time) + '

查看
'; + } + + function renderCeremonies(items) { + var container = query('[data-ceremony-list]'); + var list = normalizeList(items); + + if (!container) return; + if (!list.length) { + container.innerHTML = '
暂无贺礼邀请
'; + return; + } + + container.innerHTML = list.map(buildCeremonyRow).join(''); + } + + function renderGifts(items) { + var container = query('[data-ceremony-gift-list]'); + var list = normalizeList(items); + + if (!container) return; + if (!list.length) { + container.innerHTML = '
暂无献礼记录
'; + return; + } + + container.innerHTML = list.map(buildGiftRow).join(''); + } + + function renderMerits(items) { + var container = query('[data-merit-list]'); + var list = normalizeList(items); + + if (!container) return; + if (!list.length) { + container.innerHTML = '
暂无功德记录
'; + return; + } + + container.innerHTML = list.map(buildMeritRow).join(''); + } + + function getFormValues(form) { + var values = {}; + + queryAll('[name]', form).forEach(function (field) { + values[field.name] = field.value; + }); + + return values; + } + + async function loadCeremonies() { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var ceremonyId = getCurrentCeremonyId(); + + if (!api || !genealogyId) return; + + try { + if (query('[data-ceremony-list]')) { + renderCeremonies(await api.ceremonies(genealogyId)); + } + + if (ceremonyId && query('[data-ceremony-gift-list]')) { + selectedCeremonyId = ceremonyId; + renderGifts(await api.ceremonyGifts(genealogyId, ceremonyId)); + } + } catch (error) { + showMessage(error.message || '贺礼数据加载失败'); + } + } + + async function loadMerits() { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + + if (!api || !genealogyId || !query('[data-merit-list]')) return; + + try { + renderMerits(await api.meritRecords(genealogyId)); + } catch (error) { + showMessage(error.message || '功德记录加载失败'); + } + } + + async function submitCeremony(form) { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var ceremonyId = getCurrentCeremonyId(); + var body = buildCeremonyBody(getFormValues(form)); + + if (!api || !genealogyId) return; + if (!body.ceremonyType || !body.ceremonyTitle) { + showMessage('请填写贺礼类型和标题'); + return; + } + + try { + if (ceremonyId) { + await api.updateCeremony(genealogyId, ceremonyId, body); + } else { + await api.createCeremony(genealogyId, body); + } + showMessage('贺礼已保存'); + root.location.href = 'profile-gift.html?genealogyId=' + encodeURIComponent(genealogyId); + } catch (error) { + showMessage(error.message || '保存贺礼失败'); + } + } + + async function submitGift(form) { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var ceremonyId = selectedCeremonyId || getCurrentCeremonyId(); + var body = buildGiftBody(getFormValues(form)); + + if (!api || !genealogyId || !ceremonyId) { + showMessage('请先选择贺礼邀请'); + return; + } + + try { + await api.createCeremonyGift(genealogyId, ceremonyId, body); + form.reset(); + showMessage('献礼已保存'); + renderGifts(await api.ceremonyGifts(genealogyId, ceremonyId)); + } catch (error) { + showMessage(error.message || '保存献礼失败'); + } + } + + async function submitMerit(form) { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var body = buildMeritBody(getFormValues(form)); + + if (!api || !genealogyId) return; + if (!body.donorName || !body.meritTitle) { + showMessage('请填写功德人和功德标题'); + return; + } + + try { + await api.createMeritRecord(genealogyId, body); + showMessage('功德记录已保存'); + root.location.href = 'profile-merit.html?genealogyId=' + encodeURIComponent(genealogyId); + } catch (error) { + showMessage(error.message || '保存功德记录失败'); + } + } + + function bindActions() { + if (!documentRef) return; + + documentRef.addEventListener('click', function (event) { + var ceremonyButton = event.target.closest('[data-ceremony-id]'); + + if (!ceremonyButton) return; + selectedCeremonyId = ceremonyButton.getAttribute('data-ceremony-id'); + if (getApi() && getCurrentGenealogyId()) { + getApi().ceremonyGifts(getCurrentGenealogyId(), selectedCeremonyId).then(renderGifts).catch(function (error) { + showMessage(error.message || '献礼加载失败'); + }); + } + }); + + documentRef.addEventListener('submit', function (event) { + var ceremonyForm = event.target.closest('[data-ceremony-form]'); + var giftForm = event.target.closest('[data-ceremony-gift-form]'); + var meritForm = event.target.closest('[data-merit-form]'); + + if (ceremonyForm) { + event.preventDefault(); + submitCeremony(ceremonyForm); + } + + if (giftForm) { + event.preventDefault(); + submitGift(giftForm); + } + + if (meritForm) { + event.preventDefault(); + submitMerit(meritForm); + } + }); + } + + function init() { + if (!documentRef) return; + + bindActions(); + loadCeremonies(); + loadMerits(); + } + + return { + normalizeList: normalizeList, + buildCeremonyBody: buildCeremonyBody, + buildMeritBody: buildMeritBody, + buildCeremonyRow: buildCeremonyRow, + init: init + }; +}); diff --git a/public/js/feed-pages.js b/public/js/feed-pages.js new file mode 100644 index 0000000..ff7279e --- /dev/null +++ b/public/js/feed-pages.js @@ -0,0 +1,321 @@ +(function (root, factory) { + // 家族圈动态模块同时支持浏览器页面和 Node 单元测试。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.FeedPages = factory(root); + if (root.document) { + root.document.addEventListener('DOMContentLoaded', function () { + root.FeedPages.init(); + }); + } +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var documentRef = root.document; + + function normalizeList(data) { + // 动态分页响应兼容 rows、records、list、data。 + if (Array.isArray(data)) return data; + if (!data || typeof data !== 'object') return []; + if (Array.isArray(data.rows)) return data.rows; + if (Array.isArray(data.records)) return data.records; + if (Array.isArray(data.list)) return data.list; + if (Array.isArray(data.data)) return data.data; + return []; + } + + function pick(item, keys, fallback) { + var source = item || {}; + var index; + var value; + + for (index = 0; index < keys.length; index += 1) { + value = source[keys[index]]; + if (value !== undefined && value !== null && value !== '') return value; + } + + return fallback || ''; + } + + function escapeHtml(value) { + // 动态内容进入 innerHTML 前转义,详情富文本不在这里处理。 + return String(value === undefined || value === null ? '' : value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function getApi() { + return root.GenealogyApi && root.GenealogyApi.defaultClient; + } + + function query(selector, rootNode) { + return (rootNode || documentRef).querySelector(selector); + } + + function queryAll(selector, rootNode) { + return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)); + } + + function showMessage(message) { + if (root.layui && root.layui.layer) { + root.layui.layer.msg(message); + return; + } + + root.alert(message); + } + + function getQueryParam(search, name) { + var params = new URLSearchParams(String(search || '').replace(/^\?/, '')); + return params.get(name) || ''; + } + + function getCurrentGenealogyId() { + var search = root.location && root.location.search; + var page = query('[data-feed-page], [data-feed-edit-page]'); + + return getQueryParam(search, 'genealogyId') || + (page && page.getAttribute('data-genealogy-id')) || + getQueryParam(search, 'id') || + ''; + } + + function getCurrentFeedId() { + var search = root.location && root.location.search; + + return getQueryParam(search, 'feedId') || ''; + } + + function trimOrUndefined(value) { + var text = String(value === undefined || value === null ? '' : value).trim(); + + return text || undefined; + } + + function toNumber(value) { + var text = String(value === undefined || value === null ? '' : value).trim(); + var number = Number(text); + + return text && Number.isFinite(number) ? number : undefined; + } + + function buildFeedBody(values) { + // FamilyFeedBody 来自 APP.openapi.json。 + var source = values || {}; + + return { + content: String(source.content || '').trim(), + mediaOssIds: trimOrUndefined(source.mediaOssIds), + visibility: String(source.visibility || '').trim() || '2', + status: String(source.status || '').trim() || '0' + }; + } + + function buildCommentBody(values) { + // FamilyFeedCommentBody 要求 content 必填。 + var source = values || {}; + + return { + parentCommentId: toNumber(source.parentCommentId), + replyUserId: toNumber(source.replyUserId), + content: String(source.content || '').trim() + }; + } + + function getFeedId(item) { + return pick(item, ['feedId', 'id'], ''); + } + + function buildFeedRow(item) { + var feedId = getFeedId(item); + var author = pick(item, ['authorName', 'nickName', 'memberName'], '家族成员'); + var content = pick(item, ['content', 'feedContent'], '暂无内容'); + var likeCount = pick(item, ['likeCount', 'likes'], '0'); + var commentCount = pick(item, ['commentCount', 'comments'], '0'); + var createTime = pick(item, ['createTime', 'publishTime', 'updateTime'], '未记录时间'); + + return '
' + + '

' + escapeHtml(author) + '

' + escapeHtml(content) + '

' + escapeHtml(createTime) + ' · ' + escapeHtml(likeCount) + ' 赞 · ' + escapeHtml(commentCount) + ' 评论
' + + '
' + + '
'; + } + + function renderFeeds(items) { + var container = query('[data-feed-list]'); + var list = normalizeList(items); + + if (!container) return; + if (!list.length) { + container.innerHTML = '
暂无家族动态
'; + return; + } + + container.innerHTML = list.map(buildFeedRow).join(''); + } + + function getFormValues(form) { + var values = {}; + + queryAll('[name]', form).forEach(function (field) { + if (field.type === 'checkbox') { + values[field.name] = field.checked ? '1' : ''; + return; + } + + values[field.name] = field.value; + }); + + return values; + } + + function fillFeedForm(feed) { + var source = feed || {}; + + queryAll('[name]').forEach(function (field) { + var value = pick(source, [field.name], ''); + if (field.type === 'checkbox') { + field.checked = value === '1' || value === true; + return; + } + + if (value !== '') field.value = value; + }); + } + + async function loadFeeds() { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var feedId = getCurrentFeedId(); + + if (!api || !genealogyId) return; + + try { + if (query('[data-feed-list]')) { + renderFeeds(await api.feedsPage(genealogyId, { + pageNum: 1, + pageSize: 10 + })); + } + + if (feedId && query('[data-feed-edit-page]')) { + fillFeedForm(await api.feedDetail(genealogyId, feedId)); + } + } catch (error) { + showMessage(error.message || '家族动态加载失败'); + } + } + + async function submitFeed(form) { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var feedId = getCurrentFeedId(); + var body = buildFeedBody(getFormValues(form)); + + if (!api || !genealogyId) { + showMessage('请从具体家谱进入动态发布'); + return; + } + + if (!body.content) { + showMessage('请填写动态内容'); + return; + } + + try { + if (feedId) { + await api.updateFeed(genealogyId, feedId, body); + } else { + await api.createFeed(genealogyId, body); + } + + showMessage('动态已保存'); + root.location.href = 'profile-feed.html?genealogyId=' + encodeURIComponent(genealogyId); + } catch (error) { + showMessage(error.message || '保存动态失败'); + } + } + + async function likeFeed(feedId) { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + + if (!api || !genealogyId || !feedId) return; + + try { + await api.likeFeed(genealogyId, feedId); + showMessage('已点赞'); + loadFeeds(); + } catch (error) { + showMessage(error.message || '点赞失败'); + } + } + + async function commentFeed(feedId) { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var content; + + if (!api || !genealogyId || !feedId) return; + + content = root.prompt('请输入评论内容', ''); + if (!content) return; + + try { + await api.createFeedComment(genealogyId, feedId, buildCommentBody({ + content: content + })); + showMessage('评论已发布'); + loadFeeds(); + } catch (error) { + showMessage(error.message || '评论失败'); + } + } + + function bindActions() { + if (!documentRef) return; + + documentRef.addEventListener('submit', function (event) { + var form = event.target.closest('[data-feed-form]'); + + if (!form) return; + event.preventDefault(); + submitFeed(form); + }); + + documentRef.addEventListener('click', function (event) { + var likeButton = event.target.closest('[data-feed-like]'); + var commentButton = event.target.closest('[data-feed-comment]'); + + if (likeButton) { + event.preventDefault(); + likeFeed(likeButton.getAttribute('data-feed-like')); + } + + if (commentButton) { + event.preventDefault(); + commentFeed(commentButton.getAttribute('data-feed-comment')); + } + }); + } + + function init() { + if (!documentRef) return; + + bindActions(); + loadFeeds(); + } + + return { + normalizeList: normalizeList, + buildFeedBody: buildFeedBody, + buildCommentBody: buildCommentBody, + buildFeedRow: buildFeedRow, + init: init + }; +}); diff --git a/public/js/feedback-pages.js b/public/js/feedback-pages.js new file mode 100644 index 0000000..e918712 --- /dev/null +++ b/public/js/feedback-pages.js @@ -0,0 +1,177 @@ +(function (root, factory) { + // 反馈模块同时支持浏览器页面和 Node 测试。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.FeedbackPages = factory(root); + if (root.document) { + root.document.addEventListener('DOMContentLoaded', function () { + root.FeedbackPages.init(); + }); + } +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var documentRef = root.document; + + function normalizeList(data) { + // 兼容分页 rows、records、list 和直接数组。 + if (Array.isArray(data)) return data; + if (!data || typeof data !== 'object') return []; + if (Array.isArray(data.rows)) return data.rows; + if (Array.isArray(data.records)) return data.records; + if (Array.isArray(data.list)) return data.list; + if (Array.isArray(data.data)) return data.data; + return []; + } + + function pick(item, keys, fallback) { + var source = item || {}; + var index; + var value; + + for (index = 0; index < keys.length; index += 1) { + value = source[keys[index]]; + if (value !== undefined && value !== null && value !== '') return value; + } + + return fallback || ''; + } + + function buildFeedbackBody(values) { + // 接口只接收类型、内容、联系方式;页面上的标题合并到内容前面。 + var title = values.feedbackTitle ? '【' + values.feedbackTitle + '】' : ''; + + return { + feedbackType: values.feedbackType || 'suggestion', + feedbackContent: title + (values.feedbackContent || ''), + contactInfo: values.contactInfo || '' + }; + } + + function buildFeedbackRow(item) { + var type = pick(item, ['feedbackType', 'typeName'], '反馈'); + var content = pick(item, ['feedbackContent', 'content'], '暂无描述'); + var status = pick(item, ['status', 'statusName'], '已提交'); + var time = pick(item, ['createTime', 'createDate', 'createdAt'], '时间待确认'); + + return '

' + type + '

' + + status + ' · ' + time + ' · ' + content + + '

查看
'; + } + + function getApi() { + return root.GenealogyApi && root.GenealogyApi.defaultClient; + } + + function query(selector, rootNode) { + return (rootNode || documentRef).querySelector(selector); + } + + function queryAll(selector, rootNode) { + return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)); + } + + function readForm(form) { + // name 属性和反馈接口字段保持一致,减少页面文案变化带来的影响。 + var values = {}; + + queryAll('input[name], textarea[name], select[name]', form).forEach(function (field) { + values[field.name] = String(field.value || '').trim(); + }); + + return values; + } + + function setBusy(button, busy) { + // 加载态只切换 class,具体样式在 CSS 中维护。 + if (!button) return; + button.classList.toggle('is-loading', busy); + if ('disabled' in button) button.disabled = busy; + button.setAttribute('aria-busy', busy ? 'true' : 'false'); + } + + function showMessage(message) { + if (root.layui && root.layui.layer) { + root.layui.layer.msg(message); + return; + } + + root.alert(message); + } + + function renderFeedbackList(items) { + var container = query('[data-feedback-list]'); + + if (!container) return; + if (!items.length) { + container.innerHTML = '
暂无历史反馈
'; + return; + } + + container.innerHTML = items.map(buildFeedbackRow).join(''); + } + + async function loadFeedbackList() { + var api = getApi(); + var container = query('[data-feedback-list]'); + + if (!api || !container) return; + container.classList.add('is-loading'); + try { + renderFeedbackList(normalizeList(await api.feedbackList())); + } catch (error) { + showMessage(error.message || '历史反馈加载失败'); + } finally { + container.classList.remove('is-loading'); + } + } + + async function submitFeedback(form, button) { + var api = getApi(); + var values = readForm(form); + + if (!api) return; + if (!values.feedbackContent) { + showMessage('请填写反馈内容'); + return; + } + + setBusy(button, true); + try { + await api.submitFeedback(buildFeedbackBody(values)); + form.reset(); + showMessage('反馈已提交'); + loadFeedbackList(); + } catch (error) { + showMessage(error.message || '反馈提交失败'); + } finally { + setBusy(button, false); + } + } + + function bindFeedbackForms() { + queryAll('[data-feedback-form]').forEach(function (form) { + form.addEventListener('submit', function (event) { + event.preventDefault(); + submitFeedback(form, query('[type="submit"]', form)); + }); + }); + } + + function init() { + if (!documentRef) return; + + bindFeedbackForms(); + loadFeedbackList(); + } + + return { + normalizeList: normalizeList, + buildFeedbackBody: buildFeedbackBody, + buildFeedbackRow: buildFeedbackRow, + init: init + }; +}); diff --git a/public/js/genealogy-pages.js b/public/js/genealogy-pages.js new file mode 100644 index 0000000..f6287b8 --- /dev/null +++ b/public/js/genealogy-pages.js @@ -0,0 +1,408 @@ +(function (root, factory) { + // 家谱模块脚本同时支持浏览器运行和 Node 单元测试。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.GenealogyPages = factory(root); + if (root.document) { + root.document.addEventListener('DOMContentLoaded', function () { + root.GenealogyPages.init(); + }); + } +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var documentRef = root.document; + + function normalizeList(data) { + // 列表接口可能返回数组、rows、records 或 list,这里统一成数组供页面渲染。 + if (Array.isArray(data)) return data; + if (!data || typeof data !== 'object') return []; + if (Array.isArray(data.rows)) return data.rows; + if (Array.isArray(data.records)) return data.records; + if (Array.isArray(data.list)) return data.list; + if (Array.isArray(data.data)) return data.data; + return []; + } + + function pick(item, keys, fallback) { + // 后端字段名存在少量差异时,按优先级取第一个可用字段。 + var source = item || {}; + var index; + var value; + + for (index = 0; index < keys.length; index += 1) { + value = source[keys[index]]; + if (value !== undefined && value !== null && value !== '') return value; + } + + return fallback || ''; + } + + function buildGenealogyBody(values) { + // 创建家谱接口必填 genealogyName、surname、regionCode,地区编码由地区选择器回填。 + var source = values || {}; + + return { + genealogyName: source.genealogyName || '', + surname: source.surname || '', + regionCode: source.regionCode || '', + addressDetail: source.addressDetail || '', + intro: source.intro || '', + visibility: source.visibility || '1', + joinMode: source.joinMode || '1' + }; + } + + function getApi() { + return root.GenealogyApi && root.GenealogyApi.defaultClient; + } + + function query(selector, rootNode) { + return (rootNode || documentRef).querySelector(selector); + } + + function queryAll(selector, rootNode) { + return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)); + } + + function readForm(form) { + // 页面表单 name 与接口字段对齐,后续加字段只需要补 HTML。 + var values = {}; + + queryAll('input[name], textarea[name], select[name]', form).forEach(function (field) { + values[field.name] = String(field.value || '').trim(); + }); + + return values; + } + + function getQueryParam(search, name) { + // 详情页和加入页用 query 参数传递 genealogyId,测试环境也可直接传 search 字符串。 + var params = new URLSearchParams(String(search || '').replace(/^\?/, '')); + return params.get(name) || ''; + } + + function getCurrentGenealogyId() { + var search = root.location && root.location.search; + return getQueryParam(search, 'id') || getQueryParam(search, 'genealogyId'); + } + + function escapeHtml(value) { + // 接口内容进入 innerHTML 前统一转义,避免公开页面渲染异常内容。 + return String(value === undefined || value === null ? '' : value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function setBusy(button, busy) { + // 加载状态只加 class,样式统一写在 CSS 中。 + if (!button) return; + button.classList.toggle('is-loading', busy); + if ('disabled' in button) button.disabled = busy; + button.setAttribute('aria-busy', busy ? 'true' : 'false'); + } + + function showMessage(message) { + if (root.layui && root.layui.layer) { + root.layui.layer.msg(message); + return; + } + + root.alert(message); + } + + function requireValue(value, message) { + if (value) return true; + showMessage(message); + return false; + } + + function buildFamilyHeroTitle(detail) { + return pick(detail, ['genealogyName', 'name', 'title'], '未命名家谱'); + } + + function formatJoinMode(value) { + // APP.openapi.json 中 joinMode 为枚举值,页面展示时转成中文。 + var map = { + 0: '关闭加入', + 1: '审核加入', + 2: '邀请码加入' + }; + + return map[value] || value || '多人共修'; + } + + function buildFamilySummary(detail) { + // 后端详情字段可能来自统计字段或普通展示字段,这里集中做字段映射。 + var memberCount = pick(detail, ['memberCount', 'membersCount'], '0'); + var generationCount = pick(detail, ['generationCount', 'generationTotal'], '0'); + var articleCount = pick(detail, ['articleCount', 'articlesCount'], '0'); + var albumCount = pick(detail, ['albumCount', 'albumsCount'], '0'); + + return [ + { label: '成员', value: memberCount }, + { label: '世代', value: generationCount }, + { label: '谱文', value: articleCount }, + { label: '相册', value: albumCount } + ]; + } + + function buildJoinApplyBody(values) { + // 接口文档要求 applicantName、phone、relationDesc、applyReason,可选 inviterUserId。 + var source = values || {}; + var inviterUserId = source.inviterUserId ? Number(source.inviterUserId) : undefined; + + return { + applicantName: String(source.applicantName || '').trim(), + phone: String(source.phone || '').trim(), + relationDesc: String(source.relationDesc || '').trim(), + applyReason: String(source.applyReason || '').trim() || '申请加入家谱', + inviterUserId: inviterUserId + }; + } + + function renderPublicGenealogies(items) { + // 广场页复用现有卡片结构,只替换接口返回的数据。 + var container = query('[data-genealogy-list="public"]'); + var html; + + if (!container) return; + if (!items.length) { + container.innerHTML = '
暂无公开家谱
'; + return; + } + + html = items.slice(0, 6).map(function (item, index) { + // 第一条沿用推荐卡片样式,其余走普通家谱卡片样式。 + var id = pick(item, ['genealogyId', 'id'], ''); + var name = pick(item, ['genealogyName', 'name', 'title'], '未命名家谱'); + var surname = pick(item, ['surname'], '家谱'); + var region = pick(item, ['addressDetail', 'regionName', 'originPlace'], '地区待完善'); + var intro = pick(item, ['intro', 'description', 'summary'], '家谱资料正在完善中。'); + var className = index === 0 ? 'family-card featured tilt-card' : 'card family-lite tilt-card'; + + return '' + + '

' + name + '

' + + '

' + intro + '

' + + '
' + region + '' + surname + '
' + + '
'; + }).join(''); + + container.innerHTML = html; + } + + function buildMyGenealogyRow(item) { + // 我的家谱列表保持 profile-module.css 的 module-row 结构。 + var id = pick(item, ['genealogyId', 'id'], ''); + var name = pick(item, ['genealogyName', 'name'], '未命名家谱'); + var role = pick(item, ['roleName', 'memberRole'], '成员'); + var count = pick(item, ['memberCount', 'membersCount'], '0'); + + return '' + + '

' + name + '

' + role + ' · 共修入 ' + count + ' 人

' + + '进入主页
'; + } + + function renderMyGenealogies(items) { + // 没有数据时显示公共空状态,避免保留旧静态示例误导用户。 + var container = query('[data-genealogy-list="mine"]'); + + if (!container) return; + if (!items.length) { + container.innerHTML = '
你还没有创建或加入家谱
'; + return; + } + + container.innerHTML = items.map(buildMyGenealogyRow).join(''); + } + + function renderFamilyDetail(detail) { + var page = query('[data-family-detail]'); + var summaryGrid = query('[data-family-summary]'); + var joinLink = query('[data-family-join-link]'); + var id; + + if (!page) return; + + id = pick(detail, ['genealogyId', 'id'], getCurrentGenealogyId()); + + queryAll('[data-family-text]').forEach(function (node) { + var field = node.getAttribute('data-family-text'); + var fallback = node.getAttribute('data-fallback') || ''; + var value = field === 'title' + ? buildFamilyHeroTitle(detail) + : pick(detail, field.split('|'), fallback); + + node.textContent = field.indexOf('joinMode') !== -1 ? formatJoinMode(value) : value; + }); + + if (summaryGrid) { + summaryGrid.innerHTML = buildFamilySummary(detail).map(function (item) { + return '' + escapeHtml(item.value) + '' + escapeHtml(item.label) + ''; + }).join(''); + } + + if (joinLink && id) { + joinLink.href = 'join-genealogy.html?id=' + encodeURIComponent(id); + } + } + + async function loadFamilyDetail() { + // 只有详情页存在 data-family-detail,且 URL 带 id/genealogyId 时才请求接口。 + var api = getApi(); + var page = query('[data-family-detail]'); + var genealogyId = getCurrentGenealogyId(); + + if (!api || !page) return; + if (!genealogyId) { + showMessage('缺少家谱ID'); + return; + } + + page.classList.add('is-loading'); + try { + renderFamilyDetail(await api.genealogyDetail(genealogyId)); + } catch (error) { + showMessage(error.message || '家谱详情加载失败'); + } finally { + page.classList.remove('is-loading'); + } + } + + async function submitJoinApply(form, button) { + var api = getApi(); + var values = readForm(form); + var genealogyId = values.genealogyId || getCurrentGenealogyId(); + var body = buildJoinApplyBody(values); + + if (!api) return; + if (!requireValue(genealogyId, '请填写家谱ID或邀请码')) return; + if (!requireValue(body.applicantName, '请填写你的姓名')) return; + if (!requireValue(body.phone, '请填写手机号')) return; + if (!requireValue(body.relationDesc, '请填写关系说明')) return; + + setBusy(button, true); + try { + await api.applyJoinGenealogy(genealogyId, body); + showMessage('申请已提交,请等待管理员审核'); + root.location.href = form.getAttribute('data-success-url') || 'profile-join-family.html'; + } catch (error) { + showMessage(error.message || '申请提交失败'); + } finally { + setBusy(button, false); + } + } + + async function loadPublicGenealogies() { + // 只有带 data-genealogy-list="public" 的页面才会真正发起请求。 + var api = getApi(); + var container = query('[data-genealogy-list="public"]'); + + if (!api || !container) return; + container.classList.add('is-loading'); + try { + renderPublicGenealogies(normalizeList(await api.publicGenealogies())); + } catch (error) { + showMessage(error.message || '公开家谱加载失败'); + } finally { + container.classList.remove('is-loading'); + } + } + + async function loadMyGenealogies() { + // 只有个人中心“我的家谱”页面会命中这个容器。 + var api = getApi(); + var container = query('[data-genealogy-list="mine"]'); + + if (!api || !container) return; + container.classList.add('is-loading'); + try { + renderMyGenealogies(normalizeList(await api.myGenealogies())); + } catch (error) { + showMessage(error.message || '我的家谱加载失败'); + } finally { + container.classList.remove('is-loading'); + } + } + + async function submitGenealogy(form, button) { + // 创建成功后跳到我的家谱列表,后续继续维护成员和内容。 + var api = getApi(); + var body = buildGenealogyBody(readForm(form)); + + if (!api) return; + if (!requireValue(body.genealogyName, '请填写家谱名称')) return; + if (!requireValue(body.surname, '请填写主要姓氏')) return; + if (!requireValue(body.regionCode, '请选择家族所在地')) return; + + setBusy(button, true); + try { + await api.createGenealogy(body); + showMessage('家谱创建成功'); + root.location.href = form.getAttribute('data-success-url') || 'profile-families.html'; + } catch (error) { + showMessage(error.message || '家谱创建失败'); + } finally { + setBusy(button, false); + } + } + + function bindCreateForm() { + // 创建表单用 submit 事件,兼容按钮点击和键盘回车提交。 + var form = query('[data-genealogy-form="create"]'); + + if (!form) return; + form.addEventListener('submit', function (event) { + event.preventDefault(); + submitGenealogy(form, query('[type="submit"]', form)); + }); + } + + function bindJoinForm() { + // 加入页用同一个 submit 入口,兼容详情页带 id 跳转和用户手动输入家谱ID。 + var form = query('[data-genealogy-form="join"]'); + var idField; + var currentId; + + if (!form) return; + + idField = query('[name="genealogyId"]', form); + currentId = getCurrentGenealogyId(); + if (idField && currentId && !idField.value) idField.value = currentId; + + form.addEventListener('submit', function (event) { + event.preventDefault(); + submitJoinApply(form, query('[type="submit"]', form)); + }); + } + + function init() { + // 一个模块脚本服务多个家谱页面,通过 data 属性决定当前页面要做什么。 + if (!documentRef) return; + + bindCreateForm(); + bindJoinForm(); + loadPublicGenealogies(); + loadMyGenealogies(); + loadFamilyDetail(); + } + + return { + normalizeList: normalizeList, + pick: pick, + buildGenealogyBody: buildGenealogyBody, + getQueryParam: getQueryParam, + buildFamilyHeroTitle: buildFamilyHeroTitle, + buildFamilySummary: buildFamilySummary, + formatJoinMode: formatJoinMode, + buildJoinApplyBody: buildJoinApplyBody, + buildMyGenealogyRow: buildMyGenealogyRow, + init: init + }; +}); diff --git a/public/js/generation-pages.js b/public/js/generation-pages.js new file mode 100644 index 0000000..e643257 --- /dev/null +++ b/public/js/generation-pages.js @@ -0,0 +1,349 @@ +(function (root, factory) { + // 字辈谱模块同时支持浏览器页面和 Node 单元测试。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.GenerationPages = factory(root); + if (root.document) { + root.document.addEventListener('DOMContentLoaded', function () { + root.GenerationPages.init(); + }); + } +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var documentRef = root.document; + + function normalizeList(data) { + // 通用列表响应可能是数组,也可能包在 rows、records、list 或 data 中。 + if (Array.isArray(data)) return data; + if (!data || typeof data !== 'object') return []; + if (Array.isArray(data.rows)) return data.rows; + if (Array.isArray(data.records)) return data.records; + if (Array.isArray(data.list)) return data.list; + if (Array.isArray(data.data)) return data.data; + return []; + } + + function pick(item, keys, fallback) { + var source = item || {}; + var index; + var value; + + for (index = 0; index < keys.length; index += 1) { + value = source[keys[index]]; + if (value !== undefined && value !== null && value !== '') return value; + } + + return fallback || ''; + } + + function escapeHtml(value) { + // 字辈文本进入 innerHTML 前统一转义。 + return String(value === undefined || value === null ? '' : value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function getApi() { + return root.GenealogyApi && root.GenealogyApi.defaultClient; + } + + function query(selector, rootNode) { + if (!documentRef) return null; + return (rootNode || documentRef).querySelector(selector); + } + + function queryAll(selector, rootNode) { + if (!documentRef) return []; + return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)); + } + + function showMessage(message) { + if (root.layui && root.layui.layer) { + root.layui.layer.msg(message); + return; + } + + if (root.alert) { + root.alert(message); + } + } + + function getQueryParam(search, name) { + var params = new URLSearchParams(String(search || '').replace(/^\?/, '')); + return params.get(name) || ''; + } + + function getCurrentGenealogyId() { + var search = root.location && root.location.search; + var page = query('[data-generation-page]'); + + return getQueryParam(search, 'id') || + getQueryParam(search, 'genealogyId') || + (page && page.getAttribute('data-genealogy-id')) || + ''; + } + + function toNumber(value, fallback) { + var number = Number(String(value || '').trim()); + return Number.isFinite(number) && number > 0 ? number : fallback; + } + + function toBoolean(value) { + return value === true || value === 'true' || value === '1' || value === 'on'; + } + + function buildGenerationBody(values) { + // GenerationPoemBody 必填 generationNo、generationText,其余字段给接口默认值。 + var source = values || {}; + var generationNo = toNumber(source.generationNo, 1); + + return { + generationNo: generationNo, + generationText: String(source.generationText || '').trim(), + description: String(source.description || '').trim(), + sortOrder: toNumber(source.sortOrder, generationNo), + status: String(source.status || '').trim() || '0' + }; + } + + function buildGenerationBatchBody(values) { + // GenerationPoemBatchBody 只包含批量文本和是否遇到断代即停止。 + var source = values || {}; + + return { + content: String(source.content || '').trim(), + stopMissingOldGeneration: toBoolean(source.stopMissingOldGeneration) + }; + } + + function getFormValues(form) { + var values = {}; + + queryAll('[name]', form).forEach(function (field) { + if (field.type === 'checkbox') { + values[field.name] = field.checked ? 'on' : ''; + return; + } + + values[field.name] = field.value; + }); + + return values; + } + + function setBatchStatus(message) { + var statusNode = query('[data-generation-batch-status]'); + + if (statusNode) { + statusNode.textContent = message || ''; + } + } + + function buildGenerationRow(item, genealogyId) { + var poemId = pick(item, ['poemId', 'id'], ''); + var generationNo = pick(item, ['generationNo', 'generation', 'sortOrder'], '1'); + var text = pick(item, ['generationText', 'content', 'text'], '未设置'); + var memberCount = pick(item, ['memberCount', 'personCount', 'count'], '0'); + var description = pick(item, ['description', 'remark'], '暂无备注'); + + return '
' + + '

第 ' + escapeHtml(generationNo) + ' 代:' + escapeHtml(text) + '

' + escapeHtml(memberCount) + ' 人 · ' + escapeHtml(description) + '

' + + '' + + '
'; + } + + function renderGenerations(items, genealogyId) { + var container = query('[data-generation-list]'); + + if (!container) return; + if (!items.length) { + container.innerHTML = '
暂无字辈记录
'; + return; + } + + container.innerHTML = items.map(function (item) { + return buildGenerationRow(item, genealogyId); + }).join(''); + } + + function renderBatchPreview(data) { + var container = query('[data-generation-batch-preview]'); + var items = normalizeList(data); + + if (!container) return; + + if (!items.length) { + container.innerHTML = '
预览完成,接口未返回明细
'; + return; + } + + container.innerHTML = items.map(function (item) { + return buildGenerationRow(item, getCurrentGenealogyId()); + }).join(''); + } + + async function loadGenerations() { + var api = getApi(); + var container = query('[data-generation-list]'); + var genealogyId = getCurrentGenealogyId(); + + if (!api || !container) return; + if (!genealogyId) { + container.innerHTML = '
请从具体家谱进入字辈谱
'; + return; + } + + container.classList.add('is-loading'); + try { + renderGenerations(normalizeList(await api.generationPoems(genealogyId)), genealogyId); + } catch (error) { + showMessage(error.message || '字辈谱加载失败'); + } finally { + container.classList.remove('is-loading'); + } + } + + async function createGeneration() { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var generationNo; + var generationText; + var body; + + if (!api || !genealogyId) { + showMessage('请从具体家谱进入字辈谱'); + return; + } + + generationNo = root.prompt('请输入世代序号', ''); + if (!generationNo) return; + generationText = root.prompt('请输入字辈内容', ''); + if (!generationText) return; + + body = buildGenerationBody({ + generationNo: generationNo, + generationText: generationText + }); + + try { + await api.createGenerationPoem(genealogyId, body); + showMessage('字辈已新增'); + loadGenerations(); + } catch (error) { + showMessage(error.message || '新增字辈失败'); + } + } + + async function editGeneration(button) { + var api = getApi(); + var genealogyId = button.getAttribute('data-genealogy-id') || getCurrentGenealogyId(); + var poemId = button.getAttribute('data-generation-edit'); + var currentText = button.getAttribute('data-generation-text') || ''; + var generationNo = button.getAttribute('data-generation-no') || '1'; + var nextText; + + if (!api || !genealogyId || !poemId) return; + + nextText = root.prompt('请输入新的字辈', currentText); + if (!nextText) return; + + button.classList.add('is-loading'); + try { + await api.updateGenerationPoem(genealogyId, poemId, buildGenerationBody({ + generationNo: generationNo, + generationText: nextText + })); + showMessage('字辈已更新'); + loadGenerations(); + } catch (error) { + showMessage(error.message || '更新字辈失败'); + } finally { + button.classList.remove('is-loading'); + } + } + + async function submitGenerationBatch(action) { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var form = query('[data-generation-batch-form]'); + var body; + var result; + + if (!api || !genealogyId || !form) { + showMessage('请从具体家谱进入字辈谱'); + return; + } + + body = buildGenerationBatchBody(getFormValues(form)); + if (!body.content) { + showMessage('请填写批量字辈内容'); + return; + } + + try { + setBatchStatus(action === 'save' ? '批量保存中...' : '批量预览中...'); + + if (action === 'save') { + await api.saveGenerationPoemsBatch(genealogyId, body); + setBatchStatus('批量保存完成'); + showMessage('批量字辈已保存'); + loadGenerations(); + return; + } + + result = await api.previewGenerationPoems(genealogyId, body); + renderBatchPreview(result); + setBatchStatus('批量预览完成'); + } catch (error) { + setBatchStatus('批量处理失败'); + showMessage(error.message || '批量字辈处理失败'); + } + } + + function bindActions() { + if (!documentRef) return; + + documentRef.addEventListener('click', function (event) { + var addButton = event.target.closest('[data-generation-add]'); + var editButton = event.target.closest('[data-generation-edit]'); + var batchButton = event.target.closest('[data-generation-batch-action]'); + + if (addButton) { + event.preventDefault(); + createGeneration(); + } + + if (editButton) { + event.preventDefault(); + editGeneration(editButton); + } + + if (batchButton) { + event.preventDefault(); + submitGenerationBatch(batchButton.getAttribute('data-generation-batch-action')); + } + }); + } + + function init() { + if (!documentRef) return; + + bindActions(); + loadGenerations(); + } + + return { + normalizeList: normalizeList, + buildGenerationBody: buildGenerationBody, + buildGenerationBatchBody: buildGenerationBatchBody, + buildGenerationRow: buildGenerationRow, + init: init + }; +}); diff --git a/public/js/growth-pages.js b/public/js/growth-pages.js new file mode 100644 index 0000000..d6589be --- /dev/null +++ b/public/js/growth-pages.js @@ -0,0 +1,245 @@ +(function (root, factory) { + // 成长记录模块同时支持浏览器页面和 Node 单元测试。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.GrowthPages = factory(root); + if (root.document) { + root.document.addEventListener('DOMContentLoaded', function () { + root.GrowthPages.init(); + }); + } +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var documentRef = root.document; + + function normalizeList(data) { + // 成长记录列表兼容数组、rows、records、list、data。 + if (Array.isArray(data)) return data; + if (!data || typeof data !== 'object') return []; + if (Array.isArray(data.rows)) return data.rows; + if (Array.isArray(data.records)) return data.records; + if (Array.isArray(data.list)) return data.list; + if (Array.isArray(data.data)) return data.data; + return []; + } + + function pick(item, keys, fallback) { + var source = item || {}; + var index; + var value; + + for (index = 0; index < keys.length; index += 1) { + value = source[keys[index]]; + if (value !== undefined && value !== null && value !== '') return value; + } + + return fallback || ''; + } + + function escapeHtml(value) { + // 成长记录标题和内容进入 innerHTML 前统一转义。 + return String(value === undefined || value === null ? '' : value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function getApi() { + return root.GenealogyApi && root.GenealogyApi.defaultClient; + } + + function query(selector, rootNode) { + return (rootNode || documentRef).querySelector(selector); + } + + function queryAll(selector, rootNode) { + return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)); + } + + function showMessage(message) { + if (root.layui && root.layui.layer) { + root.layui.layer.msg(message); + return; + } + + root.alert(message); + } + + function getQueryParam(search, name) { + var params = new URLSearchParams(String(search || '').replace(/^\?/, '')); + return params.get(name) || ''; + } + + function getCurrentGenealogyId() { + var search = root.location && root.location.search; + var page = query('[data-growth-page], [data-growth-edit-page]'); + + return getQueryParam(search, 'genealogyId') || + (page && page.getAttribute('data-genealogy-id')) || + getQueryParam(search, 'id') || + ''; + } + + function getCurrentRecordId() { + var search = root.location && root.location.search; + + return getQueryParam(search, 'recordId') || ''; + } + + function trimOrUndefined(value) { + var text = String(value === undefined || value === null ? '' : value).trim(); + + return text || undefined; + } + + function toNumber(value, fallback) { + var text = String(value === undefined || value === null ? '' : value).trim(); + var number = Number(text); + + return text && Number.isFinite(number) ? number : fallback; + } + + function buildGrowthBody(values) { + // GrowthRecordBody 来自 APP.openapi.json,recordTitle 必填。 + var source = values || {}; + + return { + lineagePersonId: toNumber(source.lineagePersonId), + recordType: String(source.recordType || '').trim() || 'growth', + recordTitle: String(source.recordTitle || '').trim(), + recordContent: trimOrUndefined(source.recordContent), + recordDate: trimOrUndefined(source.recordDate), + remindTime: trimOrUndefined(source.remindTime), + mediaOssIds: trimOrUndefined(source.mediaOssIds), + sortOrder: toNumber(source.sortOrder, 1), + status: String(source.status || '').trim() || '0' + }; + } + + function getRecordId(item) { + return pick(item, ['recordId', 'id'], ''); + } + + function buildGrowthRow(item, genealogyId) { + var recordId = getRecordId(item); + var title = pick(item, ['recordTitle', 'title'], '未命名成长记录'); + var type = pick(item, ['recordType', 'type'], 'growth'); + var date = pick(item, ['recordDate', 'createTime'], '未记录日期'); + + return '' + + '

' + escapeHtml(title) + '

' + escapeHtml(type) + ' · ' + escapeHtml(date) + '

' + + '编辑
'; + } + + function renderGrowthRecords(items, genealogyId) { + var container = query('[data-growth-list]'); + var list = normalizeList(items); + + if (!container) return; + if (!list.length) { + container.innerHTML = '
暂无成长记录
'; + return; + } + + container.innerHTML = list.map(function (item) { + return buildGrowthRow(item, genealogyId); + }).join(''); + } + + function getFormValues(form) { + var values = {}; + + queryAll('[name]', form).forEach(function (field) { + values[field.name] = field.value; + }); + + return values; + } + + function fillGrowthForm(record) { + var source = record || {}; + + queryAll('[name]').forEach(function (field) { + var value = pick(source, [field.name], ''); + if (value !== '') field.value = value; + }); + } + + async function loadGrowthRecords() { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var recordId = getCurrentRecordId(); + + if (!api || !genealogyId) return; + + try { + if (query('[data-growth-list]')) { + renderGrowthRecords(await api.growthRecords(genealogyId), genealogyId); + } + + if (recordId && query('[data-growth-edit-page]')) { + fillGrowthForm(await api.growthRecordDetail(genealogyId, recordId)); + } + } catch (error) { + showMessage(error.message || '成长记录加载失败'); + } + } + + async function submitGrowth(form) { + var api = getApi(); + var genealogyId = getCurrentGenealogyId(); + var recordId = getCurrentRecordId(); + var body = buildGrowthBody(getFormValues(form)); + + if (!api || !genealogyId) return; + if (!body.recordTitle) { + showMessage('请填写成长记录标题'); + return; + } + + try { + if (recordId) { + await api.updateGrowthRecord(genealogyId, recordId, body); + } else { + await api.createGrowthRecord(genealogyId, body); + } + + showMessage('成长记录已保存'); + root.location.href = 'profile-growth.html?genealogyId=' + encodeURIComponent(genealogyId); + } catch (error) { + showMessage(error.message || '保存成长记录失败'); + } + } + + function bindActions() { + if (!documentRef) return; + + documentRef.addEventListener('submit', function (event) { + var form = event.target.closest('[data-growth-form]'); + + if (!form) return; + event.preventDefault(); + submitGrowth(form); + }); + } + + function init() { + if (!documentRef) return; + + bindActions(); + loadGrowthRecords(); + } + + return { + normalizeList: normalizeList, + buildGrowthBody: buildGrowthBody, + buildGrowthRow: buildGrowthRow, + init: init + }; +}); diff --git a/public/js/help-pages.js b/public/js/help-pages.js new file mode 100644 index 0000000..07cf0b5 --- /dev/null +++ b/public/js/help-pages.js @@ -0,0 +1,156 @@ +(function (root, factory) { + // 帮助中心模块同时支持浏览器页面和 Node 单元测试。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.HelpPages = factory(root); + if (root.document) { + root.document.addEventListener('DOMContentLoaded', function () { + root.HelpPages.init(); + }); + } +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var documentRef = root.document; + + function normalizeList(data) { + // 通用列表响应可能是数组,也可能包在 rows、records、list 或 data 中。 + if (Array.isArray(data)) return data; + if (!data || typeof data !== 'object') return []; + if (Array.isArray(data.rows)) return data.rows; + if (Array.isArray(data.records)) return data.records; + if (Array.isArray(data.list)) return data.list; + if (Array.isArray(data.data)) return data.data; + return []; + } + + function pick(item, keys, fallback) { + var source = item || {}; + var index; + var value; + + for (index = 0; index < keys.length; index += 1) { + value = source[keys[index]]; + if (value !== undefined && value !== null && value !== '') return value; + } + + return fallback || ''; + } + + function escapeHtml(value) { + // 帮助文章来自接口,进入 innerHTML 前先转义。 + return String(value === undefined || value === null ? '' : value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function getApi() { + return root.GenealogyApi && root.GenealogyApi.defaultClient; + } + + function query(selector, rootNode) { + return (rootNode || documentRef).querySelector(selector); + } + + function showMessage(message) { + if (root.layui && root.layui.layer) { + root.layui.layer.msg(message); + return; + } + + root.alert(message); + } + + function buildHelpItem(item, index) { + // 列表接口字段未在文档中细化,这里兼容常见标题和摘要字段。 + var id = pick(item, ['helpId', 'id', 'articleId'], ''); + var title = pick(item, ['title', 'articleTitle', 'name'], '帮助文章'); + var content = pick(item, ['summary', 'description', 'content', 'articleContent'], '暂无详细说明'); + var hasFullContent = Boolean(pick(item, ['content', 'articleContent'], '')); + var open = index === 0 ? ' open' : ''; + + return '' + + '' + escapeHtml(title) + '' + + '

' + escapeHtml(content) + '

' + + ''; + } + + function renderHelpArticles(items) { + var container = query('[data-help-list]'); + + if (!container) return; + if (!items.length) { + container.innerHTML = '
暂无帮助文章
'; + return; + } + + container.innerHTML = items.map(buildHelpItem).join(''); + } + + async function loadHelpArticles() { + var api = getApi(); + var container = query('[data-help-list]'); + + if (!api || !container) return; + + container.classList.add('is-loading'); + try { + renderHelpArticles(normalizeList(await api.helpArticles())); + } catch (error) { + showMessage(error.message || '帮助文章加载失败'); + } finally { + container.classList.remove('is-loading'); + } + } + + async function loadHelpDetail(details) { + var api = getApi(); + var helpId = details && details.getAttribute('data-help-id'); + var paragraph = details && query('p', details); + var content; + + if (!api || !details || !helpId || details.getAttribute('data-help-loaded') === 'true') return; + + details.classList.add('is-loading'); + try { + content = await api.helpArticleDetail(helpId); + paragraph.textContent = pick(content, ['content', 'articleContent', 'summary', 'description'], paragraph.textContent); + details.setAttribute('data-help-loaded', 'true'); + } catch (error) { + showMessage(error.message || '帮助详情加载失败'); + } finally { + details.classList.remove('is-loading'); + } + } + + function bindDetailLoad() { + var container = query('[data-help-list]'); + + if (!container) return; + container.addEventListener('toggle', function (event) { + if (event.target && event.target.matches('details') && event.target.open) { + loadHelpDetail(event.target); + } + }, true); + } + + function init() { + if (!documentRef) return; + + bindDetailLoad(); + loadHelpArticles(); + } + + return { + normalizeList: normalizeList, + buildHelpItem: buildHelpItem, + renderHelpArticles: renderHelpArticles, + init: init + }; +}); diff --git a/public/js/join-apply-pages.js b/public/js/join-apply-pages.js new file mode 100644 index 0000000..704bb71 --- /dev/null +++ b/public/js/join-apply-pages.js @@ -0,0 +1,271 @@ +(function (root, factory) { + // 加入申请模块同时支持浏览器页面和 Node 单元测试。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.JoinApplyPages = factory(root); + if (root.document) { + root.document.addEventListener('DOMContentLoaded', function () { + root.JoinApplyPages.init(); + }); + } +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var documentRef = root.document; + + function normalizeList(data) { + // 列表响应可能直接是数组,也可能使用 rows、records、list 或 data 包裹。 + if (Array.isArray(data)) return data; + if (!data || typeof data !== 'object') return []; + if (Array.isArray(data.rows)) return data.rows; + if (Array.isArray(data.records)) return data.records; + if (Array.isArray(data.list)) return data.list; + if (Array.isArray(data.data)) return data.data; + return []; + } + + function pick(item, keys, fallback) { + var source = item || {}; + var index; + var value; + + for (index = 0; index < keys.length; index += 1) { + value = source[keys[index]]; + if (value !== undefined && value !== null && value !== '') return value; + } + + return fallback || ''; + } + + function escapeHtml(value) { + // 申请人信息来自接口,渲染前统一转义。 + return String(value === undefined || value === null ? '' : value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function getApi() { + return root.GenealogyApi && root.GenealogyApi.defaultClient; + } + + function query(selector, rootNode) { + return (rootNode || documentRef).querySelector(selector); + } + + function showMessage(message) { + if (root.layui && root.layui.layer) { + root.layui.layer.msg(message); + return; + } + + root.alert(message); + } + + function getQueryParam(search, name) { + var params = new URLSearchParams(String(search || '').replace(/^\?/, '')); + return params.get(name) || ''; + } + + function getCurrentGenealogyId() { + var search = root.location && root.location.search; + var fromQuery = getQueryParam(search, 'id') || getQueryParam(search, 'genealogyId'); + var fromPage = query('[data-genealogy-id]'); + + return fromQuery || (fromPage && fromPage.getAttribute('data-genealogy-id')) || ''; + } + + function formatApplyStatus(status) { + // 审核接口要求 status,按常见字典值做页面中文展示。 + var map = { + 0: '待审核', + 1: '已通过', + 2: '已拒绝' + }; + + return map[status] || status || '待审核'; + } + + function buildAuditBody(action, remark) { + var isReject = action === 'reject'; + + return { + status: isReject ? '2' : '1', + auditRemark: remark || (isReject ? '信息核验未通过' : '信息核验通过') + }; + } + + function buildPendingRow(item, genealogyId) { + var applyId = pick(item, ['applyId', 'id'], ''); + var name = pick(item, ['applicantName', 'name', 'nickName'], '未命名申请人'); + var relation = pick(item, ['relationDesc', 'applyReason', 'remark'], '关系说明待确认'); + var time = pick(item, ['createTime', 'createdAt', 'applyTime'], '提交时间待确认'); + + return '
' + + '

' + escapeHtml(name) + '申请加入家谱

' + escapeHtml(time) + ' · ' + escapeHtml(relation) + '

' + + '
' + + '' + + '' + + '
'; + } + + function buildMineRow(item) { + var applyId = pick(item, ['applyId', 'id'], ''); + var genealogyName = pick(item, ['genealogyName', 'familyName', 'name'], '未命名家谱'); + var status = formatApplyStatus(pick(item, ['status', 'auditStatus'], '0')); + var relation = pick(item, ['relationDesc', 'applyReason', 'auditRemark'], '关系说明待确认'); + var action = status === '待审核' + ? '' + : '' + escapeHtml(status) + ''; + + return '
' + + '

' + escapeHtml(genealogyName) + '

' + escapeHtml(status) + ' · ' + escapeHtml(relation) + '

' + + action + + '
'; + } + + function renderPending(items, genealogyId) { + var container = query('[data-join-apply-list="pending"]'); + + if (!container) return; + if (!items.length) { + container.innerHTML = '
暂无待审核申请
'; + return; + } + + container.innerHTML = items.map(function (item) { + return buildPendingRow(item, genealogyId); + }).join(''); + } + + function renderMine(items) { + var container = query('[data-join-apply-list="mine"]'); + + if (!container) return; + if (!items.length) { + container.innerHTML = '
暂无加入申请记录
'; + return; + } + + container.innerHTML = items.map(buildMineRow).join(''); + } + + async function loadPending() { + var api = getApi(); + var container = query('[data-join-apply-list="pending"]'); + var genealogyId = getCurrentGenealogyId(); + + if (!api || !container) return; + if (!genealogyId) { + container.innerHTML = '
请从具体家谱进入审核页
'; + return; + } + + container.classList.add('is-loading'); + try { + renderPending(normalizeList(await api.pendingJoinApplies(genealogyId)), genealogyId); + } catch (error) { + showMessage(error.message || '待审核申请加载失败'); + } finally { + container.classList.remove('is-loading'); + } + } + + async function loadMine() { + var api = getApi(); + var container = query('[data-join-apply-list="mine"]'); + + if (!api || !container) return; + + container.classList.add('is-loading'); + try { + renderMine(normalizeList(await api.myJoinApplies())); + } catch (error) { + showMessage(error.message || '加入申请记录加载失败'); + } finally { + container.classList.remove('is-loading'); + } + } + + async function audit(button) { + var api = getApi(); + var action = button.getAttribute('data-join-audit'); + var genealogyId = button.getAttribute('data-genealogy-id'); + var applyId = button.getAttribute('data-apply-id'); + var remark = action === 'reject' ? root.prompt('请输入拒绝原因', '') : ''; + + if (!api || !genealogyId || !applyId) return; + if (action === 'reject' && remark === null) return; + + button.classList.add('is-loading'); + try { + await api.auditJoinApply(genealogyId, applyId, buildAuditBody(action, remark)); + showMessage(action === 'reject' ? '已拒绝申请' : '已同意申请'); + loadPending(); + } catch (error) { + showMessage(error.message || '审核申请失败'); + } finally { + button.classList.remove('is-loading'); + } + } + + async function cancel(button) { + var api = getApi(); + var applyId = button.getAttribute('data-join-cancel'); + + if (!api || !applyId) return; + if (root.confirm && !root.confirm('确认撤销当前加入申请?')) return; + + button.classList.add('is-loading'); + try { + await api.cancelJoinApply(applyId); + showMessage('已撤销加入申请'); + loadMine(); + } catch (error) { + showMessage(error.message || '撤销申请失败'); + } finally { + button.classList.remove('is-loading'); + } + } + + function bindActions() { + if (!documentRef) return; + + documentRef.addEventListener('click', function (event) { + var auditButton = event.target.closest('[data-join-audit]'); + var cancelButton = event.target.closest('[data-join-cancel]'); + + if (auditButton) { + event.preventDefault(); + audit(auditButton); + } + + if (cancelButton) { + event.preventDefault(); + cancel(cancelButton); + } + }); + } + + function init() { + if (!documentRef) return; + + bindActions(); + loadPending(); + loadMine(); + } + + return { + normalizeList: normalizeList, + formatApplyStatus: formatApplyStatus, + buildAuditBody: buildAuditBody, + buildPendingRow: buildPendingRow, + buildMineRow: buildMineRow, + init: init + }; +}); diff --git a/public/js/jquery360.js b/public/js/jquery360.js new file mode 100644 index 0000000..200b54e --- /dev/null +++ b/public/js/jquery360.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 035?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('(E(3l,1Q){if(3l.3O){G}if(!3l.7N){3l.7N={}}if(!7N.aS){7N.aS=E(){}}H cJ=\'4.1.11 (ma-m9-31)\',4K=m8.mf.2W(),2E=4K.3B(\'gf\')>-1&&4K.3B(\'gz\')==-1,j4=4K.3B(\'gf\')==-1&&4K.3B(\'lY\')>-1,6J=4K.3B(\'lV\')>-1&&4K.3B(\'lZ\')==-1,6H=4K.3B(\'m2\')>-1,c1=4K.3B(\'gz\')>-1,kr=4K.3B(\'mz\')>-1,kH=/mx|mw|mA/.2f(4K),6D=1M.mo!=\'mq\',52=!3l.gR,dq=/(?:gf|mp|lp|gz)[\\/:\\s](\\d+)/.2y(4K),3F=dq?dq[1]:\'0\',da=2k jr().jt();E 85(L){if(!L){G 1l}G ha.4M.7F.1P(L)===\'[aw it]\'}E an(L){if(!L){G 1l}G ha.4M.7F.1P(L)===\'[aw hJ]\'}E 5w(L,1w){1J(H i=0,1D=1w.1a;i<1D;i++){if(L===1w[i]){G i}}G-1}E 1y(3g,fn){if(85(3g)){1J(H i=0,1D=3g.1a;i<1D;i++){if(fn.1P(3g[i],i,3g[i])===1l){3d}}}1d{1J(H 15 in 3g){if(3g.lO(15)){if(fn.1P(3g[15],15,3g[15])===1l){3d}}}}}E 4b(2z){G 2z.1g(/(?:^[ \\t\\n\\r]+)|(?:[ \\t\\n\\r]+$)/g,\'\')}E dT(L,2z,4t){4t=4t===1Q?\',\':4t;G(4t+2z+4t).3B(4t+L+4t)>=0}E 3n(L,bA){bA=bA||\'px\';G L&&/^-?\\d+(?:\\.\\d+)?$/.2f(L)?L+bA:L}E 3f(L){H 1G;G L&&(1G=/(\\d+)/.2y(L))?6Y(1G[1],10):0}E 3Z(L){G L.1g(/&/g,\'&iG;\').1g(//g,\'>\').1g(/"/g,\'&gq;\')}E 8Q(L){G L.1g(/</g,\'<\').1g(/>/g,\'>\').1g(/&gq;/g,\'"\').1g(/&iG;/g,\'&\')}E 8F(2z){H 1w=2z.2s(\'-\');2z=\'\';1y(1w,E(15,L){2z+=(15>0)?L.ft(0).d2()+L.4r(1):L});G 2z}E aj(L){E bB(d){H s=6Y(d,10).7F(16).d2();G s.1a>1?s:\'0\'+s}G L.1g(/lG\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)/ig,E($0,$1,$2,$3){G\'#\'+bB($1)+bB($2)+bB($3)})}E 3o(L,4t){4t=4t===1Q?\',\':4t;H 25={},1w=85(L)?L:L.2s(4t),1G;1y(1w,E(15,L){if((1G=/^(\\d+)\\.\\.(\\d+)$/.2y(L))){1J(H i=6Y(1G[1],10);i<=6Y(1G[2],10);i++){25[i.7F()]=1f}}1d{25[L]=1f}});G 25}E b6(3g,2p){G it.4M.jd.1P(3g,2p||0)}E 2a(L,jc){G L===1Q?jc:L}E ip(1v){G!1v||/[<>"]/.2f(1v)}E hK(1v,8l){G 1v.3B(\'?\')>=0?1v+\'&\'+8l:1v+\'?\'+8l}E 4D(1i,1q,9X){if(!9X){9X=1q;1q=1z}H 8D;if(1q){H fn=E(){};fn.4M=1q.4M;8D=2k fn();1y(9X,E(15,L){8D[15]=L})}1d{8D=9X}8D.ba=1i;1i.4M=8D;1i.1q=1q?1q.4M:1z}E dU(1A){H 1G;if((1G=/\\{[\\s\\S]*\\}|\\[[\\s\\S]*\\]/.2y(1A))){1A=1G[0]}H cx=/[\\lS\\lu\\lQ-\\lE\\lN\\lq\\mr\\oh-\\nL\\nK-\\nJ\\nN-\\mG\\nS\\nR-\\o9]/g;cx.o8=0;if(cx.2f(1A)){1A=1A.1g(cx,E(a){G\'\\\\u\'+(\'o6\'+a.ia(0).7F(16)).jd(-4)})}if(/^[\\],:{}\\s]*$/.2f(1A.1g(/\\\\(?:["\\\\\\/mZ]|u[0-9a-fA-F]{4})/g,\'@\').1g(/"[^"\\\\\\n\\r]*"|1f|1l|1z|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g,\']\').1g(/(?:^|:|,)(?:\\s*\\[)+/g,\'\'))){G n3(\'(\'+1A+\')\')}jf\'n2 n1 iA\'}H 6h=7n.jA;H K={jJ:1l,n0:cJ,6S:2E,mT:6J,mL:6H,mI:c1,V:3F,mM:da,2e:1y,mR:85,mP:an,n5:5w,nl:dT,gL:4b,nt:3n,hn:3f,5k:3Z,6g:8Q,nq:8F,9F:aj,hh:3o,dV:b6,ng:2a,cQ:ip,n7:hK,n6:4D,c7:dU};H dC=3o(\'a,nO,nc,b,fb,nd,hW,br,2m,ne,5H,fK,nf,em,23,i,2n,1B,hI,nb,2G,25,q,s,na,1o,a3,1p,fP,9b,fN,fO,2U,n8,u,H\'),dy=3o(\'hb,nh,9y,1n,3C,dd,gA,M,dl,dt,np,3K,nr,h1,h2,h3,h4,h5,h6,4p,hr,N,1H,hI,hx,li,25,1X,9C,nm,4x,aw,ol,p,3S,2w,1e,1c,gk,56,hX,cS,iy,1Y,66,5u\'),dB=3o(\'bV,ni,fb,br,nj,nk,hr,2n,1B,hx,3E,9C,8l,7k\'),f8=3o(\'b,fb,hW,fK,em,23,i,s,a3,1p,fP,9b,fN,fO,u\'),iR=3o(\'2n,1c,1B,2U,2m\'),bj=3o(\'3S,1e,2w\'),aD=3o(\'N,4p,1n,56,66,1c,ol,5u,li\'),n4=3o(\'mO,dd,dt,li,W,p,56,hX,cS,iy,66\'),hv=3o(\'6B,mQ,mN,mH,55,mJ,mK,mS,mY,mU,gn,eA,7b\'),iW=3o(\'1B,2m,2U,1o\');E i4(){H 2J=1M.7f(\'2w\'),1S;1J(H i=0,1D=2J.1a;i<1D;i++){1S=2J[i].1S||\'\';if(/aZ[\\w\\-\\.]*\\.js/.2f(1S)){G 1S.mV(0,1S.mW(\'/\')+1)}}G\'\'}K.5Q=i4();K.W={2K:1f,5r:1l,9E:1f,kz:1f,3Q:1f,f3:1f,5Q:K.5Q,3T:K.5Q+\'kT/\',db:K.5Q+\'1k/\',5U:\'8H\',3y:\'fG-fD\',8X:\'\',6V:\'p\',g2:2,jB:\'3K\',cD:2,h9:\'fT\',kk:1f,jy:1l,6s:\'ke-eG\',7a:\'\\t\',5t:\'\',6A:\'\',b7:nu,5A:63,ld:50,fr:bU,3I:[\'5D\',\'|\',\'8s\',\'8r\',\'81\',\'8e\',\'8g\',\'8h\',\'69\',\'8u\',\'8n\',\'6p\',\'6Z\',\'77\',\'9o\',\'1c\',,\'av\',\'9H\',\'70\',\'5y\'],3I:[\'5D\',\'|\',\'8s\',\'8r\',\'|\',\'9V\',\'7d\',\'aA\',\'7C\',\'71\',\'|\',\'81\',\'8e\',\'8g\',\'8h\',\'8t\',\'8p\',\'7s\',\'cy\',\'as\',\'at\',\'av\',\'9H\',\'70\',\'|\',\'5y\',\'/\',\'4E\',\'5d\',\'69\',\'|\',\'8u\',\'8n\',\'6p\',\'6Z\',\'77\',\'9o\',\'ah\',\'cz\',\'|\',\'1c\',\'hr\',\'4Z\',\'3E\',\'82\',\'|\',\'9m\'],73:[\'5D\',\'5y\'],eB:[[\'#jN\',\'#jO\',\'#jP\',\'#jQ\',\'#jM\',\'#jL\'],[\'#jH\',\'#jI\',\'#jS\',\'#jZ\',\'#k0\',\'#k1\'],[\'#jY\',\'#jX\',\'#jT\',\'#jU\',\'#jV\',\'#jW\'],[\'#ep\',\'#jG\',\'#jF\',\'#jp\',\'#jq\',\'#8P\']],kJ:[\'nv\',\'dz\',\'o1\',\'o3\',\'gj\',\'o4\',\'o0\',\'nZ\'],4g:{23:[\'id\',\'1j\',\'1K\',\'3H\',\'hA\',\'.2v-1K\'],1p:[\'id\',\'1j\',\'.1K\',\'.2v-1K\',\'.23-3H\',\'.23-6w\',\'.2v\',\'.23-6Q\',\'.23-1e\',\'.1A-7m\',\'.aC-2j\',\'.5s-17\'],M:[\'id\',\'1j\',\'2j\',\'.1F\',\'.4I\',\'.30\',\'.1A-2j\',\'.1K\',\'.2v-1K\',\'.23-3H\',\'.23-6w\',\'.23-6Q\',\'.2v\',\'.23-1e\',\'.1A-7m\',\'.aC-2j\',\'.5s-17\'],1c:[\'id\',\'1j\',\'1F\',\'iD\',\'iE\',\'19\',\'17\',\'2j\',\'iv\',\'.30\',\'.4I\',\'.1F\',\'i3\',\'.1A-2j\',\'.1K\',\'.2v-1K\',\'.23-3H\',\'.23-6w\',\'.23-6Q\',\'.23-1e\',\'.1A-7m\',\'.2v\',\'.19\',\'.17\',\'.1F-2g\'],\'56,cS\':[\'id\',\'1j\',\'2j\',\'nV\',\'19\',\'17\',\'gD\',\'hi\',\'i3\',\'.1A-2j\',\'.1K\',\'.2v-1K\',\'.23-3H\',\'.23-6w\',\'.23-6Q\',\'.23-1e\',\'.1A-7m\',\'.aC-2j\',\'.2v\',\'.1F\'],a:[\'id\',\'1j\',\'4v\',\'2C\',\'O\'],7k:[\'id\',\'1j\',\'1S\',\'19\',\'17\',\'1m\',\'gX\',\'hc\',\'hd\',\'.19\',\'.17\',\'2j\',\'hm\',\'nW\'],2n:[\'id\',\'1j\',\'1S\',\'19\',\'17\',\'1F\',\'cV\',\'1Y\',\'2j\',\'.19\',\'.17\',\'.1F\'],\'p,ol,5u,li,9y,h1,h2,h3,h4,h5,h6\':[\'id\',\'1j\',\'2j\',\'.1A-2j\',\'.1K\',\'.2v-1K\',\'.23-3H\',\'.23-6w\',\'.2v\',\'.23-6Q\',\'.23-1e\',\'.1A-7m\',\'.aC-2j\',\'.1A-7s\',\'.5s-17\'],3S:[\'id\',\'1j\'],gg:[\'1e\',\'hl\'],5D:[\'1S\',\'1m\'],hr:[\'id\',\'1j\',\'.fT-3d-8b\'],\'br,gk,66,9b,b,fN,fO,em,i,u,fP,s,fK\':[\'id\',\'1j\'],1H:[\'id\',\'1j\',\'1S\',\'cW\',\'19\',\'17\',\'.19\',\'.17\']},kL:\'\'};H fF=1l;H fC=3o(\'8,9,13,32,46,48..57,59,61,65..90,i9,i8..ik,od,oe..of,og..oc\');H i5=3o(\'33..40\');H cr={};1y(fC,E(15,L){cr[15]=L});1y(i5,E(15,L){cr[15]=L});E ij(el,1m,fn){if(el.fv){el.fv(1m,fn,fF)}1d if(el.aa){el.aa(\'on\'+1m,fn)}}E a7(el,1m,fn){if(el.i6){el.i6(1m,fn,fF)}1d if(el.i7){el.i7(\'on\'+1m,fn)}}H i2=(\'9c,oa,nU,nT,2m,nD,9S,c3,ct,6T,nE,\'+\'1O,nF,nG,bC,5C,fh,fg,nC,nB,nx,nw,86,\'+\'9q,ny,nz,g0,nA,nH,c2,2P,2C,i1,nI,nP,2Y\').2s(\',\');E f9(el,4w){J.2A(el,4w)}4D(f9,{2A:E(el,4w){H f=J,Q=el.4T||el.1M||el;f.4w=4w;1y(i2,E(15,L){f[L]=4w[L]});if(!f.2C){f.2C=f.2P||Q}if(f.2C.29===3){f.2C=f.2C.1U}if(!f.g0&&f.bC){f.g0=f.bC===f.2C?f.i1:f.bC}if(f.86==1z&&f.c3!=1z){H d=Q.9t,1n=Q.1n;f.86=f.c3+(d&&d.dA||1n&&1n.dA||0)-(d&&d.hZ||1n&&1n.hZ||0);f.9q=f.ct+(d&&d.dk||1n&&1n.dk||0)-(d&&d.i0||1n&&1n.i0||0)}if(!f.2Y&&((f.9S||f.9S===0)?f.9S:f.fh)){f.2Y=f.9S||f.fh}if(!f.fg&&f.6T){f.fg=f.6T}if(!f.2Y&&f.2m!==1Q){f.2Y=(f.2m&1?1:(f.2m&2?3:(f.2m&4?2:0)))}ly(f.2Y){6y lx:f.2Y=59;3d;6y lA:6y lB:6y 43:f.2Y=61;3d;6y lv:6y 45:f.2Y=i8;3d;6y 42:f.2Y=i9;3d;6y 47:f.2Y=ik;3d;6y 78:f.2Y=lF;3d}if(f.2Y>=96&&f.2Y<=lH){f.2Y-=48}},3X:E(){H ev=J.4w;if(ev.3X){ev.3X()}1d{ev.lI=1l}},5L:E(){H ev=J.4w;if(ev.5L){ev.5L()}1d{ev.lK=1f}},9A:E(){J.3X();J.5L()}});H 9M=\'hy\'+da,ff=0,3x={};E bz(el){G el[9M]||1z}E im(el){el[9M]=++ff;G ff}E fd(el){3r{3J el[9M]}3q(e){if(el.cp){el.cp(9M)}}}E 7T(el,1m,fn){if(1m.3B(\',\')>=0){1y(1m.2s(\',\'),E(){7T(el,J,fn)});G}H id=bz(el);if(!id){id=im(el)}if(3x[id]===1Q){3x[id]={}}H 2B=3x[id][1m];if(2B&&2B.1a>0){a7(el,1m,2B[0])}1d{3x[id][1m]=[];3x[id].el=el}2B=3x[id][1m];if(2B.1a===0){2B[0]=E(e){H io=e?2k f9(el,e):1Q;1y(2B,E(i,4w){if(i>0&&4w){4w.1P(el,io)}})}}if(5w(fn,2B)<0){2B.1N(fn)}ij(el,1m,2B[0])}E 94(el,1m,fn){if(1m&&1m.3B(\',\')>=0){1y(1m.2s(\',\'),E(){94(el,J,fn)});G}H id=bz(el);if(!id){G}if(1m===1Q){if(id in 3x){1y(3x[id],E(15,2B){if(15!=\'el\'&&2B.1a>0){a7(el,15,2B[0])}});3J 3x[id];fd(el)}G}if(!3x[id]){G}H 2B=3x[id][1m];if(2B&&2B.1a>0){if(fn===1Q){a7(el,1m,2B[0]);3J 3x[id][1m]}1d{1y(2B,E(i,4w){if(i>0&&4w===fn){2B.jR(i,1)}});if(2B.1a==1){a7(el,1m,2B[0]);3J 3x[id][1m]}}H aY=0;1y(3x[id],E(){aY++});if(aY<2){3J 3x[id];fd(el)}}}E di(el,1m){if(1m.3B(\',\')>=0){1y(1m.2s(\',\'),E(){di(el,J)});G}H id=bz(el);if(!id){G}H 2B=3x[id][1m];if(3x[id]&&2B&&2B.1a>0){2B[0]()}}E cK(el,15,fn){H f=J;15=/^\\d{2,}$/.2f(15)?15:15.d2().ia(0);7T(el,\'a4\',E(e){if(e.6T&&e.2Y==15&&!e.c2&&!e.9c){fn.1P(el);e.9A()}})}H fw=1l;E hV(fn){if(fw){fn(3O);G}H 68=1l;E a8(){if(!68){68=1f;fn(3O);fw=1f}}E fu(){if(!68){3r{1M.9t.ie(\'3W\')}3q(e){6v(fu,63);G}a8()}}E ic(){if(1M.c5===\'lD\'){a8()}}if(1M.fv){7T(1M,\'lT\',a8)}1d if(1M.aa){7T(1M,\'lJ\',ic);H fz=1l;3r{fz=3l.lL==1z}3q(e){}if(1M.9t.ie&&fz){fu()}}7T(3l,\'aH\',a8)}if(3l.aa){3l.aa(\'ls\',E(){1y(3x,E(15,2B){if(2B.el){94(2B.el)}})})}K.ln=cK;K.a9=hV;E 7M(1s){H 37={},ab=/\\s*([\\w\\-]+)\\s*:([^;]*)(;|$)/g,1G;22((1G=ab.2y(1s))){H 15=4b(1G[1].2W()),L=4b(aj(1G[2]));37[15]=L}G 37}E 5f(2l){H 37={},ab=/\\s+(?:([\\w\\-:]+)|(?:([\\w\\-:]+)=([^\\s"\'<>]+))|(?:([\\w\\-:"]+)="([^"]*)")|(?:([\\w\\-:"]+)=\'([^\']*)\'))(?=(?:\\s|\\/|>)+)/g,1G;22((1G=ab.2y(2l))){H 15=(1G[1]||1G[2]||1G[4]||1G[6]).2W(),L=(1G[2]?1G[3]:(1G[4]?1G[5]:1G[7]))||\'\';37[15]=L}G 37}E k5(2l,2V){if(/\\s+1j\\s*=/.2f(2l)){2l=2l.1g(/(\\s+1j=["\']?)([^"\']*)(["\']?[\\s>])/,E($0,$1,$2,$3){if((\' \'+$2+\' \').3B(\' \'+2V+\' \')<0){G $2===\'\'?$1+2V+$3:$1+$2+\' \'+2V+$3}1d{G $0}})}1d{2l=2l.4r(0,2l.1a-1)+\' 1j="\'+2V+\'">\'}G 2l}E hN(1s){H 2z=\'\';1y(7M(1s),E(15,L){2z+=15+\':\'+L+\';\'});G 2z}E 9r(1v,5e,4S,8k){5e=2a(5e,\'\').2W();if(1v.4r(0,5)!=\'1O:\'){1v=1v.1g(/([^:])\\/\\//g,\'$1/\')}if(5w(5e,[\'7h\',\'hD\',\'9i\'])<0){G 1v}4S=4S||9N.lj+\'//\'+9N.4S;if(8k===1Q){H m=9N.8k.1G(/^(\\/.*)\\//);8k=m?m[1]:\'\'}H 1G;if((1G=/^(\\w+:\\/\\/[^\\/]*)/.2y(1v))){if(1G[1]!==4S){G 1v}}1d if(/^\\w+:/.2f(1v)){G 1v}E gr(5E){H 6l=5E.2s(\'/\'),a6=[];1J(H i=0,1D=6l.1a;i<1D;i++){H 5Z=6l[i];if(5Z==\'..\'){if(a6.1a>0){a6.6K()}}1d if(5Z!==\'\'&&5Z!=\'.\'){a6.1N(5Z)}}G\'/\'+a6.3Y(\'/\')}if(/^\\//.2f(1v)){1v=4S+gr(1v.4r(1))}1d if(!/^\\w+:\\/\\//.2f(1v)){1v=4S+gr(8k+\'/\'+1v)}E gB(5E,gu){if(1v.4r(0,5E.1a)===5E){H 1w=[];1J(H i=0;i0){by+=\'/\'+1w.3Y(\'/\')}if(8k==\'/\'){by+=\'/\'}G by+1v.4r(5E.1a)}1d{if((1G=/^(.*)\\//.2y(5E))){G gB(1G[1],++gu)}}}if(5e===\'hD\'){1v=gB(4S+8k,0).4r(2)}1d if(5e===\'7h\'){if(1v.4r(0,4S.1a)===4S){1v=1v.4r(4S.1a)}}G 1v}E 7i(N,4g,8X,9D,7a){if(N==1z){N=\'\'}8X=8X||\'\';9D=2a(9D,1l);7a=2a(7a,\'\\t\');H hB=\'hF-a3,x-a3,a3,lU,g6,x-g6,hF-g6\'.2s(\',\');N=N.1g(/(<(?:3S|3S\\s[^>]*)>)([\\s\\S]*?)(<\\/3S>)/ig,E($0,$1,$2,$3){G $1+$2.1g(/<(?:br|br\\s[^>]*)>/ig,\'\\n\')+$3});N=N.1g(/<(?:br|br\\s[^>]*)\\s*\\/?>\\s*<\\/p>/ig,\'

\');N=N.1g(/(<(?:p|p\\s[^>]*)>)\\s*(<\\/p>)/ig,\'$1
$2\');N=N.1g(/\\gl/g,\'\');N=N.1g(/\\mj/g,\'&7C;\');N=N.1g(/\\mk/g,\'&ab;\');N=N.1g(/\\ml/g,\'&hG;\');N=N.1g(/\\mm/g,\'&hG;\');N=N.1g(/<[^>]+/g,E($0){G $0.1g(/\\s+/g,\' \')});H 6t={};if(4g){1y(4g,E(15,L){H 1w=15.2s(\',\');1J(H i=0,1D=1w.1a;i<1D;i++){6t[1w[i]]=3o(L)}});if(!6t.2w){N=N.1g(/(<(?:2w|2w\\s[^>]*)>)([\\s\\S]*?)(<\\/2w>)/ig,\'\')}if(!6t.1e){N=N.1g(/(<(?:1e|1e\\s[^>]*)>)([\\s\\S]*?)(<\\/1e>)/ig,\'\')}}H bK=/(\\s*)<(\\/)?([\\w\\-:]+)((?:\\s+|(?:\\s+[\\w\\-:]+)|(?:\\s+[\\w\\-:]+=[^\\s"\'<>]+)|(?:\\s+[\\w\\-:"]+="[^"]*")|(?:\\s+[\\w\\-:"]+=\'[^\']*\'))*)(\\/)?>(\\s*)/g;H 6z=[];N=N.1g(bK,E($0,$1,$2,$3,$4,$5,$6){H 2b=$0,6X=$1||\'\',7y=$2||\'\',2x=$3.2W(),1V=$4||\'\',aK=$5?\' \'+$5:\'\',5O=$6||\'\';if(4g&&!6t[2x]){G\'\'}if(aK===\'\'&&dB[2x]){aK=\' /\'}if(dC[2x]){if(6X){6X=\' \'}if(5O){5O=\' \'}}if(bj[2x]){if(7y){5O=\'\\n\'}1d{6X=\'\\n\'}}if(9D&&2x==\'br\'){5O=\'\\n\'}if(dy[2x]&&!bj[2x]){if(9D){if(7y&&6z.1a>0&&6z[6z.1a-1]===2x){6z.6K()}1d{6z.1N(2x)}6X=\'\\n\';5O=\'\\n\';1J(H i=0,1D=7y?6z.1a:6z.1a-1;i<1D;i++){6X+=7a;if(!7y){5O+=7a}}if(aK){6z.6K()}1d if(!7y){5O+=7a}}1d{6X=5O=\'\'}}if(1V!==\'\'){H 4R=5f(2b);if(2x===\'23\'){H 9B={},8a=\'\';1y(4R,E(15,L){if(15===\'1K\'){9B.1K=L;3J 4R[15]}if(15===\'3H\'){9B[\'23-3H\']=hB[6Y(L,10)-1]||\'\';3J 4R[15]}if(15===\'hA\'){9B[\'23-6w\']=L;3J 4R[15]}if(15===\'1e\'){8a=L}});if(8a&&!/;$/.2f(8a)){8a+=\';\'}1y(9B,E(15,L){if(L===\'\'){G}if(/\\s/.2f(L)){L="\'"+L+"\'"}8a+=15+\':\'+L+\';\'});4R.1e=8a}1y(4R,E(15,L){if(hv[15]){4R[15]=15}if(5w(15,[\'1S\',\'4v\'])>=0){4R[15]=9r(L,8X)}if(4g&&15!==\'1e\'&&!6t[2x][\'*\']&&!6t[2x][15]||2x===\'1n\'&&15===\'mE\'||/^hy\\d+$/.2f(15)){3J 4R[15]}if(15===\'1e\'&&L!==\'\'){H bv=7M(L);1y(bv,E(k,v){if(4g&&!6t[2x].1e&&!6t[2x][\'.\'+k]){3J bv[k]}});H 1e=\'\';1y(bv,E(k,v){1e+=k+\':\'+v+\';\'});4R.1e=1e}});1V=\'\';1y(4R,E(15,L){if(15===\'1e\'&&L===\'\'){G}L=L.1g(/"/g,\'&gq;\');1V+=\' \'+15+\'="\'+L+\'"\'})}if(2x===\'23\'){2x=\'1p\'}G 6X+\'<\'+7y+2x+1V+aK+\'>\'+5O});N=N.1g(/(<(?:3S|3S\\s[^>]*)>)([\\s\\S]*?)(<\\/3S>)/ig,E($0,$1,$2,$3){G $1+$2.1g(/\\n/g,\'<1p id="hQ">\\n\')+$3});N=N.1g(/\\n\\s*\\n/g,\'\\n\');N=N.1g(/<1p id="hQ">\\n/g,\'\\n\');G 4b(N)}E g8(N,4g){N=N.1g(/<9C[\\s\\S]*?>/ig,\'\').1g(//ig,\'\').1g(/<1e[^>]*>[\\s\\S]*?<\\/1e>/ig,\'\').1g(/<2w[^>]*>[\\s\\S]*?<\\/2w>/ig,\'\').1g(/]+>[\\s\\S]*?<\\/w:[^>]+>/ig,\'\').1g(/]+>[\\s\\S]*?<\\/o:[^>]+>/ig,\'\').1g(/
[\\s\\S]*?<\\/hR>/ig,\'\').1g(/<(?:1c|56)[^>]*>/ig,E(2b){G 2b.1g(/1F-cB:([#\\w\\s]+)/ig,\'1F:$1\')});G 7i(N,4g)}E gv(1S){if(/\\.(93|lm)(\\?|$)/i.2f(1S)){G\'mh/x-pn-hS-1I\'}if(/\\.(kO|kN)(\\?|$)/i.2f(1S)){G\'kn/x-m1-8j\'}G\'gg/x-ms-lg-1I\'}E hT(1m){if(/hS/i.2f(1m)){G\'ke-93\'}if(/8j/i.2f(1m)){G\'ke-8j\'}G\'ke-aq\'}E dO(bw){G 5f(6g(bw))}E cv(2I){H N=\'<7k \';1y(2I,E(15,L){N+=15+\'="\'+L+\'" \'});N+=\'/>\';G N}E dK(hP,2I){H 19=2I.19,17=2I.17,1m=2I.1m||gv(2I.1S),bw=cv(2I),1e=\'\';if(/\\D/.2f(19)){1e+=\'19:\'+19+\';\'}1d if(19>0){1e+=\'19:\'+19+\'px;\'}if(/\\D/.2f(17)){1e+=\'17:\'+17+\';\'}1d if(17>0){1e+=\'17:\'+17+\'px;\'}H N=\'<2n 1j="\'+hT(1m)+\'" 1S="\'+hP+\'" \';if(1e!==\'\'){N+=\'1e="\'+1e+\'" \'}N+=\'1O-ke-2l="\'+5k(bw)+\'" cV="" />\';G N}E hL(2z,1O){H fn=2k hJ("3g","H p=[],7d=E(){p.1N.jK(p,fo);};"+"lX(3g){p.1N(\'"+2z.1g(/[\\r\\t\\n]/g," ").2s("<%").3Y("\\t").1g(/((^|%>)[^\\t]*)\'/g,"$1\\r").1g(/\\t=(.*?)%>/g,"\',$1,\'").2s("\\t").3Y("\');").2s("%>").3Y("p.1N(\'").2s("\\r").3Y("\\\\\'")+"\');}G p.3Y(\'\');");G 1O?fn(1O):fn}K.m5=9r;K.gZ=7i;K.m6=7M;K.md=5f;K.me=gv;K.mg=dO;K.mc=cv;K.mb=dK;K.m7=g8;K.o2=hL;E ay(5c,4y){if(5c.29==9&&4y.29!=9){G 1f}22((4y=4y.1U)){if(4y==5c){G 1f}}G 1l}H f6=1M.4e(\'M\');f6.iT(\'2V\',\'t\');H hM=f6.2V!==\'t\';E ar(el,15){15=15.2W();H L=1z;if(!hM&&el.51.2W()!=\'2w\'){H M=el.4T.4e(\'M\');M.3z(el.5V(1l));H 37=5f(8Q(M.3u));if(15 in 37){L=37[15]}}1d{3r{L=el.9G(15,2)}3q(e){L=el.9G(15,1)}}if(15===\'1e\'&&L!==1z){L=hN(L)}G L}E aQ(1E,1T){H dN=1E.2s(\',\');if(dN.1a>1){H bx=[];1y(dN,E(){1y(aQ(J,1T),E(){if(5w(J,bx)<0){bx.1N(J)}})});G bx}1T=1T||1M;E 5k(2z){if(8o 2z!=\'c4\'){G 2z}G 2z.1g(/([^\\w\\-])/g,\'\\\\$1\')}E bD(2z){G 2z.1g(/\\\\/g,\'\')}E bE(dP,iq){G dP===\'*\'||dP.2W()===5k(iq.2W())}E dH(id,2l,1T){H 1w=[],Q=1T.4T||1T,el=Q.fe(bD(id));if(el){if(bE(2l,el.51)&&ay(1T,el)){1w.1N(el)}}G 1w}E dI(2V,2l,1T){H Q=1T.4T||1T,1w=[],2J,i,1D,el;if(1T.j0){2J=1T.j0(bD(2V));1J(i=0,1D=2J.1a;i<1D;i++){el=2J[i];if(bE(2l,el.51)){1w.1N(el)}}}1d if(Q.iZ){2J=Q.iZ((1T.51!==\'#1M\'?1T.51+\' \':\'\')+2l+\'.\'+2V);1J(i=0,1D=2J.1a;i<1D;i++){el=2J[i];if(ay(1T,el)){1w.1N(el)}}}1d{2J=1T.7f(2l);2V=\' \'+2V+\' \';1J(i=0,1D=2J.1a;i<1D;i++){el=2J[i];if(el.29==1){H 2L=el.2V;if(2L&&(\' \'+2L+\' \').3B(2V)>-1){1w.1N(el)}}}}G 1w}E j2(O,2l,1T){H 1w=[],Q=1T.4T||1T,2J=Q.qh(bD(O)),el;1J(H i=0,1D=2J.1a;i<1D;i++){el=2J[i];if(bE(2l,el.51)&&ay(1T,el)){if(el.9G(\'O\')!==1z){1w.1N(el)}}}G 1w}E dQ(15,L,2l,1T){H 1w=[],2J=1T.7f(2l),el;1J(H i=0,1D=2J.1a;i<1D;i++){el=2J[i];if(el.29==1){if(L===1z){if(ar(el,15)!==1z){1w.1N(el)}}1d{if(L===5k(ar(el,15))){1w.1N(el)}}}}G 1w}E 1o(1E,1T){H 1w=[],4s;4s=/^((?:\\\\.|[^.#\\s\\[<>])+)/.2y(1E);H 2l=4s?4s[1]:\'*\';if((4s=/#((?:[\\w\\-]|\\\\.)+)$/.2y(1E))){1w=dH(4s[1],2l,1T)}1d if((4s=/\\.((?:[\\w\\-]|\\\\.)+)$/.2y(1E))){1w=dI(4s[1],2l,1T)}1d if((4s=/\\[((?:[\\w\\-]|\\\\.)+)\\]/.2y(1E))){1w=dQ(4s[1].2W(),1z,2l,1T)}1d if((4s=/\\[((?:[\\w\\-]|\\\\.)+)\\s*=\\s*[\'"]?((?:\\\\.|[^\'"]+)+)[\'"]?\\]/.2y(1E))){H 15=4s[1].2W(),L=4s[2];if(15===\'id\'){1w=dH(L,2l,1T)}1d if(15===\'1j\'){1w=dI(L,2l,1T)}1d if(15===\'O\'){1w=j2(L,2l,1T)}1d{1w=dQ(15,L,2l,1T)}}1d{H 2J=1T.7f(2l),el;1J(H i=0,1D=2J.1a;i<1D;i++){el=2J[i];if(el.29==1){1w.1N(el)}}}G 1w}H 6l=[],1w,bK=/((?:\\\\.|[^\\s>])+|[\\s>])/g;22((1w=bK.2y(1E))){if(1w[1]!==\' \'){6l.1N(1w[1])}}H 7Q=[];if(6l.1a==1){G 1o(6l[0],1T)}H dY=1l,5Z,2J,bL,L,v,i,j,k,1a,1D,l;1J(i=0,j3=6l.1a;i\'){dY=1f;iC}if(i>0){2J=[];1J(j=0,1D=7Q.1a;j<1D;j++){L=7Q[j];bL=1o(5Z,L);1J(k=0,l=bL.1a;k0?1w[0]:1z}K.qc=iX;K.pY=aQ;E 5B(L){G K(L)[0]}E 6P(T){if(!T){G 1M}G T.4T||T.1M||T}E 6N(T){if(!T){G 3l}H Q=6P(T);G Q.pX||Q.pW}E jg(el,N){if(el.29!=1){G}H Q=6P(el);3r{el.3u=\'<2n id="91" 19="0" 17="0" 1e="4a:4N;" />\'+N;H 97=Q.fe(\'91\');97.1U.7g(97)}3q(e){K(el).j8();K(\'@\'+N,Q).2e(E(){el.3z(J)})}}E bM(el,2L){G dT(2L,el.2V,\' \')}E bJ(el,15,L){if(2E&&3F<8&&15.2W()==\'1j\'){15=\'2V\'}el.iT(15,\'\'+L)}E je(el,15){if(2E&&3F<8&&15.2W()==\'1j\'){15=\'2V\'}bJ(el,15,\'\');el.cp(15)}E dj(T){if(!T||!T.51){G\'\'}G T.51.2W()}E j7(el,15){H f=J,38=6N(el),ag=8F(15),L=\'\';if(38.iS){H 1e=38.iS(el,1z);L=1e[ag]||1e.pU(15)||el.1e[ag]}1d if(el.iU){L=el.iU[ag]||el.1e[ag]}G L}E de(T){G!!iW[dj(T)]}E 4i(Q){Q=Q||1M;G 6D?Q.1n:Q.9t}E q2(Q){H el=4i(Q);G 7n.9P(el.fY,el.7A)}E q8(Q){H el=4i(Q);G 7n.9P(el.jC,el.9g)}E 7D(Q){Q=Q||1M;H x,y;if(2E||j4||c1){x=4i(Q).dA;y=4i(Q).dk}1d{x=6N(Q).q7;y=6N(Q).q6}G{x:x,y:y}}E 4c(T){J.2A(T)}4D(4c,{2A:E(T){H f=J;T=85(T)?T:[T];H 1a=0;1J(H i=0,1D=T.1a;i<1D;i++){if(T[i]){f[i]=T[i].ba===4c?T[i][0]:T[i];1a++}}f.1a=1a;f.Q=6P(f[0]);f.O=dj(f[0]);f.1m=f.1a>0?f[0].29:1z;f.38=6N(f[0])},2e:E(fn){H f=J;1J(H i=0;i0?37.eq(0):1z},83:E(){H 37=J.2O();G 37.1a>0?37.eq(37.1a-1):1z},2N:E(){if(J.1a<1){G-1}H i=-1,bI=J[0];22(bI){i++;bI=bI.az}G i},2H:E(){if(J.1a<1){G 1z}H T=J[0].az;G T?2k 4c(T):1z},2S:E(){if(J.1a<1){G 1z}H T=J[0].4B;G T?2k 4c(T):1z},gi:E(fn,9e){if(J.1a<1){G}9e=(9e===1Q)?1f:9e;E dr(T){H n=9e?T.3L:T.6b;22(n){H 2S=9e?n.4B:n.az;if(fn(n)===1l){G 1l}if(dr(n)===1l){G 1l}n=2S}}dr(J[0]);G J}});1y((\'5R,1Z,qR,qO,aH,6c,9T,jw,2d,qK,\'+\'3U,co,af,ao,ax,qM,q9,\'+\'oP,1o,d5,a4,oS,ad,iA,6M\').2s(\',\'),E(i,1m){4c.4M[1m]=E(fn){G fn?J.3e(1m,fn):J.iB(1m)}});H iw=K;K=E(1E,1T){if(1E===1Q||1E===1z){G}E 58(T){if(!T[0]){T=[]}G 2k 4c(T)}if(8o 1E===\'c4\'){if(1T){1T=5B(1T)}H 1a=1E.1a;if(1E.ft(0)===\'@\'){1E=1E.4r(1)}if(1E.1a!==1a||/<.+>/.2f(1E)){H Q=1T?1T.4T||1T:1M,M=Q.4e(\'M\'),37=[];M.3u=\'<2n id="91" 19="0" 17="0" 1e="4a:4N;" />\'+1E;1J(H i=0,1D=M.2Q.1a;i<1D;i++){H 1i=M.2Q[i];if(1i.id==\'91\'){iC}37.1N(1i)}G 58(37)}G 58(aQ(1E,1T))}if(1E&&1E.ba===4c){G 1E}if(1E.dV){1E=1E.dV()}if(85(1E)){G 58(1E)}G 58(b6(fo))};1y(iw,E(15,L){K[15]=L});K.p3=4c;3l.3O=K;H 8R=0,8J=1,7G=2,8I=3,eX=0;E e7(R){R.4k=(R.1W===R.2t&&R.2c===R.2o);G R}E bh(R,6U,9O){H Q=R.Q,4q=[];E 9Z(T,2c,2o){H 1a=T.4d.1a,aU;if(6U){H 5V=T.5V(1f);if(2c>0){aU=5V.ap(2c)}1d{aU=5V}if(2o<1a){aU.ap(2o-2c)}}if(9O){H 3C=T;if(2c>0){3C=T.ap(2c);R.3V(T,2c)}if(2o<1a){H 5i=3C.ap(2o-2c);R.3i(5i,0)}4q.1N(3C)}G aU}E is(){if(9O){R.aO().2g(1f)}1J(H i=0,1D=4q.1a;i<1D;i++){H T=4q[i];if(T.1U){T.1U.7g(T)}}}H 9L=R.4H().ea();H 2h=-1,aN=-1,a5=-1,2F=-1,4C=R.4V(),3a=Q.g1();if(4C.29==3){H 80=9Z(4C,R.2c,R.2o);if(6U){3a.3z(80)}is();G 6U?3a:R}E f1(1q,3a){H T=1q.3L,f2;22(T){H 4h=2k 5S(Q).4P(T);2h=4h.7O(8J,R);if(2h>=0&&aN<=0){aN=4h.7O(8R,R)}if(aN>=0&&a5<=0){a5=4h.7O(7G,R)}if(a5>=0&&2F<=0){2F=4h.7O(8I,R)}if(2F>=0){G 1l}f2=T.4B;if(2h>0){if(T.29==1){if(aN>=0&&a5<=0){if(6U){3a.3z(T.5V(1f))}if(9O){4q.1N(T)}}1d{H bF;if(6U){bF=T.5V(1l);3a.3z(bF)}if(f1(T,bF)===1l){G 1l}}}1d if(T.29==3){H 80;if(T==9L.1W){80=9Z(T,9L.2c,T.4d.1a)}1d if(T==9L.2t){80=9Z(T,0,9L.2o)}1d{80=9Z(T,0,T.4d.1a)}if(6U){3r{3a.3z(80)}3q(e){}}}}T=f2}}f1(4C,3a);if(9O){R.aO().2g(1f)}1J(H i=0,1D=4q.1a;i<1D;i++){H T=4q[i];if(T.1U){T.1U.7g(T)}}G 6U?3a:R}E 9w(R,el){H T=el;22(T){H 1b=K(T);if(1b.O==\'gw\'||1b.O==\'1o\'){G}T=T.1U}3r{R.dM(el)}3q(e){}}E eD(1x,2D){H Q=1x.bb().4T,ak=1x.bH();ak.2g(2D);H 1q=ak.bb(),87=1q.2Q;if(87.1a===0){G{T:1q.1U,2p:K(1q).2N()}}H 3v=Q,7u=0,5v=-1;H 4h=1x.bH();9w(4h,1q);1J(H i=0,1D=87.1a;i<1D;i++){H T=87[i];5v=4h.iI(\'dG\',ak);if(5v===0){G{T:T.1U,2p:i}}if(T.29==1){H bG=1x.bH(),36,1b=K(T),58=T;if(1b.5h()){36=Q.4e(\'1p\');1b.8b(36);58=36;7u+=1b.1A().1g(/\\r\\n|\\n|\\r/g,\'\').1a}9w(bG,58);4h.bd(\'e9\',bG);if(5v>0){7u+=bG.1A.1g(/\\r\\n|\\n|\\r/g,\'\').1a}1d{7u=0}if(36){K(36).1t()}}1d if(T.29==3){4h.iP(\'iL\',T.4d.1a);7u+=T.4d.1a}if(5v<0){3v=T}}if(5v<0&&3v.29==1){G{T:1q,2p:K(1q.6b).2N()+1}}if(5v>0){22(3v.4B&&3v.29==1){3v=3v.4B}}4h=1x.bH();9w(4h,1q);4h.bd(\'e9\',ak);7u-=4h.1A.1g(/\\r\\n|\\n|\\r/g,\'\').1a;if(5v>0&&3v.29==3){H 9v=3v.az;22(9v&&9v.29==3){7u-=9v.4d.1a;9v=9v.az}}G{T:3v,2p:7u}}E e8(T,2p){H Q=T.4T||T,R=Q.1n.dm();if(Q==T){R.2g(1f);G R}if(T.29==1&&T.2Q.1a>0){H 2O=T.2Q,2D,1i;if(2p===0){1i=2O[0];2D=1f}1d{1i=2O[2p-1];2D=1l}if(!1i){G R}if(K(1i).O===\'4p\'){if(2p===1){2D=1f}if(2p===2){2D=1l}R.2g(2D);G R}if(1i.29==1){H bu=K(1i),1p;if(bu.5h()){1p=Q.4e(\'1p\');if(2D){bu.7v(1p)}1d{bu.8b(1p)}1i=1p}9w(R,1i);R.2g(2D);if(1p){K(1p).1t()}G R}T=1i;2p=2D?0:1i.4d.1a}H 36=Q.4e(\'1p\');K(T).7v(36);9w(R,36);R.iP(\'iL\',2p);K(36).1t();G R}E fX(1x){H Q,R;E ee(2h){if(K(2h.T).O==\'66\'){2h.T=2h.T.2T[2h.2p];2h.2p=0}}if(52){if(1x.2R){Q=6P(1x.2R(0));R=2k 5S(Q);R.4P(1x.2R(0));G R}Q=1x.bb().4T;H 2h=eD(1x,1f),2F=eD(1x,1l);ee(2h);ee(2F);R=2k 5S(Q);R.3V(2h.T,2h.2p);R.3i(2F.T,2F.2p);G R}H 1W=1x.1W;Q=1W.4T||1W;R=2k 5S(Q);R.3V(1W,1x.2c);R.3i(1x.2t,1x.2o);G R}E 5S(Q){J.2A(Q)}4D(5S,{2A:E(Q){H f=J;f.1W=Q;f.2c=0;f.2t=Q;f.2o=0;f.4k=1f;f.Q=Q},4V:E(){E ef(T){H ed=[];22(T){ed.1N(T);T=T.1U}G ed}H bg=ef(J.1W),eg=ef(J.2t),i=0,e5=bg.1a,iK=eg.1a,bt,bf;22(++i){bt=bg[e5-i];bf=eg[iK-i];if(!bt||!bf||bt!==bf){3d}}G bg[e5-i+1]},3V:E(T,2p){H f=J,Q=f.Q;f.1W=T;f.2c=2p;if(f.2t===Q){f.2t=T;f.2o=2p}G e7(J)},3i:E(T,2p){H f=J,Q=f.Q;f.2t=T;f.2o=2p;if(f.1W===Q){f.1W=T;f.2c=2p}G e7(J)},4Q:E(T){G J.3V(T.1U||J.Q,K(T).2N())},aG:E(T){G J.3V(T.1U||J.Q,K(T).2N()+1)},7S:E(T){G J.3i(T.1U||J.Q,K(T).2N())},7B:E(T){G J.3i(T.1U||J.Q,K(T).2N()+1)},4P:E(T){G J.4Q(T).7B(T)},49:E(T){H 1b=K(T);if(1b.1m==3||1b.9U()){G J.4P(T)}H 2O=1b.2O();if(2O.1a>0){G J.4Q(2O[0]).7B(2O[2O.1a-1])}G J.3V(T,0).3i(T,0)},2g:E(iH){if(iH){G J.3i(J.1W,J.2c)}G J.3V(J.2t,J.2o)},7O:E(5g,R){H ew=J.3A(),eu=R.3A();if(52){H 1w={};1w[8R]=\'dG\';1w[8J]=\'gF\';1w[7G]=\'p5\';1w[8I]=\'e9\';H 5v=ew.iI(1w[5g],eu);if(5v!==0){G 5v}H 5c,4y,39,aR,ai;if(5g===8R||5g===8I){5c=J.1W;aR=J.2c}if(5g===8J||5g===7G){5c=J.2t;aR=J.2o}if(5g===8R||5g===8J){4y=R.1W;ai=R.2c}if(5g===7G||5g===8I){4y=R.2t;ai=R.2o}if(5c===4y){H ey=aR-ai;G ey>0?1:(ey<0?-1:0)}39=4y;22(39&&39.1U!==5c){39=39.1U}if(39){G K(39).2N()>=aR?-1:1}39=5c;22(39&&39.1U!==4y){39=39.1U}if(39){G K(39).2N()>=ai?1:-1}39=K(4y).2S();if(39&&39.eC(5c)){G 1}39=K(5c).2S();if(39&&39.eC(4y)){G-1}}1d{G ew.7O(5g,eu)}},4H:E(){G 2k 5S(J.Q).3V(J.1W,J.2c).3i(J.2t,J.2o)},7F:E(){H 1x=J.3A(),2z=52?1x.1A:1x.7F();G 2z.1g(/\\r\\n|\\n|\\r/g,\'\')},gE:E(){G bh(J,1f,1l)},gQ:E(){G bh(J,1l,1f)},bn:E(){G bh(J,1f,1f)},4f:E(T){H f=J,1L=f.1W,2q=f.2c,ec=f.2t,eo=f.2o,3L,6b,c,be=1;if(T.51.2W()===\'#1M-pG\'){3L=T.3L;6b=T.6b;be=T.2Q.1a}if(1L.29==1){c=1L.2Q[2q];if(c){1L.7V(T,c);if(1L===ec){eo+=be}}1d{1L.3z(T)}}1d if(1L.29==3){if(2q===0){1L.1U.7V(T,1L);if(1L.1U===ec){eo+=be}}1d if(2q>=1L.4d.1a){if(1L.4B){1L.1U.7V(T,1L.4B)}1d{1L.1U.3z(T)}}1d{if(2q>0){c=1L.ap(2q)}1d{c=1L}1L.1U.7V(T,c);if(1L===ec){ec=c;eo-=2q}}}if(3L){f.4Q(3L).7B(6b)}1d{f.4P(T)}if(f.7O(7G,f.4H().3i(ec,eo))>=1){G f}G f.3i(ec,eo)},pL:E(T){T.3z(J.bn());G J.4f(T).4P(T)},5h:E(){H f=J,1L=f.1W,2q=f.2c,ec=f.2t,eo=f.2o,1x;G 1L.29==1&&1L===ec&&2q+1===eo&&K(1L.2Q[2q]).5h()},3A:E(jh){H f=J,Q=f.Q,T,1x;if(!52){1x=Q.hk();3r{1x.3V(f.1W,f.2c);1x.3i(f.2t,f.2o)}3q(e){}G 1x}if(jh&&f.5h()){1x=Q.1n.pH();1x.pI(f.1W.2Q[f.2c]);G 1x}H R=f.4H().ea();1x=Q.1n.dm();1x.bd(\'dG\',e8(R.1W,R.2c));1x.bd(\'gF\',e8(R.2t,R.2o));G 1x},N:E(){G K(J.gE()).bX()},ea:E(){H f=J;E eb(T,1C,2D){if(T.29!=1){G}H 2O=K(T).2O();if(2O.1a===0){G}H 3W,5i,1i,2p;if(1C>0){3W=2O.eq(1C-1)}if(1C<2O.1a){5i=2O.eq(1C)}if(3W&&3W.1m==3){1i=3W[0];2p=1i.4d.1a}if(5i&&5i.1m==3){1i=5i[0];2p=0}if(!1i){G}if(2D){f.3V(1i,2p)}1d{f.3i(1i,2p)}}eb(f.1W,f.2c,1f);eb(f.2t,f.2o,1l);G f},aO:E(){H f=J;E eF(T,1C,2D){if(T.29!=3){G}if(1C===0){if(2D){f.4Q(T)}1d{f.7S(T)}}1d if(1C==T.4d.1a){if(2D){f.aG(T)}1d{f.7B(T)}}}eF(f.1W,f.2c,1f);eF(f.2t,f.2o,1l);G f},gm:E(b9){H f=J;f.aO();E eY(T,1C,2D){H 1b=K(T),1q;if(1b.1m==3||aD[1b.O]||!b9&&1b.7P()){G}if(1C===0){22(!1b.2H()){1q=1b.1q();if(!1q||aD[1q.O]||!b9&&1q.7P()){3d}1b=1q}if(2D){f.4Q(1b[0])}1d{f.7S(1b[0])}}1d if(1C==1b.2O().1a){22(!1b.2S()){1q=1b.1q();if(!1q||aD[1q.O]||!b9&&1q.7P()){3d}1b=1q}if(2D){f.aG(1b[0])}1d{f.7B(1b[0])}}}eY(f.1W,f.2c,1f);eY(f.2t,f.2o,1l);G f},a0:E(){H f=J,1i,4k=f.4k;22(f.1W.29==1&&(1i=f.1W.2Q[f.2c])&&1i.29==1&&!K(1i).9U()){f.3V(1i,0)}if(4k){G f.2g(4k)}22(f.2t.29==1&&f.2o>0&&(1i=f.2t.2Q[f.2o-1])&&1i.29==1&&!K(1i).9U()){f.3i(1i,1i.2Q.1a)}G f},67:E(eU){H f=J,Q=f.Q,5o,3v=K(\'<1p 1e="4a:4N;">\',Q)[0];3v.id=\'pK\'+(eX++)+\'g9\';if(!f.4k){5o=3v.5V(1f);5o.id=\'pt\'+(eX++)+\'g9\'}if(5o){f.4H().2g(1l).4f(5o).7S(5o)}f.4f(3v).aG(3v);G{2h:eU?\'#\'+3v.id:3v,2F:5o?(eU?\'#\'+5o.id:5o):1z}},60:E(2i){H f=J,Q=f.Q,2h=K(2i.2h,Q),2F=2i.2F?K(2i.2F,Q):1z;if(!2h||2h.1a<1){G f}f.4Q(2h[0]);2h.1t();if(2F&&2F.1a>0){f.7S(2F[0]);2F.1t()}1d{f.2g(1f)}G f},ps:E(){7N.aS(\'--------------------\');7N.aS(J.1W.29==3?J.1W.4d:J.1W,J.2c);7N.aS(J.2t.29==3?J.2t.4d:J.2t,J.2o)}});E cd(3j){if(!3j.51){G 3j.ba===5S?3j:fX(3j)}G 2k 5S(3j)}K.pa=5S;K.R=cd;K.p7=8R;K.p8=8J;K.pg=7G;K.pp=8I;E 6q(Q,15,L){3r{Q.kj(15,1l,L)}3q(e){}}E g4(Q,15){H L=\'\';3r{L=Q.pq(15)}3q(e){}if(8o L!==\'c4\'){L=\'\'}G L}E bp(Q){H 38=6N(Q);G 52?Q.3t:38.gR()}E gS(Q){H 5K=bp(Q),1x;3r{if(5K.pr>0){1x=5K.pm(0)}1d{1x=5K.hk()}}3q(e){}if(52&&(!1x||(!1x.2R&&1x.bb().4T!==Q))){G 1z}G 1x}E e4(25){H 6x={},1w,v;1y(25,E(15,L){1w=15.2s(\',\');1J(H i=0,1D=1w.1a;i<1D;i++){v=1w[i];6x[v]=L}});G 6x}E b8(1b,25){G eS(1b,25,\'*\')||eS(1b,25)}E eS(1b,25,5W){5W=5W||1b.O;if(1b.1m!==1){G 1l}H 6x=e4(25);if(!6x[5W]){G 1l}H 1w=6x[5W].2s(\',\');1J(H i=0,1D=1w.1a;i<1D;i++){H 15=1w[i];if(15===\'*\'){G 1f}H 1G=/^(\\.?)([^=]+)(?:=([^=]*))?$/.2y(15);H 5b=1G[1]?\'1s\':\'1V\';15=1G[2];H L=1G[3]||\'\';if(L===\'\'&&1b[5b](15)!==\'\'){G 1f}if(L!==\'\'&&1b[5b](15)===L){G 1f}}G 1l}E ce(1b,25){if(1b.1m!=1){G}eM(1b,25,\'*\');eM(1b,25)}E eM(1b,25,5W){5W=5W||1b.O;if(1b.1m!==1){G}H 6x=e4(25);if(!6x[5W]){G}H 1w=6x[5W].2s(\',\'),ds=1l;1J(H i=0,1D=1w.1a;i<1D;i++){H 15=1w[i];if(15===\'*\'){ds=1f;3d}H 1G=/^(\\.?)([^=]+)(?:=([^=]*))?$/.2y(15);15=1G[2];if(1G[1]){15=8F(15);if(1b[0].1e[15]){1b[0].1e[15]=\'\'}}1d{1b.4o(15)}}if(ds){1b.1t(1f)}}E bi(1b){H 9W=1b;22(9W.3M()){9W=9W.3M()}G 9W}E 8f(1b){if(1b.1m!=1||1b.9U()){G 1l}G 1b.N().1g(/<[^>]+>/g,\'\')===\'\'}E h7(a,b){a=a.8S(1f);H hf=bi(a),98=a,bc=1l;22(b){22(98){if(98.O===b.O){gT(98,b.1V(),b.1s());bc=1f}98=98.3M()}if(!bc){hf.2r(b.8S(1l))}bc=1l;b=b.3M()}G a}E gO(1b,2X){2X=2X.8S(1f);if(1b.1m==3){bi(2X).2r(1b.8S(1l));1b.cc(2X);G 2X}H dD=1b,1i;22((1i=1b.3M())&&1i.2O().1a==1){1b=1i}1i=1b.3M();H 3a=1b.Q.g1();22(1i){3a.3z(1i[0]);1i=1i.2S()}2X=h7(dD,2X);if(3a.3L){bi(2X).2r(3a)}dD.cc(2X);G 2X}E gT(1b,2I,7L){1y(2I,E(15,L){if(15!==\'1e\'){1b.1V(15,L)}});1y(7L,E(15,L){1b.1s(15,L)})}E ht(1b){22(1b&&1b.O!=\'1n\'){if(bj[1b.O]||1b.O==\'M\'&&1b.6I(\'ke-2w\')){G 1f}1b=1b.1q()}G 1l}E 8U(R){J.2A(R)}4D(8U,{2A:E(R){H f=J,Q=R.Q;f.Q=Q;f.38=6N(Q);f.5K=bp(Q);f.R=R},3t:E(gV){H f=J,Q=f.Q,1x=gS(Q);f.5K=bp(Q);if(1x){f.R=cd(1x);if(K(f.R.1W).O==\'N\'){f.R.49(Q.1n).2g(1l)}G f}if(gV){f.R.49(Q.1n).2g(1l)}G f},1o:E(bq){bq=2a(bq,1f);H f=J,5K=f.5K,R=f.R.4H().a0(),1L=R.1W,2q=R.2c,ec=R.2t,eo=R.2o,Q=6P(1L),38=f.38,1x,dE=1l;if(bq&&1L.29==1&&R.4k){if(52){H 36=K(\'<1p>&3b;\',Q);R.4f(36[0]);1x=Q.1n.dm();3r{1x.dM(36[0])}3q(ex){}1x.2g(1l);1x.1o();36.1t();38.1Z();G f}if(6H){H 2O=1L.2Q;if(K(1L).bs()||2q>0&&K(2O[2q-1]).bs()||2O[2q]&&K(2O[2q]).bs()){R.4f(Q.pM(\'\\gl\'));dE=1f}}}if(52){3r{1x=R.3A(1f);1x.1o()}3q(e){}}1d{if(dE){R.2g(1l)}1x=R.3A(1f);5K.pP();5K.pF(1x);if(Q!==1M){H 1C=K(1x.2t).1C();38.fx(1C.x,1C.y)}}38.1Z();G f},88:E(L){H f=J,Q=f.Q,R=f.R,2X;2X=K(L,Q);if(R.4k){R.a0();R.4f(2X[0]).49(2X[0]);G f}if(2X.7P()){H bo=2X.8S(1f),1i=bo;22(1i.3M()){1i=1i.3M()}1i.2r(R.bn());R.4f(bo[0]).4P(bo[0]);G f}R.gm();H 2i=R.67(),4C=R.4V(),2D=1l;K(4C).gi(E(T){if(!2D&&T==2i.2h){2D=1f;G}if(2D){if(T==2i.2F){G 1l}H 1b=K(T);if(ht(1b)){G}if(1b.1m==3&&4b(T.4d).1a>0){H 1q;22((1q=1b.1q())&&1q.9I()&&1q.2O().1a==1){1b=1q}gO(1b,2X)}}});R.60(2i);G f},2s:E(2D,25){H R=J.R,Q=R.Q;H 7K=R.4H().2g(2D);H T=7K.1W,1C=7K.2c,1q=T.29==3?T.1U:T,dJ=1l,1b;22(1q&&1q.1U){1b=K(1q);if(25){if(!1b.9I()){3d}if(!b8(1b,25)){3d}}1d{if(aD[1b.O]){3d}}dJ=1f;1q=1q.1U}if(dJ){H 36=Q.4e(\'1p\');R.4H().2g(!2D).4f(36);if(2D){7K.4Q(1q.3L).3i(T,1C)}1d{7K.3V(T,1C).7B(1q.6b)}H 3a=7K.bn(),3M=3a.3L,83=3a.6b;if(2D){7K.4f(3a);R.aG(83).7S(36)}1d{1q.3z(3a);R.4Q(36).7S(3M)}H fp=36.1U;if(fp==R.2t){H 2H=K(36).2H(),2S=K(36).2S();if(2H&&2S&&2H.1m==3&&2S.1m==3){R.3i(2H[0],2H[0].4d.1a)}1d if(!2D){R.3i(R.2t,R.2o-1)}}fp.7g(36)}G J},1t:E(25){H f=J,Q=f.Q,R=f.R;R.gm();if(R.2c===0){H 7t=K(R.1W),1q;22((1q=7t.1q())&&1q.9I()&&1q.2O().1a==1){7t=1q}R.3V(7t[0],0);7t=K(R.1W);if(7t.7P()){ce(7t,25)}H bk=7t.1q();if(bk&&bk.7P()){ce(bk,25)}}H 1L,2q;if(R.4k){f.2s(1f,25);1L=R.1W;2q=R.2c;if(2q>0){H bl=K(1L.2Q[2q-1]);if(bl&&8f(bl)){bl.1t();R.3V(1L,2q-1)}}H bm=K(1L.2Q[2q]);if(bm&&8f(bm)){bm.1t()}if(8f(1L)){R.au(1L);1L.1t()}R.2g(1f);G f}f.2s(1f,25);f.2s(1l,25);H bO=Q.4e(\'1p\'),cf=Q.4e(\'1p\');R.4H().2g(1l).4f(cf);R.4H().2g(1f).4f(bO);H 4q=[],bN=1l;K(R.4V()).gi(E(T){if(!bN&&T==bO){bN=1f;G}if(T==cf){G 1l}if(bN){4q.1N(T)}});K(bO).1t();K(cf).1t();1L=R.1W;2q=R.2c;H ec=R.2t,eo=R.2o;if(2q>0){H au=K(1L.2Q[2q-1]);if(au&&8f(au)){au.1t();R.3V(1L,2q-1);if(1L==ec){R.3i(ec,eo-1)}}H cg=K(1L.2Q[2q]);if(cg&&8f(cg)){cg.1t();if(1L==ec){R.3i(ec,eo-1)}}}H ch=K(ec.2Q[R.2o]);if(ch&&8f(ch)){ch.1t()}H 2i=R.67(1f);1y(4q,E(i,T){ce(K(T),25)});R.60(2i);G f},4U:E(25){H R=J.R;H ec=R.2t,eo=R.2o,T=(ec.29==3||eo===0)?ec:ec.2Q[eo-1];E 6i(T){H 1i=T,1q=T;22(1q){if(b8(K(1q),25)){G K(1q)}1q=1q.1U}22(1i&&(1i=1i.6b)){if(b8(K(1i),25)){G K(1i)}}G 1z}H gb=6i(T);if(gb){G gb}if(T.29==1||(ec.29==3&&eo===0)){H 2H=K(T).2H();if(2H){G 6i(2H)}}G 1z},4V:E(2x){H R=J.R,1L=R.1W,2q=R.2c,ec=R.2t,eo=R.2o,3v=(1L.29==3||2q===0)?1L:1L.2Q[2q-1],5o=(ec.29==3||eo===0)?ec:ec.2Q[eo-1];E 6i(T){22(T){if(T.29==1){if(T.2x.2W()===2x){G T}}T=T.1U}G 1z}H 2h=6i(3v),2F=6i(5o);if(2h&&2F&&2h===2F){G K(2h)}G 1z},kI:E(15){H f=J,Q=f.Q,4n=1l;3r{4n=Q.qB(15)}3q(e){}G 4n},L:E(15){H f=J,Q=f.Q,R=f.R;E lc(L){G L.2W()}15=lc(15);H L=\'\',1b;if(15===\'fm\'||15===\'5d\'){L=g4(Q,\'5d\');L=L.1g(/[\'"]/g,\'\');G lc(L)}if(15===\'4E\'){L=g4(Q,15);if(L===\'\'){1b=f.4U({\'h1,h2,h3,h4,h5,h6,p,M,3S,hb\':\'*\'});if(1b){L=1b.O}}if(L===\'qW\'){L=\'p\'}G lc(L)}if(15===\'69\'){1b=f.4U({\'*\':\'.23-3H\'});if(1b){L=1b.1s(\'23-3H\')}G lc(L)}if(15===\'8u\'){1b=f.4U({\'*\':\'.1K\'});if(1b){L=1b.1s(\'1K\')}L=aj(L);if(L===\'\'){L=\'8H\'}G lc(L)}if(15===\'8n\'){1b=f.4U({\'*\':\'.2v-1K\'});if(1b){L=1b.1s(\'2v-1K\')}L=aj(L);if(L===\'\'){L=\'8H\'}G lc(L)}G L},8M:E(2X,25){H f=J;if(f.4U(25)){f.1t(25)}1d{f.88(2X)}G f.1o()},6p:E(){G J.8M(\'<9b>\',{1p:\'.23-6Q=6p\',9b:\'*\',b:\'*\'})},6Z:E(){G J.8M(\'\',{1p:\'.23-1e=6Z\',em:\'*\',i:\'*\'})},77:E(){G J.8M(\'\',{1p:\'.1A-7m=77\',u:\'*\'})},9o:E(){G J.8M(\'\',{1p:\'.1A-7m=5s-pT\',s:\'*\'})},8u:E(L){G J.88(\'<1p 1e="1K:\'+L+\';">\').1o()},8n:E(L){G J.88(\'<1p 1e="2v-1K:\'+L+\';">\').1o()},69:E(L){G J.88(\'<1p 1e="23-3H:\'+L+\';">\').1o()},5d:E(L){G J.fm(L)},fm:E(L){G J.88(\'<1p 1e="23-6w:\'+L+\';">\').1o()},cz:E(){H 25={\'*\':\'.23-6Q,.23-1e,.1A-7m,.1K,.2v-1K,.23-3H,.23-6w,.1A-7s\'},ho=f8;1y(ho,E(15,L){25[15]=\'*\'});J.1t(25);G J.1o()},aW:E(L,b5){H f=J,R=f.R;if(L===\'\'){G f}E gM(R,L){L=\'<2n id="91" 19="0" 17="0" 1e="4a:4N;" />\'+L;H 1x=R.3A();if(1x.2R){1x.2R(0).pZ=L}1d{1x.qb(L)}H 97=R.Q.fe(\'91\');97.1U.7g(97);H 95=fX(1x);R.3i(95.2t,95.2o);R.2g(1l);f.1o(1l)}E 5l(R,L){H Q=R.Q,3a=Q.g1();K(\'@\'+L,Q).2e(E(){3a.3z(J)});R.gQ();R.4f(3a);R.2g(1l);f.1o(1l)}if(52&&b5){3r{gM(R,L)}3q(e){5l(R,L)}G f}5l(R,L);G f},hr:E(){G J.aW(\'
\')},7d:E(){J.38.7d();G J},jo:E(1v,1Y,19,17,1F,2j){1Y=2a(1Y,\'\');1F=2a(1F,0);H N=\'<2n 1S="\'+3Z(1v)+\'" 1O-ke-1S="\'+3Z(1v)+\'" \';if(19){N+=\'19="\'+3Z(19)+\'" \'}if(17){N+=\'17="\'+3Z(17)+\'" \'}if(1Y){N+=\'1Y="\'+3Z(1Y)+\'" \'}if(2j){N+=\'2j="\'+3Z(2j)+\'" \'}N+=\'cV="\'+3Z(1Y)+\'" \';N+=\'/>\';G J.aW(N)},dZ:E(1v,1m){H f=J,Q=f.Q,R=f.R;f.1o();H a=f.4U({a:\'*\'});if(a&&!R.5h()){R.4P(a.3A());f.1o()}H N=\'\'+3Z(1v)+\'\';G f.aW(N)}if(R.5h()){H T=K(R.1W.2Q[R.2c]);N+=\'>\';T.8b(K(N,Q));T.2S().2r(T);R.4P(T[0]);G f.1o()}E fL(T,1v,1m){K(T).1V(\'4v\',1v).1V(\'1O-ke-1S\',1v);if(1m){K(T).1V(\'2C\',1m)}1d{K(T).4o(\'2C\')}}H 1L=R.1W,2q=R.2c,ec=R.2t,eo=R.2o;if(1L.29==1&&1L===ec&&2q+1===eo){H 1i=1L.2Q[2q];if(1i.51.2W()==\'a\'){fL(1i,1v,1m);G f}}6q(Q,\'dZ\',\'gK\');K(\'a[4v="gK"]\',Q).2e(E(){fL(J,1v,1m)});G f},82:E(){H f=J,Q=f.Q,R=f.R;f.1o();if(R.4k){H a=f.4U({a:\'*\'});if(a){R.4P(a.3A());f.1o()}6q(Q,\'82\',1z);if(6H&&K(R.1W).O===\'2n\'){H 1q=K(R.1W).1q();if(1q.O===\'a\'){1q.1t(1f)}}}1d{6q(Q,\'82\',1z)}G f}});1y((\'4E,70,81,8e,8g,8h,8t,\'+\'8p,7s,cy,as,at\').2s(\',\'),E(i,O){8U.4M[O]=E(L){H f=J;f.1o();6q(f.Q,O,L);if(52&&5w(O,\'81,8e,8g,8h\'.2s(\',\'))>=0){f.3t()}if(!52||5w(O,\'4E,70,8t,8p\'.2s(\',\'))>=0){f.3t()}G f}});1y(\'aA,7C,71\'.2s(\',\'),E(i,O){8U.4M[O]=E(){H f=J;if(!f.Q.qj(O)){jf\'qk qi\'}f.1o();6q(f.Q,O,1z);G f}});E f7(3j){if(3j.51){H Q=6P(3j);3j=cd(Q).49(Q.1n).2g(1l)}G 2k 8U(3j)}K.qe=8U;K.1h=f7;E b1(W){H 5x=W.5x,9h=W.9h,6L=W.6L||5x,99=W.99,ca=W.ca===1Q?1f:W.ca;H cb=[1M];if(ca){K(\'1H\').2e(E(){H 1S=9r(J.1S||\'\',\'7h\');if(/^qn?:\\/\\//.2f(1S)){G}H Q;3r{Q=cn(J)}3q(e){}if(Q){H 1C=K(J).1C();K(Q).1O(\'1C-x\',1C.x);K(Q).1O(\'1C-y\',1C.y);cb.1N(Q)}})}6L.3U(E(e){if(e.2m!==0&&e.2m!==1){G}e.5L();H f=6L.3A(),x=3f(5x.1s(\'3W\')),y=3f(5x.1s(\'4L\')),19=5x.19(),17=5x.17(),86=e.86,9q=e.9q;if(99){99()}E fi(e){e.3X();H g3=K(6P(e.2C));H 4J=6h((g3.1O(\'1C-x\')||0)+e.86-86);H 4A=6h((g3.1O(\'1C-y\')||0)+e.9q-9q);9h.1P(6L,x,y,19,17,4J,4A)}E fk(e){e.3X()}E fj(e){e.3X();K(cb).2u(\'af\',fi).2u(\'co\',fj).2u(\'kS\',fk);if(f.kR){f.kR()}}K(cb).af(fi).co(fj).3e(\'kS\',fk);if(f.l6){f.l6()}})}E 6o(W){J.2A(W)}4D(6o,{2A:E(W){H f=J;f.O=W.O||\'\';f.Q=W.Q||1M;f.38=6N(f.Q);f.x=3n(W.x);f.y=3n(W.y);f.z=W.z;f.19=3n(W.19);f.17=3n(W.17);f.M=K(\'\');f.W=W;f.fc=W.l5;if(f.19){f.M.1s(\'19\',f.19)}if(f.17){f.M.1s(\'17\',f.17)}if(f.z){f.M.1s({7X:\'7h\',3W:f.x,4L:f.y,\'z-2N\':f.z})}if(f.z&&(f.x===1Q||f.y===1Q)){f.eJ(f.19,f.17)}if(W.2L){f.M.2Z(W.2L)}if(W.3Q){f.M.2Z(\'ke-eZ\')}if(W.1s){f.M.1s(W.1s)}if(W.1S){K(W.1S).cc(f.M)}1d{K(f.Q.1n).2r(f.M)}if(W.N){f.M.N(W.N)}if(W.eV){if(2E&&3F<7||6D){H 6R=7D();K(f.38).3e(\'9T\',E(e){H 1C=7D(),4J=1C.x-6R.x,4A=1C.y-6R.y;f.1C(3f(f.x)+4J,3f(f.y)+4A,1l)})}1d{f.M.1s(\'7X\',\'q0\')}}},1C:E(x,y,5G){H f=J;5G=2a(5G,1f);if(x!==1z){x=x<0?0:3n(x);f.M.1s(\'3W\',x);if(5G){f.x=x}}if(y!==1z){y=y<0?0:3n(y);f.M.1s(\'4L\',y);if(5G){f.y=y}}G f},eJ:E(19,17){H f=J,w=3f(19)||0,h=3f(17)||0,6R=7D();if(f.fc){H 1b=K(f.fc),1C=1b.1C(),4J=6h(1b[0].9g/2-w/2),4A=6h(1b[0].7A/2-h/2);x=4J<0?1C.x:1C.x+4J;y=4A<0?1C.y:1C.y+4A}1d{H 5J=4i(f.Q);x=6h(6R.x+(5J.9g-w)/2);y=6h(6R.y+(5J.7A-h)/2)}if(!(2E&&3F<7||6D)){x-=6R.x;y-=6R.y}G f.1C(x,y)},1t:E(){H f=J;if(2E&&3F<7||6D){K(f.38).2u(\'9T\')}f.M.1t();1y(f,E(i){f[i]=1z});G J},7c:E(){J.M.7c();G J},5I:E(){J.M.5I();G J},jm:E(W){H f=J;W=W||{};W.5x=f.M;W.9h=E(x,y,19,17,4J,4A){if((x=x+4J)<0){x=0}if((y=y+4A)<0){y=0}f.1C(x,y)};b1(W);G f}});E bS(W){G 2k 6o(W)}K.qv=6o;K.qU=bS;E cn(1H){1H=5B(1H);G 1H.qV||1H.d3.1M}H N,ci=\'\';if((N=1M.7f(\'N\'))){ci=N[0].gA}E la(3T,6s,5t,6A){H 1w=[(ci===\'\'?\'\':\'\'),\'<4p><9C kM="kE-8" /><1Y>\',\'<1e>\',\'N {4I:0;30:0;}\',\'1n {4I:0;30:lb;}\',\'1n, 56 {}\',\'1n, p, M {kV-88: 3d-kV;}\',\'p {4I:lb 0;}\',\'1c {1F-2g:2g;}\',\'2n {1F:0;}\',\'4x {4a:4N;}\',\'1c.ke-7w 56 {1F:79 qT #cj;}\',\'2n.ke-8j {\',\' 1F:79 cu #cj;\',\' 2v-cC:1v(\'+3T+\'5F/8j.92);\',\' 2v-7X:3C 3C;\',\' 2v-9k:no-9k;\',\' 19:7H;\',\' 17:7H;\',\'}\',\'2n.ke-93 {\',\' 1F:79 cu #cj;\',\' 2v-cC:1v(\'+3T+\'5F/93.92);\',\' 2v-7X:3C 3C;\',\' 2v-9k:no-9k;\',\' 19:7H;\',\' 17:7H;\',\'}\',\'2n.ke-aq {\',\' 1F:79 cu #cj;\',\' 2v-cC:1v(\'+3T+\'5F/aq.92);\',\' 2v-7X:3C 3C;\',\' 2v-9k:no-9k;\',\' 19:7H;\',\' 17:7H;\',\'}\',\'2n.ke-4Z {\',\' 1F:79 qL #qN;\',\' 19:gj;\',\' 17:gj;\',\'}\',\'.ke-2w, .ke-4x, .ke-4a-4N {\',\' 4a:4N;\',\' 23-3H:0;\',\' 19:0;\',\' 17:0;\',\'}\',\'\'];if(!85(5t)){5t=[5t]}1y(5t,E(i,5E){if(5E){1w.1N(\'<3E 4v="\'+5E+\'" do="dn" />\')}});if(6A){1w.1N(\'<1e>\'+6A+\'\')}1w.1N(\'<1n \'+(6s?\'1j="\'+6s+\'"\':\'\')+\'>\');G 1w.3Y(\'\\n\')}E 8O(1b,L){if(1b.gC()){if(L===1Q){H N=1b.L();N=N.1g(/(<(?:p|p\\s[^>]*)>) *(<\\/p>)/ig,\'\');G N}G 1b.L(L)}G 1b.N(L)}E 9f(W){J.2A(W)}4D(9f,6o,{2A:E(W){H f=J;9f.1q.2A.1P(f,W);f.2P=K(W.2P);f.M.2Z(\'ke-1u\');f.2K=2a(W.2K,1f);f.6r=W.6r;f.53=W.53;f.6e=W.6e;H 3T=2a(W.3T,\'\'),6s=W.6s,5t=W.5t,6A=W.6A,9Q=9N.lj!=\'pR:\'&&9N.4S.1g(/:\\d+/,\'\')!==1M.9i,ll=(\'1M.9z();\'+(9Q?\'1M.9i="\'+1M.9i+\'";\':\'\')+\'1M.7q();\'),kX=2E?\' 1S="jj:oU(E(){\'+d8(ll)+\'}())"\':\'\';f.1H=K(\'<1H 1j="ke-1u-1H" kU="1f" cW="0"\'+kX+\'>\').1s(\'19\',\'63%\');f.2U=K(\'<2U 1j="ke-1u-2U" kU="1f">\').1s(\'19\',\'63%\');f.8T=ou(6Y(W.8T,10))?f.2P.1V(\'c9\'):6Y(W.8T,10);f.1H.1V(\'c9\',f.8T);f.2U.1V(\'c9\',f.8T);if(f.19){f.l0(f.19)}if(f.17){f.fE(f.17)}if(f.2K){f.2U.5I()}1d{f.1H.5I()}E a9(){H Q=cn(f.1H);Q.9z();if(9Q){Q.9i=1M.9i}Q.dw(la(3T,6s,5t,6A));Q.7q();f.38=f.1H[0].d3;f.Q=Q;H 1h=f7(Q);f.4Y(E(e){1h.3t()});if(6H){K(Q).2d(E(e){if(K(e.2C).O===\'2n\'){1h.3t(1f);1h.R.4P(e.2C);1h.1o()}})}if(2E){f.cq=E(){H 95=1h.R.4H();95.a0();if(95.5h()){f.5R()}};K(1M).3U(f.cq);K(Q).a4(E(e){if(e.2Y==8){1h.3t();H 1x=1h.R;if(1x.5h()){1x.2g(1f);K(1x.1W.2Q[1x.2c]).1t();e.3X()}}})}f.1h=1h;f.N(8O(f.2P));if(2E){Q.1n.55=1f;Q.1n.l8=1f;Q.1n.cp(\'55\')}1d{Q.2K=\'on\'}if(W.44){W.44.1P(f)}}if(9Q){f.1H.3e(\'aH\',E(e){f.1H.2u(\'aH\');if(2E){a9()}1d{6v(a9,0)}})}f.M.2r(f.1H);f.M.2r(f.2U);f.2P.5I();!9Q&&a9()},l0:E(L){H f=J;L=3n(L);f.19=L;f.M.1s(\'19\',L);G f},fE:E(L){H f=J;L=3n(L);f.17=L;f.M.1s(\'17\',L);f.1H.1s(\'17\',L);if((2E&&3F<8)||6D){L=3n(3f(L)-2)}f.2U.1s(\'17\',L);G f},1t:E(){H f=J,Q=f.Q;K(Q.1n).2u();K(Q).2u();K(f.38).2u();if(f.cq){K(1M).2u(\'3U\',f.cq)}8O(f.2P,f.N());f.2P.7c();f.1H.2u();f.2U.2u();9f.1q.1t.1P(f)},N:E(L,kZ){H f=J,Q=f.Q;if(f.2K){H 1n=Q.1n;if(L===1Q){if(kZ){L=\'\'+1n.1U.3u+\'\'}1d{L=1n.3u}if(f.6r){L=f.6r(L)}if(6J&&L==\'
\'){L=\'\'}G L}if(f.53){L=f.53(L)}if(2E&&3F>=9){L=L.1g(/(<.*?6B=")6B(".*>)/ig,\'$1$2\')}K(1n).N(L);if(f.6e){f.6e()}G f}if(L===1Q){G f.2U.L()}f.2U.L(L);G f},fU:E(4n){H f=J,L;if(4n===1Q?!f.2K:4n){if(!f.2K){L=f.N();f.2K=1f;f.2U.5I();f.N(L);H 1H=f.1H;H 17=3f(f.17);1H.17(17-2);1H.7c();6v(E(){1H.17(17)},0)}}1d{if(f.2K){L=f.N();f.2K=1l;f.N(L);f.1H.5I();f.2U.7c()}}G f.1Z()},1Z:E(){H f=J;f.2K?f.38.1Z():f.2U[0].1Z();G f},5R:E(){H f=J;if(2E){H 1B=K(\'<1B 1m="1A" 1e="pw:3W;19:0;17:0;30:0;4I:0;1F:0;" 26="" />\',f.M);f.M.2r(1B);1B[0].1Z();1B.1t()}1d{f.2K?f.38.5R():f.2U[0].5R()}G f},4Y:E(fn){H f=J,Q=f.Q,1n=Q.1n;K(Q).ad(E(e){if(!e.6T&&!e.9c&&cr[e.2Y]){fn(e)}});K(Q).co(fn).6M(fn);K(f.38).5R(fn);E dW(e){6v(E(){fn(e)},1)}K(1n).3e(\'71\',dW);K(1n).3e(\'aA\',dW);G f}});E fa(W){G 2k 9f(W)}K.pJ=9f;K.1u=fa;K.cX=cn;E dp(O,fn){H f=J,1b=f.3A(O);if(1b){if(1b.6I(\'ke-55\')){G}fn(1b)}}E aV(W){J.2A(W)}4D(aV,6o,{2A:E(W){H f=J;aV.1q.2A.1P(f,W);f.al=2a(W.al,1l);f.lh=3o(2a(W.73,[]));f.ck={};f.M.2Z(\'ke-2M\').3e(\'6M,3U,af\',E(e){e.3X()}).1V(\'5n\',\'on\');E 6i(2C){H 1b=K(2C);if(1b.6I(\'ke-b4\')){G 1b}if(1b.6I(\'ke-2M-4W\')){G 1b.1q()}}E dh(e,5b){H 1b=6i(e.2C);if(1b){if(1b.6I(\'ke-55\')){G}if(1b.6I(\'ke-7b\')){G}1b[5b](\'ke-on\')}}f.M.ao(E(e){dh(e,\'2Z\')}).ax(E(e){dh(e,\'41\')}).2d(E(e){H 1b=6i(e.2C);if(1b){if(1b.6I(\'ke-55\')){G}f.W.2d.1P(J,e,1b.1V(\'1O-O\'))}})},3A:E(O){if(J.ck[O]){G J.ck[O]}G(J.ck[O]=K(\'1p.ke-4W-\'+O,J.M).1q())},1o:E(O){dp.1P(J,O,E(1b){1b.2Z(\'ke-7b\')});G f},fB:E(O){dp.1P(J,O,E(1b){1b.41(\'ke-7b\').41(\'ke-on\')});G f},f0:E(O){H f=J,1b=O.3A?O:f.3A(O);if(1b){1b.41(\'ke-55\');1b.9n(1)}G f},et:E(O){H f=J,1b=O.3A?O:f.3A(O);if(1b){1b.41(\'ke-7b\').2Z(\'ke-55\');1b.9n(0.5)}G f},am:E(4n,73){H f=J,25=f.lh,2R;if(73){25=3o(73)}if(4n===1Q?!f.al:4n){K(\'1p.ke-b4\',f.M).2e(E(){H 1b=K(J),O=1b[0].9G(\'1O-O\',2);if(!25[O]){f.et(1b)}});f.al=1f}1d{K(\'1p.ke-b4\',f.M).2e(E(){H 1b=K(J),O=1b[0].9G(\'1O-O\',2);if(!25[O]){f.f0(1b)}});f.al=1l}G f}});E fl(W){G 2k aV(W)}K.po=aV;K.2M=fl;E 9j(W){J.2A(W)}4D(9j,6o,{2A:E(W){H f=J;W.z=W.z||bU;9j.1q.2A.1P(f,W);f.b3=2a(W.b3,1f);f.M.2Z(\'ke-1X\').3e(\'2d,3U\',E(e){e.5L()}).1V(\'5n\',\'on\')},8N:E(2R){H f=J;if(2R.1Y===\'-\'){f.M.2r(K(\'\'));G}H 9l=K(\'\'),du=K(\'\'),cl=K(\'\'),17=3n(2R.17),8y=2a(2R.8y,\'\');f.M.2r(9l);if(17){9l.1s(\'17\',17);cl.1s(\'5s-17\',17)}H 6j;if(f.b3){6j=K(\'\');if(17){6j.1s(\'17\',17)}}9l.ao(E(e){K(J).2Z(\'ke-1X-2R-on\');if(6j){6j.2Z(\'ke-1X-2R-3C-on\')}}).ax(E(e){K(J).41(\'ke-1X-2R-on\');if(6j){6j.41(\'ke-1X-2R-3C-on\')}}).2d(E(e){2R.2d.1P(K(J));e.5L()}).2r(du);if(6j){9l.2r(6j)}9l.2r(cl);if(2R.6B){8y=\'ke-4W-6B\'}if(8y!==\'\'){du.N(\'<1p 1j="ke-4F-4j ke-2M-4W ke-2M-4W-1v \'+8y+\'">\')}cl.N(2R.1Y);G f},1t:E(){H f=J;if(f.W.5Y){f.W.5Y.1P(f)}K(\'.ke-1X-2R\',f.M[0]).2u();9j.1q.1t.1P(f);G f}});E b2(W){G 2k 9j(W)}K.pj=9j;K.1X=b2;E 9s(W){J.2A(W)}4D(9s,6o,{2A:E(W){H f=J;W.z=W.z||bU;9s.1q.2A.1P(f,W);H 7l=W.7l||[[\'#jN\',\'#jO\',\'#jP\',\'#jQ\',\'#jM\',\'#jL\'],[\'#jH\',\'#jI\',\'#jS\',\'#jZ\',\'#k0\',\'#k1\'],[\'#jY\',\'#jX\',\'#jT\',\'#jU\',\'#jV\',\'#jW\'],[\'#ep\',\'#jG\',\'#jF\',\'#jp\',\'#jq\',\'#8P\']];f.9p=(W.9p||\'\').2W();f.er=[];f.M.2Z(\'ke-5j\').3e(\'2d,3U\',E(e){e.5L()}).1V(\'5n\',\'on\');H 1c=f.Q.4e(\'1c\');f.M.2r(1c);1c.2V=\'ke-5j-1c\';1c.cZ=0;1c.d0=0;1c.1F=0;H 1R=1c.gd(0),1r=1R.8C(0);1r.3G=7l[0].1a;f.es(1r,\'\',\'ke-5j-1r-4L\');1J(H i=0;i<7l.1a;i++){1R=1c.gd(i+1);1J(H j=0;j<7l[i].1a;j++){1r=1R.8C(j);f.es(1r,7l[i][j],\'ke-5j-1r\')}}},es:E(1r,1K,2L){H f=J;1r=K(1r).2Z(2L);if(f.9p===1K.2W()){1r.2Z(\'ke-5j-1r-7b\')}1r.1V(\'1Y\',1K||f.W.7R);1r.ao(E(e){K(J).2Z(\'ke-5j-1r-on\')});1r.ax(E(e){K(J).41(\'ke-5j-1r-on\')});1r.2d(E(e){e.9A();f.W.2d.1P(K(J),1K)});if(1K){1r.2r(K(\'\').1s(\'2v-1K\',1K))}1d{1r.N(f.W.7R)}K(1r).1V(\'5n\',\'on\');f.er.1N(1r)},1t:E(){H f=J;1y(f.er,E(){J.2u()});9s.1q.1t.1P(f);G f}});E dF(W){G 2k 9s(W)}K.pN=9s;K.5j=dF;E bW(W){J.2A(W)}4D(bW,{2A:E(W){H f=J,2m=K(W.2m),ei=W.ei||\'c8\',1v=W.1v||\'\',1Y=2m.L(),cm=W.cm||{},2L=2m[0].2V||\'\',2C=W.2C||\'py\'+2k jr().jt();W.eh=W.eh||E(2z){4u(2z)};H ej=[];1J(H k in cm){ej.1N(\'<1B 1m="4X" O="\'+k+\'" 26="\'+cm[k]+\'" />\')}H N=[\'\',(W.2C?\'\':\'<1H O="\'+2C+\'" 1e="4a:4N;">\'),(W.3K?\'\':\'<3K 1j="ke-aM-bV ke-3K" 5b="pz" pA="pE/3K-1O" 2C="\'+2C+\'" pC="\'+1v+\'">\'),\'<1p 1j="ke-2m-5F">\',ej.3Y(\'\'),\'<1B 1m="2m" 1j="ke-2m-5F ke-2m" 26="\'+1Y+\'" />\',\'\',\'<1B 1m="c8" 1j="ke-aM-c8" O="\'+ei+\'" c9="-1" />\',(W.3K?\'\':\'\'),\'\'].3Y(\'\');H M=K(N,2m.Q);2m.5I();2m.7v(M);f.M=M;f.2m=2m;f.1H=W.2C?K(\'1H[O="\'+2C+\'"]\'):K(\'1H\',M);f.3K=W.3K?K(W.3K):K(\'3K\',M);f.aX=K(\'.ke-aM-c8\',M);H 19=W.19||K(\'.ke-2m-5F\',M).19();K(\'.ke-aM-bV\',M).19(19);f.W=W},d5:E(){H f=J,1H=f.1H;1H.3e(\'aH\',E(){1H.2u();H aE=1M.4e(\'3K\');f.aX.7v(aE);K(aE).2r(f.aX);aE.jx();K(aE).1t(1f);H Q=K.cX(1H),3S=Q.7f(\'3S\')[0],2z=\'\',1O;if(3S){2z=3S.3u}1d{2z=Q.1n.3u}2z=8Q(2z);1H[0].1S=\'jj:1l\';3r{1O=K.c7(2z)}3q(e){f.W.eh.1P(f,\'\'+Q.1n.1U.3u+\'\')}if(1O){f.W.oG.1P(f,1O)}});f.3K[0].d5();G f},1t:E(){H f=J;if(f.aX){f.aX.2u()}f.1H.1t();f.M.1t();f.2m.7c();G f}});E jl(W){G 2k bW(W)}K.oE=bW;K.ow=jl;E ju(9d){9d=9d||{};H O=9d.O||\'\',1p=K(\'<1p 1j="ke-2m-5F ke-2m-bX" 1Y="\'+O+\'">\'),7j=K(\'<1B 1j="ke-2m-5F ke-2m" 1m="2m" 26="\'+O+\'" />\');if(9d.2d){7j.2d(9d.2d)}1p.2r(7j);G 1p}E 8Y(W){J.2A(W)}4D(8Y,6o,{2A:E(W){H f=J;H 3Q=2a(W.3Q,1f);W.z=W.z||bU;W.3Q=1l;W.eV=2a(W.eV,1f);8Y.1q.2A.1P(f,W);H 1Y=W.1Y,1n=K(W.1n,f.Q),bQ=W.bQ,64=W.64,9u=W.9u,8E=W.8E,d7=2a(W.d7,1f);f.M.2Z(\'ke-21\').3e(\'2d,3U\',E(e){e.5L()});H bP=K(\'\').bT(f.M);if(2E&&3F<7){f.eP=K(\'<1H 1S="9m:kl" 1j="ke-21-eZ">\').bT(f.M)}1d if(3Q){K(\'\').bT(f.M)}H 5z=K(\'\');bP.2r(5z);5z.N(1Y);f.eN=K(\'<1p 1j="ke-21-4W-7q" 1Y="\'+8E.O+\'">\').2d(8E.2d);5z.2r(f.eN);f.jm({6L:5z,99:W.99});H 6n=K(\'\');bP.2r(6n);6n.2r(1n);H 7E=K(\'\');if(bQ||64||9u){bP.2r(7E)}1y([{7j:bQ,O:\'9V\'},{7j:64,O:\'8W\'},{7j:9u,O:\'no\'}],E(){if(J.7j){H 2m=ju(J.7j);2m.2Z(\'ke-21-\'+J.O);7E.2r(2m)}});if(f.17){6n.17(3f(f.17)-5z.17()-7E.17())}f.M.19(f.M.19());f.M.17(f.M.17());f.8Z=1z;if(d7){H 5J=4i(f.Q),jD=7n.9P(5J.jC,5J.9g),jE=7n.9P(5J.fY,5J.7A);f.8Z=bS({x:0,y:0,z:f.z-1,2L:\'ke-21-8Z\',19:jD,17:jE})}f.eJ(f.M.19(),f.M.17());f.7E=7E;f.6n=6n;f.5z=5z;f.eR=1l},ek:E(z){H f=J;f.8Z.M.1s(\'z-2N\',z)},oY:E(bR){bR=2a(bR,\'\');H f=J,1n=f.6n;f.7Y=K(\'\'+bR+\'\').19(1n.19()).17(1n.17()).1s(\'4L\',f.5z.17()+\'px\');1n.1s(\'7o\',\'4X\').8b(f.7Y);f.eR=1f;G f},oW:E(){J.7Y&&J.7Y.1t();J.6n.1s(\'7o\',\'kp\');J.eR=1l;G J},1t:E(){H f=J;if(f.W.5Y){f.W.5Y.1P(f)}f.8Z&&f.8Z.1t();f.eP&&f.eP.1t();f.eN.2u();K(\'1B\',f.M).2u();K(\'2m\',f.M).2u();f.7E.2u();f.6n.2u();f.5z.2u();K(\'1H\',f.M).2e(E(){K(J).1t()});8Y.1q.1t.1P(f);G f}});E ga(W){G 2k 8Y(W)}K.oT=8Y;K.21=ga;E kD(W){H f=bS(W),1t=f.1t,bY=W.bY,M=f.M,bZ=[];M.2Z(\'ke-6m\').3e(\'6M,3U,af\',E(e){e.3X()});H 5u=K(\'<5u 1j="ke-6m-5u ke-oO">\');M.2r(5u);f.qQ=E(8c){H li=K(\'
  • \'+8c.1Y+\'
  • \');li.1O(\'8c\',8c);bZ.1N(li);5u.2r(li)};f.kB=0;f.1o=E(2N){f.kB=2N;1y(bZ,E(i,li){li.2u();if(i===2N){li.2Z(\'ke-6m-li-7b\');K(li.1O(\'8c\').kt).7c(\'\')}1d{li.41(\'ke-6m-li-7b\').41(\'ke-6m-li-on\').ao(E(){K(J).2Z(\'ke-6m-li-on\')}).ax(E(){K(J).41(\'ke-6m-li-on\')}).2d(E(){f.1o(i)});K(li.1O(\'8c\').kt).5I()}});if(bY){bY.1P(f,2N)}};f.1t=E(){1y(bZ,E(){J.1t()});5u.1t();1t.1P(f)};G f}K.6m=kD;E dR(1v,fn){H 4p=1M.7f(\'4p\')[0]||(6D?1M.1n:1M.9t),2w=1M.4e(\'2w\');4p.3z(2w);2w.1S=1v;2w.kM=\'kE-8\';2w.kF=2w.e3=E(){if(!J.c5||J.c5===\'68\'){if(fn){fn()}2w.kF=2w.e3=1z;4p.7g(2w)}}}E dg(1v){H 2N=1v.3B(\'?\');G 2N>0?1v.4r(0,2N):1v}E dc(1v){H 4p=1M.7f(\'4p\')[0]||(6D?1M.1n:1M.9t),3E=1M.4e(\'3E\'),ka=dg(9r(1v,\'7h\'));H df=K(\'3E[do="dn"]\',4p);1J(H i=0,1D=df.1a;i<1D;i++){if(dg(9r(df[i].4v,\'7h\'))===ka){G}}4p.3z(3E);3E.4v=1v;3E.do=\'dn\'}E km(1v,fn,5b,8l,c6){5b=5b||\'q3\';c6=c6||\'c7\';H 6a=3l.kd?2k 3l.kd():2k qa(\'kW.qw\');6a.9z(5b,1v,1f);6a.e3=E(){if(6a.c5==4&&6a.q1==ku){if(fn){H 1O=4b(6a.pV);if(c6==\'c7\'){1O=dU(1O)}fn(1O)}}};if(5b==\'pS\'){H e0=[];1y(8l,E(15,L){e0.1N(d8(15)+\'=\'+d8(L))});3r{6a.qp(\'qq-qr\',\'kn/x-kG-3K-qs\')}3q(e){}6a.ko(e0.3Y(\'&\'))}1d{6a.ko(1z)}}K.qm=dR;K.ql=dc;K.qf=km;H 6f={};E fR(O,fn){if(O===1Q){G 6f}if(!fn){G 6f[O]}6f[O]=fn}H 62={};E dL(15){H 1G,ns=\'jv\';if((1G=/^(\\w+)\\.(\\w+)$/.2y(15))){ns=1G[1];15=1G[2]}G{ns:ns,15:15}}E fQ(3j,3y){3y=3y===1Q?K.W.3y:3y;if(8o 3j===\'c4\'){if(!62[3y]){G\'no oi\'}H 1C=3j.1a-1;if(3j.4r(1C)===\'.\'){G 62[3y][3j.4r(0,1C)]}H 3g=dL(3j);G 62[3y][3g.ns][3g.15]}1y(3j,E(15,L){H 3g=dL(15);if(!62[3y]){62[3y]={}}if(!62[3y][3g.ns]){62[3y][3g.ns]={}}62[3y][3g.ns][3g.15]=L})}E k4(R,fn){if(R.4k){G}R=R.4H().aO();H 1L=R.1W,2q=R.2c;if(!6H&&!R.5h()){G}H 2n=K(1L.2Q[2q]);if(!2n||2n.O!=\'2n\'){G}if(fn(2n)){G 2n}}E ky(){H f=J,Q=f.1u.Q;K(Q).6M(E(e){if(f.1X){f.54()}if(!f.kk){e.3X();G}if(f.cs.1a===0){G}H c0=0,3I=[];1y(f.cs,E(){if(J.1Y==\'-\'){3I.1N(J);G}if(J.a1&&J.a1()){3I.1N(J);if(J.19&&J.19>c0){c0=J.19}}});22(3I.1a>0&&3I[0].1Y==\'-\'){3I.oz()}22(3I.1a>0&&3I[3I.1a-1].1Y==\'-\'){3I.6K()}H gp=1z;1y(3I,E(i){if(J.1Y==\'-\'&&gp.1Y==\'-\'){3J 3I[i]}gp=J});if(3I.1a>0){e.3X();H 1C=K(f.1u.1H).1C(),1X=b2({x:1C.x+e.c3,y:1C.y+e.ct,19:c0,1s:{7o:\'4X\'},3Q:f.3Q});1y(3I,E(){if(J.1Y){1X.8N(J)}});H 5J=4i(1X.Q),gc=1X.M.17();if(e.ct+gc>=5J.7A-63){1X.1C(1X.x,3f(1X.y)-gc)}1X.M.1s(\'7o\',\'kp\');f.1X=1X}})}E k3(){H f=J,Q=f.1u.Q,6V=f.6V;if(2E&&6V!==\'br\'){G}if(6J&&3F<3&&6V!==\'p\'){G}if(c1&&3F<9){G}H kg=3o(\'h1,h2,h3,h4,h5,h6,3S,li\'),gy=3o(\'p,h1,h2,h3,h4,h5,h6,3S,li,9y\');E gx(R){H 4C=K(R.4V());22(4C){if(4C.1m==1&&!4C.9I()){3d}4C=4C.1q()}G 4C.O}K(Q).a4(E(e){if(e.2Y!=13||e.c2||e.6T||e.9c){G}f.1h.3t();H 2x=gx(f.1h.R);if(2x==\'gw\'||2x==\'1o\'){G}if(6V===\'br\'&&!kg[2x]){e.3X();f.5l(\'
    \'+(2E&&3F<9?\'\':\'\\gl\'));G}if(!gy[2x]){6q(Q,\'4E\',\'

    \')}});K(Q).ad(E(e){if(e.2Y!=13||e.c2||e.6T||e.9c){G}if(6V==\'br\'){G}if(6J){H 1T=f.1h.4V(\'p\');H a=f.1h.4V(\'a\');if(a&&a.1A()==\'\'){a.1t(1f);f.1h.R.49(1T[0]).2g(1f);f.1h.1o()}G}f.1h.3t();H 2x=gx(f.1h.R);if(2x==\'gw\'||2x==\'1o\'){G}if(!gy[2x]){6q(Q,\'4E\',\'

    \')}H M=f.1h.4V(\'M\');if(M){H p=K(\'

    \'),1i=M[0].3L;22(1i){H 2S=1i.4B;p.2r(1i);1i=2S}M.7v(p);M.1t();f.1h.R.49(p[0]);f.1h.1o()}})}E k2(){H f=J,Q=f.1u.Q;K(Q).a4(E(e){if(e.2Y==9){e.3X();if(f.k6){f.k6.1P(f,e);G}H 1h=f.1h,R=1h.R;R.a0();if(R.4k&&R.1W.29==1){R.4f(K(\'@&3b;\',Q)[0]);1h.1o()}f.5l(\'&3b;&3b;&3b;&3b;\')}})}E jz(){H f=J;K(f.1u.2U[0],f.1u.38).1Z(E(e){if(f.k8){f.k8.1P(f,e)}}).5R(E(e){if(f.k9){f.k9.1P(f,e)}})}E 6d(N){G 4b(N.1g(/<1p [^>]*id="?mu\\w+mn\\d+g9"?[^>]*><\\/1p>/ig,\'\'))}E ae(N){G N.1g(/]+1j="?k7"?[^>]*>[\\s\\S]*?<\\/M>/ig,\'\')}E eO(8x,2i){if(8x.1a===0){8x.1N(2i);G}H 2H=8x[8x.1a-1];if(6d(2i.N)!==6d(2H.N)){8x.1N(2i)}}E eI(9Y,kc){H f=J,1u=f.1u,1n=1u.Q.1n,R,2i;if(9Y.1a===0){G f}if(1u.2K){R=f.1h.R;2i=R.67(1f);2i.N=1n.3u}1d{2i={N:1n.3u}}eO(kc,2i);H 2H=9Y.6K();if(6d(2i.N)===6d(2H.N)&&9Y.1a>0){2H=9Y.6K()}if(1u.2K){1u.N(2H.N);if(2H.2h){R.60(2H);f.1o()}}1d{K(1n).N(6d(2H.N))}G f}E 8q(W){H f=J;f.W={};E 89(15,L){if(8q.4M[15]===1Q){f[15]=L}f.W[15]=L}1y(W,E(15,L){89(15,W[15])});1y(K.W,E(15,L){if(f[15]===1Q){89(15,L)}});H 8B=K(f.2P||\'<2U/>\');if(!f.19){f.19=8B[0].1e.19||8B.19()}if(!f.17){f.17=8B[0].1e.17||8B.17()}89(\'19\',2a(f.19,f.b7));89(\'17\',2a(f.17,f.5A));89(\'19\',3n(f.19));89(\'17\',3n(f.17));if(kr&&(!kH||3F\')}1d if(O==\'/\'){8A.1N(\'\')}1d{8A.1N(\'<1p 1j="ke-b4" 1O-O="\'+O+\'" 1Y="\'+f.1k(O)+\'" 5n="on">\');8A.1N(\'<1p 1j="ke-2M-4W ke-2M-4W-1v ke-4W-\'+O+\'" 5n="on">\')}});H 2M=f.2M=fl({1S:kw,N:8A.3Y(\'\'),73:f.73,2d:E(e,O){e.9A();if(f.1X){H kx=f.1X.O;f.54();if(kx===O){G}}f.35(O)}});H 7r=3f(17)-2M.M.17();H 1u=f.1u=fa({17:7r>0&&3f(17)>f.5A?7r:f.5A,1S:kA,2P:f.2P,2K:f.2K,3T:f.3T,6s:f.6s,5t:f.5t,6A:f.6A,6r:E(N){N=f.6r(N);N=6d(ae(N));G 7i(N,f.9E?f.4g:1z,f.8X,f.kz,f.7a)},53:E(N){N=7i(N,f.9E?f.4g:1z,\'\',1l);G f.53(N)},6e:E(){f.1u=1u=J;f.6e()},44:E(){f.1u=1u=J;f.1h=1u.1h;f.fI=E(e){if(f.1X){f.54()}};K(1u.Q,1M).3U(f.fI);ky.1P(f);k3.1P(f);k2.1P(f);jz.1P(f);1u.4Y(E(e){if(!1u.2K){G}f.cO();f.3k();if(f.W.4Y){f.W.4Y.1P(f)}});1u.2U.ad(E(e){if(!e.6T&&!e.9c&&fC[e.2Y]){if(f.W.4Y){f.W.4Y.1P(f)}}});if(f.nM){f.eA()}f.3P=1f;if(f.aL===\'\'){f.aL=f.N()}if(f.6k.1a>0){H 2H=f.6k.6K();if(2H.2h){f.N(2H.N);1u.1h.R.60(2H);f.1o()}}f.44();if(f.W.44){f.W.44.1P(f)}}});3w.41(\'3w\').2Z(\'ke-3w\').2r(\'<1p 1j="ke-4F-4j ke-3w-3C-4W">\').2r(\'<1p 1j="ke-4F-4j ke-3w-5i-4W">\');if(f.aJ){K(3l).2u(\'6c\',f.aJ);f.aJ=1z}E fV(){if(3w.17()===0){6v(fV,63);G}f.6c(19,17,1l)}fV();if(5r){f.aJ=E(e){if(f.3P){f.6c(4i().9g,4i().7A,1l)}};K(3l).3e(\'6c\',f.aJ);2M.1o(\'5y\');3w.3M().1s(\'7o\',\'4X\');3w.83().1s(\'7o\',\'4X\')}1d{if(6J){K(3l).3e(\'9T\',E(e){f.ac=7D()})}if(f.g2>0){b1({5x:3s,6L:3w,9h:E(x,y,19,17,4J,4A){17+=4A;f.6c(1z,17)}})}1d{3w.3M().1s(\'7o\',\'4X\')}if(f.g2===2){b1({5x:3s,6L:3w.83(),9h:E(x,y,19,17,4J,4A){19+=4J;17+=4A;f.6c(19,17)}})}1d{3w.83().1s(\'7o\',\'4X\')}}G f},1t:E(){H f=J;if(!f.3P){G f}f.5Y();f.2P.1O(\'aZ\',\'\');if(f.1X){f.54()}1y(f.4m,E(){f.6C()});K(1M).2u(\'3U\',f.fI);f.2M.1t();f.1u.1t();f.3w.83().2u();f.3w.2u();f.3s.1t();f.3s=f.2M=f.1u=f.1X=1z;f.4m=[];f.3P=1l;G f},6c:E(19,17,5G){H f=J;5G=2a(5G,1f);if(19){if(!/%/.2f(19)){19=3f(19);19=19/ig,\'\').1g(/&3b;/ig,\' \'))}1d{G f.N(3Z(L))}},o5:E(){G 4b(J.1A().1g(/\\r\\n|\\n|\\r/,\'\'))===\'\'},nY:E(){G 4b(J.aL.1g(/\\r\\n|\\n|\\r|t/g,\'\'))!==4b(J.N().1g(/\\r\\n|\\n|\\r|t/g,\'\'))},mX:E(){H L=J.3P?J.1h.R.N():\'\';L=6d(ae(L));G L},aY:E(5e){H f=J;5e=(5e||\'N\').2W();if(5e===\'N\'){G f.N().1a}if(5e===\'1A\'){G f.1A().1g(/<(?:2n|7k).*?>/ig,\'K\').1g(/\\r\\n|\\n|\\r/g,\'\').1a}G 0},2y:E(15){15=15.2W();H f=J,1h=f.1h,fs=5w(15,\'70,7C,71,7d\'.2s(\',\'))<0;if(fs){f.3k(1l)}1h[15].jK(1h,b6(fo,1));if(fs){f.cO();f.3k(1l);if(f.W.4Y){f.W.4Y.1P(f)}}G f},5l:E(L,b5){if(!J.3P){G J}L=J.53(L);J.2y(\'aW\',L,b5);G J},fJ:E(L){J.N(J.N()+L);if(J.3P){H 1h=J.1h;1h.R.49(1h.Q.1n).2g(1l);1h.1o()}G J},cH:E(){8O(J.2P,J.N());G J},1Z:E(){J.3P?J.1u.1Z():J.2P[0].1Z();G J},5R:E(){J.3P?J.1u.5R():J.2P[0].5R();G J},3k:E(b0){b0=2a(b0,1f);H f=J,1u=f.1u,1n=1u.Q.1n,N=ae(1n.3u),2i;if(b0&&f.6k.1a>0){H 2H=f.6k[f.6k.1a-1];if(7n.n9(N.1a-6d(2H.N).1a)0){H d9=f.4m[0],aT=f.4m[f.4m.1a-1];d9.ek(aT.z+2);W.z=aT.z+3;W.d7=1l}H 21=ga(W);f.4m.1N(21);G 21},6C:E(){H f=J;if(f.4m.1a>0){f.4m.6K().1t()}if(f.4m.1a>0){H d9=f.4m[0],aT=f.4m[f.4m.1a-1];d9.ek(aT.z-1)}G f},lM:E(N){H f=J;H 21=f.72({19:ji,1Y:f.1k(\'l9\'),1n:\'<1H cW="0" 1e="19:h0;17:h8;">\'});H 1H=K(\'1H\',21.M),Q=K.cX(1H);Q.9z();Q.dw(N);Q.7q();K(Q.1n).1s(\'2v-1K\',\'#hs\');1H[0].d3.1Z();G f}};E jn(W){G 2k 8q(W)}8z=[];E fH(1E,W){W=W||{};W.5Q=2a(W.5Q,K.5Q);W.3T=2a(W.3T,W.5Q+\'kT/\');W.db=2a(W.db,W.5Q+\'1k/\');if(2a(W.f3,K.W.f3)){H 5U=2a(W.5U,K.W.5U);dc(W.3T+\'8H/8H.1s\');dc(W.3T+5U+\'/\'+5U+\'.1s\')}E 7J(3p){1y(6f,E(O,fn){if(an(fn)){fn.1P(3p,3O);if(!3p.5X){3p.5X={}}3p.5X[O]=\'l3\'}});G 3p.7J()}H 1b=K(1E);if(!1b||1b.1a===0){G}if(1b.1a>1){1b.2e(E(){fH(J,W)});G 8z[0]}W.2P=1b[0];H 3p=2k 8q(W);8z.1N(3p);if(62[3p.3y]){G 7J(3p)}dR(3p.db+3p.3y+\'.js?nn=\'+d8(K.jJ?da:cJ),E(){7J(3p)});G 3p}E 7W(1E,fn){K(1E).2e(E(i,el){K.2e(8z,E(j,3p){if(3p&&3p.2P[0]==el){fn.1P(3p,j);G 1l}})})}K.1t=E(1E){7W(1E,E(i){J.1t();8z.jR(i,1)})};K.cH=E(1E){7W(1E,E(){J.cH()})};K.N=E(1E,L){7W(1E,E(){J.N(L)})};K.5l=E(1E,L){7W(1E,E(){J.5l(L)})};K.nX=E(1E,L){7W(1E,E(){J.jo(L)})};K.fJ=E(1E,L){7W(1E,E(){J.fJ(L)})};if(2E&&3F<7){6q(1M,\'ob\',1f)}K.o7=8q;K.3p=jn;K.7J=fH;K.nQ=8z;K.1I=fR;K.1k=fQ;fR(\'jv\',E(K){H f=J,9x={8s:\'Z\',8r:\'Y\',6p:\'B\',6Z:\'I\',77:\'U\',7d:\'P\',70:\'A\'};f.6e(E(){if(f.W.4Y){f.W.4Y.1P(f)}});f.44(E(){if(f.jB!=\'3K\'){G}H el=K(f.2P),fZ=1l;22((el=el.1q())){if(el.O==\'3K\'){fZ=1f;3d}}if(fZ){el.3e(\'d5\',E(e){f.cH();K(3l).3e(\'jw\',E(){f.1u.2U.1t()})});H fS=K(\'[1m="jx"]\',el);fS.2d(E(){f.N(f.aL);f.1h.3t()});f.5Y(E(){el.2u();fS.2u()})}});f.35(\'5D\',E(){if(f.1u.2K){f.2M.am(1f);f.1u.fU(1l);f.2M.1o(\'5D\')}1d{f.2M.am(1l);f.1u.fU(1f);f.2M.fB(\'5D\');if(6J){6v(E(){f.1h.3t()},0)}1d{f.1h.3t()}}f.2K=f.1u.2K});f.44(E(){if(!f.2K){f.2M.am(1f).1o(\'5D\')}});f.35(\'5y\',E(){f.5y()});if(f.jy){H 68=1l;f.44(E(){K(f.1u.Q,f.1u.2U).ad(E(e){if(e.2Y==27){6v(E(){f.5y()},0)}});if(68){if(2E&&!f.2K){G}f.1Z()}if(!68){68=1f}})}1y(\'8s,8r\'.2s(\',\'),E(i,O){if(9x[O]){f.44(E(){cK(J.1u.Q,9x[O],E(){f.35(O)})})}f.35(O,E(){f[O]()})});f.35(\'4E\',E(){H kC=f.1k(\'4E.kY\'),g5={h1:28,h2:24,h3:18,lC:14,p:12},5p=f.1h.L(\'4E\'),1X=f.8m({O:\'4E\',19:f.3y==\'en\'?ku:cR});1y(kC,E(15,L){H 1e=\'23-3H:\'+g5[15]+\'px;\';if(15.ft(0)===\'h\'){1e+=\'23-6Q:6p;\'}1X.8N({1Y:\'<1p 1e="\'+1e+\'" 5n="on">\'+L+\'\',17:g5[15]+12,6B:(5p===15||5p===L),2d:E(){f.1o().2y(\'4E\',\'<\'+15+\'>\').54()}})})});f.35(\'5d\',E(){H 5p=f.1h.L(\'5d\'),1X=f.8m({O:\'5d\',19:cR});1y(f.1k(\'5d.le\'),E(15,L){1X.8N({1Y:\'<1p 1e="23-6w: \'+15+\';" 5n="on">\'+L+\'\',6B:(5p===15.2W()||5p===L.2W()),2d:E(){f.2y(\'5d\',15).54()}})})});f.35(\'69\',E(){H 5p=f.1h.L(\'69\'),1X=f.8m({O:\'69\',19:cR});1y(f.kJ,E(i,L){1X.8N({1Y:\'<1p 1e="23-3H:\'+L+\';" 5n="on">\'+L+\'\',17:3f(L)+12,6B:5p===L,2d:E(){f.2y(\'69\',L).54()}})})});1y(\'8u,8n\'.2s(\',\'),E(i,O){f.35(O,E(){f.8m({O:O,9p:f.1h.L(O)||\'8H\',7l:f.eB,2d:E(1K){f.2y(O,1K).54()}})})});1y((\'aA,7C,71\').2s(\',\'),E(i,O){f.35(O,E(){f.1Z();3r{f.2y(O,1z)}3q(e){4u(f.1k(O+\'lw\'))}})});f.35(\'9m\',E(){H N=\'\'+\'3O \'+cJ+\'\'+\'lz &7C; kq.kb mt mv mC.\'+\'\';f.72({O:\'9m\',19:mD,1Y:f.1k(\'9m\'),1n:N})});f.1I.fM=E(){G k4(f.1u.1h.R,E(2n){G 2n[0].2V==\'ke-4Z\'})};f.1I.4G=E(){G f.1h.4V(\'1c\')};f.1I.6G=E(){G f.1h.4V(\'66\')};f.1I.5m=E(){G f.1h.4V(\'56\')};1y((\'aF,ir,hz,hw,hE,hC,hU,ih,\'+\'ib,ii,il,hY,hH,3J\').2s(\',\'),E(i,L){H a1=5w(L,[\'aF\',\'3J\'])<0?f.1I.5m:f.1I.4G;f.gs({1Y:f.1k(\'1c\'+L),2d:E(){f.cL(\'1c\',E(){f.1I.1c[L]();f.54()})},a1:a1,19:mB,8y:\'ke-4W-1c\'+L})});f.gs({1Y:\'-\'});1y((\'70,81,8e,8g,8h,8t,\'+\'8p,7s,cy,as,at,hr,7d,\'+\'6p,6Z,77,9o,cz,82\').2s(\',\'),E(i,O){if(9x[O]){f.44(E(){cK(J.1u.Q,9x[O],E(){f.1h.3t();f.35(O)})})}f.35(O,E(){f.1Z().2y(O,1z)})});f.44(E(){H Q=f.1u.Q,1h,2i,M,2L=\'k7\',cw=1l;E ki(){1h.R.60(2i);1h.1o();if(6H){K(\'M.\'+2L,M).2e(E(){K(J).8b(\'
    \').1t(1f)});K(\'1p.kf-1e-1p\',M).1t(1f);K(\'1p.kf-8c-1p\',M).1t(1f);K(\'1p[1e]\',M).2e(E(){if(K(J).1s(\'kh-cY\')==\'gn\'){K(J).1t(1f)}});K(\'9C\',M).1t()}H N=M[0].3u;M.1t();if(N===\'\'){G}if(6H){N=N.1g(/(
    )\\1/ig,\'$1\')}if(f.cD===2){N=N.1g(/(<(?:p|p\\s[^>]*)>) *(<\\/p>)/ig,\'\');if(/my-mi-m3|m4|m0-\\w+/i.2f(N)){N=g8(N,f.9E?f.4g:K.W.4g)}1d{N=7i(N,f.9E?f.4g:1z);N=f.53(N)}}if(f.cD===1){N=N.1g(/&3b;/ig,\' \');N=N.1g(/\\n\\s*\\n/g,\'\\n\');N=N.1g(/]*>/ig,\'\\n\');N=N.1g(/<\\/p>]*>/ig,\'\\n\');N=N.1g(/<[^>]+>/g,\'\');N=N.1g(/ {2}/g,\' &3b;\');if(f.6V==\'p\'){if(/\\n/.2f(N)){N=N.1g(/^/,\'

    \').1g(/$/,\'

    \').1g(/\\n/g,\'

    \')}}1d{N=N.1g(/\\n/g,\'
    $&\')}}f.5l(N,1f)}K(Q.1n).3e(\'71\',E(e){if(f.cD===0){e.9A();G}if(cw){G}cw=1f;K(\'M.\'+2L,Q).1t();1h=f.1h.3t();2i=1h.R.67();M=K(\'\',Q).1s({7X:\'7h\',19:\'79\',17:\'79\',7I:\'4X\',3W:\'-lW\',4L:K(2i.2h).1C().y+\'px\',\'kh-cY\':\'gn\'});K(Q.1n).2r(M);if(2E){H 1x=1h.R.3A(1f);1x.dM(M[0]);1x.1o();1x.kj(\'71\');e.3X()}1d{1h.R.49(M[0]);1h.1o();M[0].8T=-1;M[0].1Z()}6v(E(){ki();cw=1l},0)})});f.6r(E(N){if(2E&&3F<=8){N=N.1g(/]*1O-ke-1B-2l="([^"]*)"[^>]*>([\\s\\S]*?)<\\/M>/ig,E(2b,2l){G 6g(2l)});N=N.1g(/(<1B)((?:\\s+[^>]*)?>)/ig,E($0,$1,$2){if(!/\\s+1m="[^"]+"/i.2f($0)){G $1+\' 1m="1A"\'+$2}G $0})}G N.1g(/(<(?:4x|4x\\s[^>]*)>)([\\s\\S]*?)(<\\/4x>)/ig,E($0,$1,$2,$3){G $1+8Q($2).1g(/\\s+/g,\' \')+$3}).1g(/<2n[^>]*1j="?ke-(8j|93|aq)"?[^>]*>/ig,E(2b){H 7U=5f(2b);H 7L=7M(7U.1e||\'\');H 2I=dO(7U[\'1O-ke-2l\']);H 19=2a(7L.19,\'\');H 17=2a(7L.17,\'\');if(/px/i.2f(19)){19=3f(19)}if(/px/i.2f(17)){17=3f(17)}2I.19=2a(7U.19,19);2I.17=2a(7U.17,17);G cv(2I)}).1g(/<2n[^>]*1j="?ke-4Z"?[^>]*>/ig,E(2b){H 7U=5f(2b);G\'\'}).1g(/]*1O-ke-2w-1V="([^"]*)"[^>]*>([\\s\\S]*?)<\\/M>/ig,E(2b,1V,5H){G\'<2w\'+6g(1V)+\'>\'+6g(5H)+\'\'}).1g(/]*1O-ke-4x-1V="([^"]*)"[^>]*>([\\s\\S]*?)<\\/M>/ig,E(2b,1V,5H){G\'<4x\'+6g(1V)+\'>\'+6g(5H)+\'\'}).1g(/(<[^>]*)1O-ke-1S="([^"]*)"([^>]*>)/ig,E(2b,2h,1S,2F){2b=2b.1g(/(\\s+(?:4v|1S)=")[^"]*(")/i,E($0,$1,$2){G $1+8Q(1S)+$2});2b=2b.1g(/\\s+1O-ke-1S="[^"]*"/i,\'\');G 2b}).1g(/(<[^>]+\\s)1O-ke-(on\\w+="[^"]*"[^>]*>)/ig,E(2b,2h,2F){G 2h+2F})});f.53(E(N){if(2E&&3F<=8){N=N.1g(/<1B[^>]*>|<(1o|2m)[^>]*>[\\s\\S]*?<\\/\\1>/ig,E(2b){H 2I=5f(2b);H 7L=7M(2I.1e||\'\');if(7L.4a==\'4N\'){G\'\'}G 2b})}G N.1g(/<7k[^>]*1m="([^"]+)"[^>]*>(?:<\\/7k>)?/ig,E(2b){H 2I=5f(2b);2I.1S=2a(2I.1S,\'\');2I.19=2a(2I.19,0);2I.17=2a(2I.17,0);G dK(f.3T+\'5F/kl.92\',2I)}).1g(/]*O="([^"]+)"[^>]*>(?:<\\/a>)?/ig,E(2b){H 2I=5f(2b);if(2I.4v!==1Q){G 2b}G\'<2n 1j="ke-4Z" 1S="\'+f.3T+\'5F/4Z.92" 1O-ke-O="\'+5k(2I.O)+\'" />\'}).1g(/<2w([^>]*)>([\\s\\S]*?)<\\/2w>/ig,E(2b,1V,5H){G\'\'+5k(5H)+\'\'}).1g(/<4x([^>]*)>([\\s\\S]*?)<\\/4x>/ig,E(2b,1V,5H){G\'\'+5k(5H)+\'\'}).1g(/(<[^>]*)(4v|1S)="([^"]*)"([^>]*>)/ig,E(2b,2h,15,1S,2F){if(2b.1G(/\\qd-ke-1S="[^"]*"/i)){G 2b}2b=2h+15+\'="\'+1S+\'"\'+\' 1O-ke-1S="\'+3Z(1S)+\'"\'+2F;G 2b}).1g(/(<[^>]+\\s)(on\\w+="[^"]*"[^>]*>)/ig,E(2b,2h,2F){G 2h+\'1O-ke-\'+2F}).1g(/<1c[^>]*\\s+1F="0"[^>]*>/ig,E(2b){if(2b.3B(\'ke-7w\')>=0){G 2b}G k5(2b,\'ke-7w\')})})})})(3l);3O.1k({5D:\'kv代码\',9V:\'预览\',8s:\'后退(4z+Z)\',8r:\'前进(4z+Y)\',aA:\'剪切(4z+X)\',7C:\'复制(4z+C)\',71:\'粘贴(4z+V)\',70:\'全选(4z+A)\',81:\'左对齐\',8e:\'居中\',8g:\'右对齐\',8h:\'两端对齐\',8t:\'编号\',8p:\'项目符号\',7s:\'增加缩进\',cy:\'减少缩进\',as:\'下标\',at:\'上标\',4E:\'段落\',5d:\'字体\',69:\'文字大小\',8u:\'文字颜色\',8n:\'文字背景\',6p:\'粗体(4z+B)\',6Z:\'斜体(4z+I)\',77:\'下划线(4z+U)\',9o:\'删除线\',cz:\'删除格式\',cC:\'图片\',qX:\'批量图片上传\',8j:\'dv\',aq:\'视音频\',1c:\'表格\',iY:\'单元格\',hr:\'插入横线\',3E:\'超级链接\',82:\'取消超级链接\',5y:\'全屏显示\',5y:\'全屏显示\',9m:\'关于\',7d:\'打印(4z+P)\',qy:\'文件空间\',5H:\'插入程序代码\',ah:\'行距\',av:\'清理kv代码\',9H:\'一键排版\',qx:\'插入文件\',4Z:\'锚点\',8W:\'确定\',no:\'取消\',7q:\'关闭\',qz:\'图片属性\',qA:\'删除图片\',qI:\'dv属性\',qJ:\'删除dv\',oJ:\'视音频属性\',oK:\'删除视音频\',p1:\'超级链接属性\',p2:\'取消超级链接\',p0:\'锚点属性\',oZ:\'删除锚点\',oX:\'表格属性\',oI:\'单元格属性\',oH:\'插入表格\',or:\'删除表格\',ot:\'左侧插入列\',op:\'右侧插入列\',oj:\'上方插入行\',ok:\'下方插入行\',om:\'向下合并单元格\',oo:\'向右合并单元格\',ov:\'拆分行\',oD:\'拆分列\',oF:\'删除列\',oC:\'删除行\',7R:\'无颜色\',oB:\'请选择文件。\',ox:"请输入有效的cP地址。\\n只允许oy,92,oA,p4格式。",pB:"请输入有效的cP地址。\\n只允许kO,kN,pl,oN,pk,pi,ph,p9,p6,lg,93,lm格式。",dX:"宽度必须为数字。",e2:"高度必须为数字。",e1:"边框必须为数字。",cQ:"请输入有效的cP地址。",eW:\'行数为必选项,只允许输入大于0的数字。\',pb:\'列数为必选项,只允许输入大于0的数字。\',iN:\'边距必须为数字。\',iM:\'间距必须为数字。\',pe:\'服务器发生故障。\',pd:\'上传成功。\',pc:\'您的浏览器安全设置不允许使用剪切操作,请使用快捷键(4z+X)来完成。\',pO:\'您的浏览器安全设置不允许使用复制操作,请使用快捷键(4z+C)来完成。\',pu:\'您的浏览器安全设置不允许使用粘贴操作,请使用快捷键(4z+V)来完成。\',l9:\'上传错误\',\'3E.1v\':\'cP\',\'3E.gI\':\'打开类型\',\'3E.hg\':\'新窗口\',\'3E.gW\':\'当前窗口\',\'1c.2T\':\'单元格数\',\'1c.3c\':\'行数\',\'1c.6O\':\'列数\',\'1c.3H\':\'大小\',\'1c.19\':\'宽度\',\'1c.17\':\'高度\',\'1c.a2\':\'%\',\'1c.px\':\'px\',\'1c.cY\':\'边距间距\',\'1c.30\':\'边距\',\'1c.4O\':\'间距\',\'1c.2j\':\'对齐方式\',\'1c.7z\':\'水平对齐\',\'1c.7x\':\'垂直对齐\',\'1c.cA\':\'默认\',\'1c.eH\':\'左对齐\',\'1c.eL\':\'居中\',\'1c.eQ\':\'右对齐\',\'1c.ix\':\'顶部\',\'1c.hu\':\'中部\',\'1c.iQ\':\'底部\',\'1c.jb\':\'基线\',\'1c.1F\':\'边框\',\'1c.cE\':\'边框\',\'1c.4l\':\'颜色\',\'1c.9J\':\'背景颜色\',\'4Z.O\':\'锚点名称\',\'4E.kY\':{h1:\'标题 1\',h2:\'标题 2\',h3:\'标题 3\',h4:\'标题 4\',p:\'正 文\'},\'5d.le\':{\'oL\':\'宋体\',\'oR\':\'新宋体\',\'oQ\':\'仿宋lf\',\'pQ\':\'楷体lf\',\'qP\':\'黑体\',\'kW qS\':\'微软雅黑\',\'cU\':\'cU\',\'cU l7\':\'cU l7\',\'l4 cT l2\':\'l4 cT l2\',\'l1 cT\':\'l1 cT\',\'kQ\':\'kQ\',\'kP\':\'kP\'},\'ah.gN\':[{\'1\':\'单倍行距\'},{\'1.5\':\'1.5倍行距\'},{\'2\':\'2倍行距\'},{\'2.5\':\'2.5倍行距\'},{\'3\':\'3倍行距\'}]},\'fG-fD\');3O.W.3y=\'fG-fD\';3O.1I(\'4Z\',E(K){H f=J,O=\'4Z\',1k=f.1k(O+\'.\');f.1I.4Z={1u:E(){H N=[\'\',\'\',\'<2G 1J="lk">\'+1k.O+\'\',\'<1B 1j="ke-1B-1A" 1m="1A" id="lk" O="O" 26="" 1e="19:7H;" />\',\'\',\'\'].3Y(\'\');H 21=f.72({O:O,19:qg,1Y:f.1k(O),1n:N,64:{O:f.1k(\'8W\'),2d:E(e){f.5l(\'\').6C().1Z()}}});H M=21.M,aI=K(\'1B[O="O"]\',M);H 2n=f.1I.fM();if(2n){aI.L(6g(2n.1V(\'1O-ke-O\')))}aI[0].1Z();aI[0].1o()},\'3J\':E(){f.1I.fM().1t()}};f.35(O,f.1I.4Z.1u)});3O.1I(\'qt\',E(K){H f=J;if(!f.qu){G}H 5A;E gU(){H 1u=f.1u;H 1n=1u.Q.1n;1u.1H[0].9T=\'no\';1n.1e.qo=\'4X\'}E fy(){H 1u=f.1u;H 1n=1u.Q.1n;1u.1H.17(5A);f.6c(1z,7n.9P((K.6S?1n.fY:1n.hq)+76,5A))}E 2A(){5A=K.hn(f.17);f.1u.4Y(fy);gU();fy()}if(f.3P){2A()}1d{f.44(2A)}});3O.1I(\'av\',E(K){H f=J,O=\'av\';f.35(O,E(){f.1Z();H N=f.N();N=N.1g(/(<2w[^>]*>)([\\s\\S]*?)(<\\/2w>)/ig,\'\');N=N.1g(/(<1e[^>]*>)([\\s\\S]*?)(<\\/1e>)/ig,\'\');N=K.gZ(N,{a:[\'4v\',\'2C\'],7k:[\'1S\',\'19\',\'17\',\'1m\',\'gX\',\'hc\',\'hd\',\'.19\',\'.17\',\'2j\',\'hm\'],2n:[\'1S\',\'19\',\'17\',\'1F\',\'cV\',\'1Y\',\'.19\',\'.17\'],1c:[\'1F\'],gg:[\'1e\',\'hl\'],5D:[\'1S\',\'1m\'],\'56,cS\':[\'hi\',\'gD\'],\'M,hr,br,gk,66,p,ol,5u,li,9y,h1,h2,h3,h4,h5,h6\':[]});f.N(N);f.1h.3t(1f);f.3k()})});3O.1I(\'ah\',E(K){H f=J,O=\'ah\',1k=f.1k(O+\'.\');f.35(O,E(){H 5p=\'\',4U=f.1h.4U({\'*\':\'.5s-17\'});if(4U){5p=4U.1s(\'5s-17\')}H 1X=f.8m({O:O,19:cR});K.2e(1k.gN,E(i,1R){K.2e(1R,E(15,L){1X.8N({1Y:L,6B:5p===15,2d:E(){f.1h.8M(\'<1p 1e="5s-17:\'+15+\';">\',{1p:\'.5s-17=\'+15});f.cO();f.3k();f.54()}})})})})});3O.1I(\'3E\',E(K){H f=J,O=\'3E\';f.1I.3E={1u:E(){H 1k=f.1k(O+\'.\'),N=\'\'+\'\'+\'<2G 1J="gG" 1e="19:gJ;">\'+1k.1v+\'\'+\'<1B 1j="ke-1B-1A" 1m="1A" id="gG" O="1v" 26="" 1e="19:oV;" />\'+\'\'+\'<2G 1J="gH" 1e="19:gJ;">\'+1k.gI+\'\'+\'<1o id="gH" O="1m">\'+\'\'+\'\',21=f.72({O:O,19:pD,1Y:f.1k(O),1n:N,64:{O:f.1k(\'8W\'),2d:E(e){H 1v=K.gL(7Z.L());if(1v==\'dS://\'||K.cQ(1v)){4u(f.1k(\'cQ\'));7Z[0].1Z();G}f.2y(\'dZ\',1v,9K.L()).6C().1Z()}}}),M=21.M,7Z=K(\'1B[O="1v"]\',M),9K=K(\'1o[O="1m"]\',M);7Z.L(\'dS://\');9K[0].W[0]=2k hp(1k.hg,\'he\');9K[0].W[1]=2k hp(1k.gW,\'\');f.1h.3t();H a=f.1I.pf();if(a){f.1h.R.4P(a[0]);f.1h.1o();7Z.L(a.1V(\'1O-ke-1S\'));9K.L(a.1V(\'2C\'))}7Z[0].1Z();7Z[0].1o()},\'3J\':E(){f.2y(\'82\',1z)}};f.35(O,f.1I.3E.1u)});3O.1I(\'9V\',E(K){H f=J,O=\'9V\',1Q;f.35(O,E(){H 1k=f.1k(O+\'.\'),N=\'\'+\'<1H 1j="ke-2U" cW="0" 1e="19:h0;17:h8;">\'+\'\',21=f.72({O:O,19:ji,1Y:f.1k(O),1n:N}),1H=K(\'1H\',21.M),Q=K.cX(1H);Q.9z();Q.dw(f.gY());Q.7q();K(Q.1n).1s(\'2v-1K\',\'#hs\');1H[0].d3.1Z()})});3O.1I(\'9H\',E(K){H f=J,O=\'9H\',aB=K.hh(\'9y,3C,M,h1,h2,h3,h4,h5,h6,p\');E hj(1b){H 1i=1b.3M();22(1i&&1i.3M()){1i=1i.3M()}G 1i}f.35(O,E(){f.1Z();H Q=f.1u.Q,R=f.1h.R,1i=K(Q.1n).3M(),2S,4q=[],7p=[],2i=R.67(1f);22(1i){2S=1i.2S();H 3L=hj(1i);if(!3L||3L.O!=\'2n\'){if(aB[1i.O]){1i.N(1i.N().1g(/^(\\s|&3b;| )+/ig,\'\'));1i.1s(\'1A-7s\',\'gP\');1i.1s(\'4I-4L\',\'d4\');1i.1s(\'4I-cB\',\'d4\');1i.1s(\'5s-17\',\'1.75\')}1d{7p.1N(1i)}if(!2S||(aB[2S.O]||aB[1i.O]&&!aB[2S.O])){if(7p.1a>0){4q.1N(7p)}7p=[]}}1i=2S}K.2e(4q,E(i,7p){H 2X=K(\'

    \',Q);7p[0].7v(2X);K.2e(7p,E(i,1b){2X.2r(1b)})});R.60(2i);f.3k()})});3O.1I(\'1c\',E(K){H f=J,O=\'1c\',1k=f.1k(O+\'.\'),7w=\'ke-7w\';E 5M(5q,1K){1K=1K.d2();5q.1s(\'2v-1K\',1K);5q.1s(\'1K\',1K===\'#8P\'?\'#ep\':\'#8P\');5q.N(1K)}H d1=[];E aP(ez,34){34.3e(\'2d,3U\',E(e){e.5L()});E 8d(){K.2e(d1,E(){J.1t()});d1=[];K(1M).2u(\'2d,3U\',8d);ez.2u(\'2d,3U\',8d)}34.2d(E(e){8d();H 5q=K(J),1C=5q.1C();H iJ=K.5j({x:1C.x,y:1C.y+5q.17(),z:pv,9p:K(J).N(),7l:f.eB,7R:f.1k(\'7R\'),3Q:f.3Q,2d:E(1K){5M(5q,1K);8d()}});d1.1N(iJ);K(1M).3e(\'2d,3U\',8d);ez.3e(\'2d,3U\',8d)})}E cG(1c,1R,1r){H e6=0;1J(H i=0,1D=1R.2T.1a;i<1D;i++){if(1R.2T[i]==1r){3d}e6+=1R.2T[i].3m-1}G 1r.3D-e6}f.1I.1c={aF:E(iu){H N=[\'\',\'\',\'<2G 1J="iF" 1e="19:5N;">\'+1k.2T+\'\',1k.3c+\' <1B 1m="1A" id="iF" 1j="ke-1B-1A ke-1B-5T" O="3c" 26="" 5P="4" /> &3b; \',1k.6O+\' <1B 1m="1A" 1j="ke-1B-1A ke-1B-5T" O="6O" 26="" 5P="4" />\',\'\',\'\',\'<2G 1J="cN" 1e="19:5N;">\'+1k.3H+\'\',1k.19+\' <1B 1m="1A" id="cN" 1j="ke-1B-1A ke-1B-5T" O="19" 26="" 5P="4" /> &3b; \',\'<1o O="6F">\',\'<20 26="%">\'+1k.a2+\'\',\'<20 26="px">\'+1k.px+\'\',\' &3b; \',1k.17+\' <1B 1m="1A" 1j="ke-1B-1A ke-1B-5T" O="17" 26="" 5P="4" /> &3b; \',\'<1o O="6E">\',\'<20 26="%">\'+1k.a2+\'\',\'<20 26="px">\'+1k.px+\'\',\'\',\'\',\'\',\'<2G 1J="iO" 1e="19:5N;">\'+1k.cY+\'\',1k.30+\' <1B 1m="1A" id="iO" 1j="ke-1B-1A ke-1B-5T" O="30" 26="" 5P="4" /> &3b; \',1k.4O+\' <1B 1m="1A" 1j="ke-1B-1A ke-1B-5T" O="4O" 26="" 5P="4" />\',\'\',\'\',\'<2G 1J="cM" 1e="19:5N;">\'+1k.2j+\'\',\'<1o id="cM" O="2j">\',\'<20 26="">\'+1k.cA+\'\',\'<20 26="3W">\'+1k.eH+\'\',\'<20 26="3C">\'+1k.eL+\'\',\'<20 26="5i">\'+1k.eQ+\'\',\'\',\'\',\'\',\'<2G 1J="d6" 1e="19:5N;">\'+1k.1F+\'\',1k.cE+\' <1B 1m="1A" id="d6" 1j="ke-1B-1A ke-1B-5T" O="1F" 26="" 5P="4" /> &3b; \',1k.4l+\' <1p 1j="ke-4F-4j ke-1B-1K">\',\'\',\'\',\'<2G 1J="j5" 1e="19:5N;">\'+1k.9J+\'\',\'<1p 1j="ke-4F-4j ke-1B-1K">\',\'\',\'\'].3Y(\'\');H 2i=f.1h.R.67();H 21=f.72({O:O,19:iV,1Y:f.1k(O),1n:N,5Y:E(){34.2u()},64:{O:f.1k(\'8W\'),2d:E(e){H 3c=84.L(),6O=9R.L(),19=3R.L(),17=6W.L(),6F=8V.L(),6E=8v.L(),30=8K.L(),4O=8L.L(),2j=eT.L(),1F=7e.L(),4l=K(34[0]).N()||\'\',6u=K(34[1]).N()||\'\';if(3c==0||!/^\\d+$/.2f(3c)){4u(f.1k(\'eW\'));84[0].1Z();G}if(6O==0||!/^\\d+$/.2f(6O)){4u(f.1k(\'eW\'));9R[0].1Z();G}if(!/^\\d*$/.2f(19)){4u(f.1k(\'dX\'));3R[0].1Z();G}if(!/^\\d*$/.2f(17)){4u(f.1k(\'e2\'));6W[0].1Z();G}if(!/^\\d*$/.2f(30)){4u(f.1k(\'iN\'));8K[0].1Z();G}if(!/^\\d*$/.2f(4O)){4u(f.1k(\'iM\'));8L[0].1Z();G}if(!/^\\d*$/.2f(1F)){4u(f.1k(\'e1\'));7e[0].1Z();G}if(1c){if(19!==\'\'){1c.19(19+6F)}1d{1c.1s(\'19\',\'\')}if(1c[0].19!==1Q){1c.4o(\'19\')}if(17!==\'\'){1c.17(17+6E)}1d{1c.1s(\'17\',\'\')}if(1c[0].17!==1Q){1c.4o(\'17\')}1c.1s(\'2v-1K\',6u);if(1c[0].6u!==1Q){1c.4o(\'6u\')}if(30!==\'\'){1c[0].cZ=30}1d{1c.4o(\'cZ\')}if(4O!==\'\'){1c[0].d0=4O}1d{1c.4o(\'d0\')}if(2j!==\'\'){1c[0].2j=2j}1d{1c.4o(\'2j\')}if(1F!==\'\'){1c.1V(\'1F\',1F)}1d{1c.4o(\'1F\')}if(1F===\'\'||1F===\'0\'){1c.2Z(7w)}1d{1c.41(7w)}if(4l!==\'\'){1c.1V(\'4l\',4l)}1d{1c.4o(\'4l\')}f.6C().1Z();f.1h.R.60(2i);f.1h.1o();f.3k();G}H 1e=\'\';if(19!==\'\'){1e+=\'19:\'+19+6F+\';\'}if(17!==\'\'){1e+=\'17:\'+17+6E+\';\'}if(6u!==\'\'){1e+=\'2v-1K:\'+6u+\';\'}H N=\'<1c\';if(1e!==\'\'){N+=\' 1e="\'+1e+\'"\'}if(30!==\'\'){N+=\' iE="\'+30+\'"\'}if(4O!==\'\'){N+=\' iD="\'+4O+\'"\'}if(2j!==\'\'){N+=\' 2j="\'+2j+\'"\'}if(1F!==\'\'){N+=\' 1F="\'+1F+\'"\'}if(1F===\'\'||1F===\'0\'){N+=\' 1j="\'+7w+\'"\'}if(4l!==\'\'){N+=\' iv="\'+4l+\'"\'}N+=\'>\';1J(H i=0;i<3c;i++){N+=\'<66>\';1J(H j=0;j<6O;j++){N+=\'<56>\'+(K.6S?\'&3b;\':\'
    \')+\'\'}N+=\'\'}N+=\'\';if(!K.6S){N+=\'
    \'}f.5l(N);f.1o().6C().1Z();f.3k()}}}),M=21.M,84=K(\'[O="3c"]\',M).L(3),9R=K(\'[O="6O"]\',M).L(2),3R=K(\'[O="19"]\',M).L(63),6W=K(\'[O="17"]\',M),8V=K(\'[O="6F"]\',M),8v=K(\'[O="6E"]\',M),8K=K(\'[O="30"]\',M).L(2),8L=K(\'[O="4O"]\',M).L(0),eT=K(\'[O="2j"]\',M),7e=K(\'[O="1F"]\',M).L(1),34=K(\'.ke-1B-1K\',M);aP(M,34.eq(0));aP(M,34.eq(1));5M(34.eq(0),\'#8P\');5M(34.eq(1),\'\');84[0].1Z();84[0].1o();H 1c;if(iu){G}1c=f.1I.4G();if(1c){84.L(1c[0].3c.1a);9R.L(1c[0].3c.1a>0?1c[0].3c[0].2T.1a:0);84.1V(\'55\',1f);9R.1V(\'55\',1f);H 1G,f5=1c[0].1e.19||1c[0].19,f4=1c[0].1e.17||1c[0].17;if(f5!==1Q&&(1G=/^(\\d+)((?:px|%)*)$/.2y(f5))){3R.L(1G[1]);8V.L(1G[2])}1d{3R.L(\'\')}if(f4!==1Q&&(1G=/^(\\d+)((?:px|%)*)$/.2y(f4))){6W.L(1G[1]);8v.L(1G[2])}8K.L(1c[0].cZ||\'\');8L.L(1c[0].d0||\'\');eT.L(1c[0].2j||\'\');7e.L(1c[0].1F===1Q?\'\':1c[0].1F);5M(34.eq(0),K.9F(1c.1V(\'4l\')||\'\'));5M(34.eq(1),K.9F(1c[0].1e.9J||1c[0].6u||\'\'));3R[0].1Z();3R[0].1o()}},ir:E(){H N=[\'\',\'\',\'<2G 1J="cN" 1e="19:5N;">\'+1k.3H+\'\',1k.19+\' <1B 1m="1A" id="cN" 1j="ke-1B-1A ke-1B-5T" O="19" 26="" 5P="4" /> &3b; \',\'<1o O="6F">\',\'<20 26="%">\'+1k.a2+\'\',\'<20 26="px">\'+1k.px+\'\',\' &3b; \',1k.17+\' <1B 1m="1A" 1j="ke-1B-1A ke-1B-5T" O="17" 26="" 5P="4" /> &3b; \',\'<1o O="6E">\',\'<20 26="%">\'+1k.a2+\'\',\'<20 26="px">\'+1k.px+\'\',\'\',\'\',\'\',\'<2G 1J="cM" 1e="19:5N;">\'+1k.2j+\'\',1k.7z+\' <1o id="cM" O="7z">\',\'<20 26="">\'+1k.cA+\'\',\'<20 26="3W">\'+1k.eH+\'\',\'<20 26="3C">\'+1k.eL+\'\',\'<20 26="5i">\'+1k.eQ+\'\',\' \',1k.7x+\' <1o O="7x">\',\'<20 26="">\'+1k.cA+\'\',\'<20 26="4L">\'+1k.ix+\'\',\'<20 26="oM">\'+1k.hu+\'\',\'<20 26="cB">\'+1k.iQ+\'\',\'<20 26="qD">\'+1k.jb+\'\',\'\',\'\',\'\',\'<2G 1J="d6" 1e="19:5N;">\'+1k.1F+\'\',1k.cE+\' <1B 1m="1A" id="d6" 1j="ke-1B-1A ke-1B-5T" O="1F" 26="" 5P="4" /> &3b; \',1k.4l+\' <1p 1j="ke-4F-4j ke-1B-1K">\',\'\',\'\',\'<2G 1J="j5" 1e="19:5N;">\'+1k.9J+\'\',\'<1p 1j="ke-4F-4j ke-1B-1K">\',\'\',\'\'].3Y(\'\');H 2i=f.1h.R.67();H 21=f.72({O:O,19:iV,1Y:f.1k(\'iY\'),1n:N,5Y:E(){34.2u()},64:{O:f.1k(\'8W\'),2d:E(e){H 19=3R.L(),17=6W.L(),6F=8V.L(),6E=8v.L(),30=8K.L(),4O=8L.L(),7z=ge.L(),7x=g7.L(),1F=7e.L(),4l=K(34[0]).N()||\'\',6u=K(34[1]).N()||\'\';if(!/^\\d*$/.2f(19)){4u(f.1k(\'dX\'));3R[0].1Z();G}if(!/^\\d*$/.2f(17)){4u(f.1k(\'e2\'));6W[0].1Z();G}if(!/^\\d*$/.2f(1F)){4u(f.1k(\'e1\'));7e[0].1Z();G}1r.1s({19:19!==\'\'?(19+6F):\'\',17:17!==\'\'?(17+6E):\'\',\'2v-1K\':6u,\'1A-2j\':7z,\'aC-2j\':7x,\'1F-19\':1F,\'1F-1e\':1F!==\'\'?\'cu\':\'\',\'1F-1K\':4l});f.6C().1Z();f.1h.R.60(2i);f.1h.1o();f.3k()}}}),M=21.M,3R=K(\'[O="19"]\',M).L(63),6W=K(\'[O="17"]\',M),8V=K(\'[O="6F"]\',M),8v=K(\'[O="6E"]\',M),8K=K(\'[O="30"]\',M).L(2),8L=K(\'[O="4O"]\',M).L(0),ge=K(\'[O="7z"]\',M),g7=K(\'[O="7x"]\',M),7e=K(\'[O="1F"]\',M).L(1),34=K(\'.ke-1B-1K\',M);aP(M,34.eq(0));aP(M,34.eq(1));5M(34.eq(0),\'#8P\');5M(34.eq(1),\'\');3R[0].1Z();3R[0].1o();H 1r=f.1I.5m();H 1G,j1=1r[0].1e.19||1r[0].19||\'\',hO=1r[0].1e.17||1r[0].17||\'\';if((1G=/^(\\d+)((?:px|%)*)$/.2y(j1))){3R.L(1G[1]);8V.L(1G[2])}1d{3R.L(\'\')}if((1G=/^(\\d+)((?:px|%)*)$/.2y(hO))){6W.L(1G[1]);8v.L(1G[2])}ge.L(1r[0].1e.7z||\'\');g7.L(1r[0].1e.7x||\'\');H 1F=1r[0].1e.cE||\'\';if(1F){1F=6Y(1F)}7e.L(1F);5M(34.eq(0),K.9F(1r[0].1e.4l||\'\'));5M(34.eq(1),K.9F(1r[0].1e.9J||\'\'));3R[0].1Z();3R[0].1o()},hH:E(){J.aF(1f)},\'3J\':E(){H 1c=f.1I.4G();f.1h.R.4Q(1c[0]).2g(1f);f.1h.1o();1c.1t();f.3k()},gh:E(2p){H 1c=f.1I.4G()[0],1R=f.1I.6G()[0],1r=f.1I.5m()[0],2N=1r.3D+2p;2N+=1c.3c[0].2T.1a-1R.2T.1a;1J(H i=0,1D=1c.3c.1a;i<1D;i++){H 5a=1c.3c[i],3h=5a.8C(2N);3h.3u=K.6S?\'\':\'
    \';2N=cG(1c,5a,3h)}f.1h.R.49(1r).2g(1f);f.1h.1o();f.3k()},hz:E(){J.gh(0)},hw:E(){J.gh(1)},go:E(2p){H 1c=f.1I.4G()[0],1R=f.1I.6G()[0],1r=f.1I.5m()[0];H 3N=1R.3N;if(2p===1){3N=1R.3N+(1r.3m-1)+2p}H 5a=1c.gd(3N);1J(H i=0,1D=1R.2T.1a;i<1D;i++){if(1R.2T[i].3m>1){1D-=1R.2T[i].3m-1}H 3h=5a.8C(i);if(2p===1&&1R.2T[i].3G>1){3h.3G=1R.2T[i].3G}3h.3u=K.6S?\'\':\'
    \'}1J(H j=3N;j>=0;j--){H 2T=1c.3c[j].2T;if(2T.1a>i){1J(H k=1r.3D;k>=0;k--){if(2T[k].3m>1){2T[k].3m+=1}}3d}}f.1h.R.49(1r).2g(1f);f.1h.1o();f.3k()},hE:E(){J.go(0)},hC:E(){J.go(1)},hU:E(){H 1c=f.1I.4G()[0],1R=f.1I.6G()[0],1r=f.1I.5m()[0],3N=1R.3N,fq=3N+1r.3m,cI=1c.3c[fq];if(1c.3c.1a<=fq){G}H 3D=1r.3D;if(cI.2T.1a<=3D){G}H 8w=cI.2T[3D];if(1r.3G!==8w.3G){G}1r.3m+=8w.3m;cI.fW(3D);f.1h.R.49(1r).2g(1f);f.1h.1o();f.3k()},ih:E(){H 1c=f.1I.4G()[0],1R=f.1I.6G()[0],1r=f.1I.5m()[0],3N=1R.3N,3D=1r.3D,cF=3D+1;if(1R.2T.1a<=cF){G}H 8w=1R.2T[cF];if(1r.3m!==8w.3m){G}1r.3G+=8w.3G;1R.fW(cF);f.1h.R.49(1r).2g(1f);f.1h.1o();f.3k()},ib:E(){H 1c=f.1I.4G()[0],1R=f.1I.6G()[0],1r=f.1I.5m()[0],3N=1R.3N;if(1r.3m===1){G}H 3D=cG(1c,1R,1r);1J(H i=1,1D=1r.3m;i<1D;i++){H 5a=1c.3c[3N+i],3h=5a.8C(3D);if(1r.3G>1){3h.3G=1r.3G}3h.3u=K.6S?\'\':\'
    \';3D=cG(1c,5a,3h)}K(1r).4o(\'3m\');f.1h.R.49(1r).2g(1f);f.1h.1o();f.3k()},ii:E(){H 1c=f.1I.4G()[0],1R=f.1I.6G()[0],1r=f.1I.5m()[0],3D=1r.3D;if(1r.3G===1){G}1J(H i=1,1D=1r.3G;i<1D;i++){H 3h=1R.8C(3D+i);if(1r.3m>1){3h.3m=1r.3m}3h.3u=K.6S?\'\':\'
    \'}K(1r).4o(\'3G\');f.1h.R.49(1r).2g(1f);f.1h.1o();f.3k()},il:E(){H 1c=f.1I.4G()[0],1R=f.1I.6G()[0],1r=f.1I.5m()[0],2N=1r.3D;1J(H i=0,1D=1c.3c.1a;i<1D;i++){H 5a=1c.3c[i],3h=5a.2T[2N];if(3h.3G>1){3h.3G-=1;if(3h.3G===1){K(3h).4o(\'3G\')}}1d{5a.fW(2N)}if(3h.3m>1){i+=3h.3m-1}}if(1R.2T.1a===0){f.1h.R.4Q(1c).2g(1f);f.1h.1o();K(1c).1t()}1d{f.1h.3t(1f)}f.3k()},hY:E(){H 1c=f.1I.4G()[0],1R=f.1I.6G()[0],1r=f.1I.5m()[0],3N=1R.3N;1J(H i=1r.3m-1;i>=0;i--){1c.lR(3N+i)}if(1c.3c.1a===0){f.1h.R.4Q(1c).2g(1f);f.1h.1o();K(1c).1t()}1d{f.1h.3t(1f)}f.3k()}};f.35(O,f.1I.1c.aF)});',62,1677,'|||||||||||||||self|||||||||||||||||||||||||function||return|var||this||val|div|html|name||doc|range||node|||options|||||||||key||height||width|length|knode|table|else|style|true|replace|cmd|child|class|lang|false|type|body|select|span|parent|cell|css|remove|edit|url|arr|rng|_each|null|text|input|pos|len|expr|border|match|iframe|plugin|for|color|sc|document|push|data|call|undefined|row|src|root|parentNode|attr|startContainer|menu|title|focus|option|dialog|while|font||map|value|||nodeType|_undef|full|startOffset|click|each|test|collapse|start|bookmark|align|new|tag|button|img|endOffset|offset|so|append|split|endContainer|unbind|background|script|tagName|exec|str|init|events|target|isStart|_IE|end|label|prev|attrs|els|designMode|cls|toolbar|index|children|srcElement|childNodes|item|next|cells|textarea|className|toLowerCase|wrapper|which|addClass|padding||||colorBox|clickToolbar|dummy|list|win|nodeC|frag|nbsp|rows|break|bind|_removeUnit|obj|newCell|setEnd|mixed|addBookmark|window|rowSpan|_addUnit|_toMap|editor|catch|try|container|selection|innerHTML|startNode|statusbar|_eventData|langType|appendChild|get|indexOf|center|cellIndex|link|_V|colSpan|size|items|delete|form|firstChild|first|rowIndex|KindEditor|isCreated|shadowMode|widthBox|pre|themesPath|mousedown|setStart|left|preventDefault|join|_escape||removeClass|||afterCreate|||||selectNodeContents|display|_trim|KNode|nodeValue|createElement|insertNode|htmlTags|testRange|_docElement|block|collapsed|borderColor|dialogs|bool|removeAttr|head|nodeList|substr|matches|delimiter|alert|href|event|noscript|nodeB|Ctrl|diffY|nextSibling|ancestor|_extend|formatblock|inline|getSelectedTable|cloneRange|margin|diffX|_ua|top|prototype|none|spacing|selectNode|setStartBefore|attrMap|host|ownerDocument|commonNode|commonAncestor|icon|hidden|afterChange|anchor||nodeName|_IERANGE|beforeSetHtml|hideMenu|disabled|td||newNode||newRow|method|nodeA|fontname|mode|_getAttrList|how|isControl|right|colorpicker|escape|insertHtml|getSelectedCell|unselectable|endNode|curVal|box|fullscreenMode|line|cssPath|ul|cmp|_inArray|moveEl|fullscreen|headerDiv|minHeight|_get|handler|source|path|common|updateProp|code|hide|docEl|sel|stopPropagation|_setColor|90px|endNewline|maxlength|basePath|blur|KRange|number|themeType|cloneNode|mapKey|_pluginStatus|beforeRemove|part|moveToBookmark||_language|100|yesBtn||tr|createBookmark|loaded|fontsize|xhr|lastChild|resize|_removeBookmarkTag|afterSetHtml|_plugins|unescape|_round|find|centerDiv|_undoStack|parts|tabs|bodyDiv|KWidget|bold|_nativeCommand|beforeGetHtml|bodyClass|htmlTagMap|bgColor|setTimeout|family|newMap|case|tagStack|cssData|checked|hideDialog|_QUIRKS|heightType|widthType|getSelectedRow|_WEBKIT|hasClass|_GECKO|pop|clickEl|contextmenu|_getWin|cols|_getDoc|weight|scrollPos|IE|ctrlKey|isCopy|newlineTag|heightBox|startNewline|parseInt|italic|selectall|paste|createDialog|noDisableItems|isReadonly|||underline||1px|indentChar|selected|show|print|borderBox|getElementsByTagName|removeChild|absolute|_formatHtml|btn|embed|colors|decoration|Math|visibility|subList|close|editHeight|indent|ksc|startPos|before|zeroborder|verticalAlign|startSlash|textAlign|clientHeight|setEndAfter|copy|_getScrollPos|footerDiv|toString|_END_TO_END|100px|overflow|create|tempRange|styles|_getCssList|console|compareBoundaryPoints|isBlock|results|noColor|setEndBefore|_bind|imgAttrs|insertBefore|_eachEditor|position|loading|urlBox|textNode|justifyleft|unlink|last|rowsBox|_isArray|pageX|nodes|wrap|setOption|fontStyle|after|tab|removePicker|justifycenter|_isEmptyNode|justifyright|justifyfull|20px|flash|pathname|param|createMenu|hilitecolor|typeof|insertunorderedlist|KEditor|redo|undo|insertorderedlist|forecolor|heightTypeBox|nextCell|stack|iconClass|_instances|htmlList|se|insertCell|childProto|closeBtn|_toCamel|_handlers|default|_END_TO_START|_START_TO_END|paddingBox|spacingBox|toggle|addItem|_elementVal|000000|_unescape|_START_TO_START|clone|tabIndex|KCmd|widthTypeBox|yes|urlType|KDialog|mask||__kindeditor_temp_tag__|gif|rm|_unbind|newRange||temp|childA|beforeDrag||strong|altKey|arg|order|KEdit|clientWidth|moveFn|domain|KMenu|repeat|itemDiv|about|opacity|strikethrough|selectedColor|pageY|_formatUrl|KColorPicker|documentElement|noBtn|prevNode|_moveToElementText|shortcutKeys|blockquote|open|stop|fontStyleMap|meta|wellFormatted|filterMode|toHex|getAttribute|quickformat|isStyle|backgroundColor|typeBox|copyRange|_eventExpendo|location|isDelete|max|isDocumentDomain|colsBox|charCode|scroll|isSingle|preview|inner|proto|fromStack|splitTextNode|shrink|cond|percent|small|keydown|incEnd|paths|_unbindEvent|readyFunc|ready|attachEvent|reg|_scrollPos|keyup|_removeTempTag|mousemove|camelKey|lineheight|posB|_toHex|pointRange|disableMode|disableAll|_isFunction|mouseover|splitText|media|_getAttr|subscript|superscript|startBefore|clearhtml|object|mouseout|_contains|previousSibling|cut|blockMap|vertical|_NOSPLIT_TAG_MAP|tempForm|prop|setStartAfter|load|nameBox|_fullscreenResizeHandler|endSlash|initContent|upload|incStart|up|_initColorPicker|_queryAll|posA|log|parentDialog|centerNode|KToolbar|inserthtml|fileBox|count|kindeditor|checkSize|_drag|_menu|centerLineMode|outline|quickMode|_toArray|minWidth|_hasAttrOrCss|toBlock|constructor|parentElement|merged|setEndPoint|nodeCount|parentB|parentsA|_copyAndDelete|_getInnerNode|_PRE_TAG_MAP|kscp|sb|sa|extractContents|copyWrapper|_getSel|hasDummy||isInline|parentA|kchild|styleMap|srcTag|mergedResults|prefix|_getId|unit|hex|fromElement|stripslashes|cmpTag|childFlag|nodeRange|duplicate|sibling|_setAttr|re|subResults|_hasClass|cmpStart|startDummy|contentDiv|previewBtn|msg|_widget|appendTo|811213|area|KUploadButton|outer|afterSelect|liList|maxWidth|_OPERA|shiftKey|clientX|string|readyState|dataType|json|file|tabindex|iframeFix|docs|replaceWith|_range|_removeAttrOrCss|endDummy|startAfter|endAfter|_direction|AAA|_itemMap|rightDiv|extraParams|_iframeDoc|mouseup|removeAttribute|_mousedownHandler|_CHANGE_KEY_MAP|_contextmenus|clientY|solid|_mediaEmbed|pasting||outdent|removeformat|alignDefault|bottom|image|pasteType|borderWidth|nextCellIndex|_getCellIndex|sync|nextRow|_VERSION|_ctrl|loadPlugin|keAlign|keWidth|updateState|URL|invalidUrl|150|th|New|Arial|alt|frameborder|iframeDoc|space|cellPadding|cellSpacing|pickerList|toUpperCase|contentWindow|15px|submit|keBorder|showMask|encodeURIComponent|firstDialog|_TIME|langPath|_loadStyle||_hasVal|links|_chopQuery|hover|_fire|_getNodeName|scrollTop||createTextRange|stylesheet|rel|_selectToolbar|_matches|walk|allFlag||leftDiv|Flash|write|_firstAddBookmark|_BLOCK_TAG_MAP|10px|scrollLeft|_SINGLE_TAG_MAP|_INLINE_TAG_MAP|nodeWrapper|hasU200b|_colorpicker|StartToStart|byId|byClass|needSplit|_mediaImg|_parseLangKey|moveToElementText|exprList|_mediaAttrs|tagA|byAttr|_loadScript|http|_inString|_json|toArray|timeoutHandler|invalidWidth|isChild|createlink|params|invalidBorder|invalidHeight|onreadystatechange|_singleKeyMap|lenA|rowSpanCount|_updateCollapsed|_getEndRange|StartToEnd|down|downPos||parents|tr2td|getParents|parentsB|afterError|fieldName|hiddenElements|setMaskIndex|||||FFFFFF||_cells|_addAttr|disable|rangeB||rangeA||diff|dialogDiv|readonly|colorTable|contains|_getStartEnd||upPos|content|alignLeft|_undoToRedo|autoPos|_redoStack|alignCenter|_removeAttrOrCssByKey|closeIcon|_addBookmarkToStack|iframeMask|alignRight|isLoading|_hasAttrOrCssByKey|alignBox|serialize|autoScroll|invalidRows|_BOOKMARK_ID|enlargePos|shadow|enable|extractNodes|nextNode|loadStyleMode|tableHeight|tableWidth|_getSetAttrDiv|_cmd|_STYLE_TAG_MAP|KEvent|_edit|basefont|_alignEl|_removeId|getElementById|_eventId|metaKey|keyCode|moveListener|upListener|selectListener|_toolbar|fontfamily||arguments|dummyParent|nextRowIndex|zIndex|changeFlag|charAt|ieReadyFunc|addEventListener|_readyFinished|scrollTo|resetHeight|toplevel||unselect|_INPUT_KEY_MAP|CN|setHeight|_useCapture|zh|_create|_docMousedownFn|appendHtml|del|setAttr|getSelectedAnchor|sub|sup|strike|_lang|_plugin|resetBtn|page|design|initResize|deleteCell|_toRange|scrollHeight|hasForm|relatedTarget|createDocumentFragment|resizeType|kdoc|_nativeCommandValue|heights|large|verticalAlignBox|_clearMsWord|__|_dialog|cNode|menuHeight|insertRow|textAlignBox|msie|video|colinsert|scan|16px|tbody|u200B|enlarge|nowrap|rowinsert|prevItem|quot|getRealPath|addContextmenu||depth|_mediaType|marquee|getAncestorTagName|pSkipTagMap|opera|dir|getRelativePath|hasVal|colspan|cloneContents|EndToStart|keUrl|keType|linkType|60px|__kindeditor_temp_url__|trim|pasteHtml|lineHeight|_wrapNode|2em|deleteContents|getSelection|_getRng|_mergeAttrs|hideScroll|forceReset|selfWindow|loop|fullHtml|formatHtml|708px|||||||_mergeWrapper|400px|dialogAlignType|Object|address|autostart|quality|_blank|lastA|newWindow|toMap|rowspan|getFirstChild|createRange|controls|allowscriptaccess|removeUnit|tags|Option|offsetHeight||FFF|_inPreElement|alignMiddle|_FILL_ATTR_MAP|colinsertright|isindex|kindeditor_|colinsertleft|face|fontSizeList|rowinsertbelow|relative|rowinsertabove|xx|emsp|insert|ins|Function|_addParam|_tmpl|_GET_SET_ATTRIBUTE|_formatCss|cellHeight|blankPath|__kindeditor_pre_newline__|xml|realaudio|_mediaClass|rowmerge|_ready|big|tfoot|rowdelete|clientLeft|clientTop|toElement|_EVENT_PROPS|bgcolor|_getBasePath|_CURSORMOVE_KEY_MAP|removeEventListener|detachEvent|109|106|charCodeAt|rowsplit|ieReadyStateFunc||doScroll|||colmerge|colsplit|_bindEvent|111|coldelete|_setId||kevent|_invalidUrl|tagB|cellprop|removeNodes|Array|isInsert|bordercolor|_K|alignTop|thead|otherNode|error|fire|continue|cellspacing|cellpadding|keRows|amp|toStart|compareEndPoints|picker|lenB|character|invalidSpacing|invalidPadding|kePadding|moveStart|alignBottom|_CONTROL_TAG_MAP|getComputedStyle|setAttribute|currentStyle|500|_VALUE_TAG_MAP|_query|tablecell|querySelectorAll|getElementsByClassName|cellWidth|byName|lenth|_NEWIE|keBgColor|getBoundingClientRect|_computedCss|empty|keepChilds|_originDisplay|alignBaseline|defaultVal|slice|_removeAttr|throw|_setHtml|hasControlRange|750|javascript|doctype|_uploadbutton|draggable|_editor|insertimage|666666|333333|Date||getTime|_createButton|core|unload|reset|fullscreenShortcut|_bindFocusEvent|round|syncType|scrollWidth|docWidth|docHeight|999999|CCCCCC|009900|006600|DEBUG|apply|FFE500|DFC5A4|E53333|E56600|FF9900|64451D|splice|99BB00|4C33E5|9933E5|CC33E5|EE33EE|003399|337FE5|B8D100|60D978|00D5FF|_bindTabEvent|_bindNewlineEvent|_getImageFromRange|_addClassToTag|afterTab|__kindeditor_paste__|afterFocus|afterBlur|absoluteUrl|net|toStack|XMLHttpRequest||Apple|brSkipTagMap|white|movePastedData|execCommand|useContextmenu|blank|_ajax|application|send|visible|kindsoft|_MOBILE|separator|panel|200|HTML|toolbarDiv|menuName|_bindContextmenuEvent|wellFormatMode|editDiv|selectedIndex|blocks|_tabs|utf|onload|www|_IOS|state|fontSizeTable|_fullscreenExecuted|layout|charset|flv|swf|Verdana|Tahoma|releaseCapture|selectstart|themes|hidefocus|word|Microsoft|iframeSrc|formatBlock|isFull|setWidth|Courier|Roman|inited|Times|alignEl|setCapture|Black|contentEditable|uploadError|_getInitHtml|5px||minChangeSize|fontName|_GB2312|asf|noDisableItemMap||protocol|keName|srcScript|rmvb|ctrl|hideContextmenu|webkit|u17b4|off|onunload||u00ad|189|Error|186|switch|Copyright|187|107|H4|complete|u0604|110|rgb|105|returnValue|readystatechange|cancelBubble|frameElement|errorDialog|u070f|hasOwnProperty|811211|u0600|deleteRow|u0000|DOMContentLoaded|medium|gecko|1981px|with|trident|khtml|mso|shockwave|applewebkit|com|worddocument|formatUrl|getCssList|clearMsWord|navigator|03|2016|mediaImg|mediaEmbed|getAttrList|mediaType|userAgent|mediaAttrs|audio|microsoft|u00A9|u00AE|u2003|u3000|_|compatMode|firefox|CSS1Compat|u17b5||All|__kindeditor_bookmark_|rights|iphone|ipad|schemas|mobile|ipod|170|reserved|350|contenteditable|534|u206f|defer|OPERA|ismap|multiple|WEBKIT|TIME|declare|colgroup|isFunction|compact|isArray|nohref|GECKO|noshade|substring|lastIndexOf|selectedHtml|noresize|bfnrt|VERSION|parse|JSON|eval|_AUTOCLOSE_TAG_MAP|inArray|extend|addParam|tt|abs|samp|kbd|acronym|bdo|cite|dfn|undef|applet|base|col|frame|inString|noframes|ver||fieldset|toCamel|frameset||addUnit|650|9px|originalTarget|offsetY|prevValue|relatedNode|screenX|offsetX|newValue|cancelable|currentTarget|detail|eventPhase|screenY|view|u202f|u2028|u200f|readonlyMode|u2060|abbr|wheelDelta|instances|ufff0|ufeff|bubbles|attrName|valign|wmode|insertimg|isDirty|32px|24px|12px|tmpl|14px|18px|isEmpty|0000|EditorClass|lastIndex|uffff|attrChange|BackgroundImageCache|222|188|190|192|219|u200c|language|tablerowinsertabove|tablerowinsertbelow||tablerowmerge||tablecolmerge|tablecolinsertright|header|tabledelete|footer|tablecolinsertleft|isNaN|tablerowsplit|uploadbutton|invalidImg|jpg|shift|bmp|pleaseSelectFile|tablerowdelete|tablecolsplit|UploadButtonClass|tablecoldelete|afterUpload|tableinsert|tablecellprop|editMedia|deleteMedia|SimSun|middle|wav|clearfix|change|FangSong_GB2312|NSimSun|keypress|DialogClass|void|260px|hideLoading|tableprop|showLoading|deleteAnchor|editAnchor|editLink|deleteLink|NodeClass|png|EndToEnd|mpg|START_TO_START|START_TO_END|avi|RangeClass|invalidCols|cutError|uploadSuccess|invalidJson|getSelectedLink|END_TO_END|mid|wmv|MenuClass|wma|mp3|getRangeAt||ToolbarClass|END_TO_START|queryCommandValue|rangeCount|dump|__kindeditor_bookmark_end_|pasteError|811214|float||kindeditor_upload_iframe_|post|enctype|invalidMedia|action|450|multipart|addRange|fragment|createControlRange|addElement|EditClass|__kindeditor_bookmark_start_|surroundContents|createTextNode|ColorPickerClass|copyError|removeAllRanges|KaiTi_GB2312|res|POST|through|getPropertyValue|responseText|defaultView|parentWindow|queryAll|outerHTML|fixed|status|_docHeight|GET|hasAttr|RegExp|scrollY|scrollX|_docWidth|mouseleave|ActiveXObject|pasteHTML|query|sdata|CmdClass|ajax|300|getElementsByName|supported|queryCommandSupported|not|loadStyle|loadScript|https|overflowY|setRequestHeader|Content|Type|urlencoded|autoheight|autoHeightMode|WidgetClass|XMLHTTP|insertfile|filemanager|editImage|deleteImage|queryCommandState|innerText|baseline|offsetLeft|offsetTop|offsetParent|replaceChild|editFlash|deleteFlash|dblclick|dashed|mouseenter|666|focusout|SimHei|add|focusin|YaHei|dotted|widget|contentDocument|Normal|multiimage|filter|alpha|textContent|offsetWidth|kindeditor_data_'.split('|'),0,{})); \ No newline at end of file diff --git a/public/js/ke/themes/common/anchor.gif b/public/js/ke/themes/common/anchor.gif new file mode 100644 index 0000000..61145ea Binary files /dev/null and b/public/js/ke/themes/common/anchor.gif differ diff --git a/public/js/ke/themes/common/blank.gif b/public/js/ke/themes/common/blank.gif new file mode 100644 index 0000000..5bfd67a Binary files /dev/null and b/public/js/ke/themes/common/blank.gif differ diff --git a/public/js/ke/themes/default/background.png b/public/js/ke/themes/default/background.png new file mode 100644 index 0000000..e59bd68 Binary files /dev/null and b/public/js/ke/themes/default/background.png differ diff --git a/public/js/ke/themes/default/default.css b/public/js/ke/themes/default/default.css new file mode 100644 index 0000000..0eee37e --- /dev/null +++ b/public/js/ke/themes/default/default.css @@ -0,0 +1,948 @@ +/* common */ +.ke-inline-block { + display: -moz-inline-stack; + display: inline-block; + vertical-align: middle; + zoom: 1; + *display: inline; +} +.ke-clearfix { + zoom: 1; +} +.ke-clearfix:after { + content: "."; + display: block; + clear: both; + font-size: 0; + height: 0; + line-height: 0; + visibility: hidden; +} +.ke-shadow { + box-shadow: 1px 1px 3px #A0A0A0; + -moz-box-shadow: 1px 1px 3px #A0A0A0; + -webkit-box-shadow: 1px 1px 3px #A0A0A0; + filter: progid:DXImageTransform.Microsoft.Shadow(color='#A0A0A0', Direction=135, Strength=3); + background-color: #F0F0EE; +} +.ke-menu a, +.ke-menu a:hover, +.ke-dialog a, +.ke-dialog a:hover { + color: #337FE5; + text-decoration: none; +} +/* icons */ +.ke-icon-source { + background-position: 0px 0px; + width: 16px; + height: 16px; +} +.ke-icon-preview { + background-position: 0px -16px; + width: 16px; + height: 16px; +} +.ke-icon-print { + background-position: 0px -32px; + width: 16px; + height: 16px; +} +.ke-icon-undo { + background-position: 0px -48px; + width: 16px; + height: 16px; +} +.ke-icon-redo { + background-position: 0px -64px; + width: 16px; + height: 16px; +} +.ke-icon-cut { + background-position: 0px -80px; + width: 16px; + height: 16px; +} +.ke-icon-copy { + background-position: 0px -96px; + width: 16px; + height: 16px; +} +.ke-icon-paste { + background-position: 0px -112px; + width: 16px; + height: 16px; +} +.ke-icon-selectall { + background-position: 0px -128px; + width: 16px; + height: 16px; +} +.ke-icon-justifyleft { + background-position: 0px -144px; + width: 16px; + height: 16px; +} +.ke-icon-justifycenter { + background-position: 0px -160px; + width: 16px; + height: 16px; +} +.ke-icon-justifyright { + background-position: 0px -176px; + width: 16px; + height: 16px; +} +.ke-icon-justifyfull { + background-position: 0px -192px; + width: 16px; + height: 16px; +} +.ke-icon-insertorderedlist { + background-position: 0px -208px; + width: 16px; + height: 16px; +} +.ke-icon-insertunorderedlist { + background-position: 0px -224px; + width: 16px; + height: 16px; +} +.ke-icon-indent { + background-position: 0px -240px; + width: 16px; + height: 16px; +} +.ke-icon-outdent { + background-position: 0px -256px; + width: 16px; + height: 16px; +} +.ke-icon-subscript { + background-position: 0px -272px; + width: 16px; + height: 16px; +} +.ke-icon-superscript { + background-position: 0px -288px; + width: 16px; + height: 16px; +} +.ke-icon-date { + background-position: 0px -304px; + width: 25px; + height: 16px; +} +.ke-icon-time { + background-position: 0px -320px; + width: 25px; + height: 16px; +} +.ke-icon-formatblock { + background-position: 0px -336px; + width: 25px; + height: 16px; +} +.ke-icon-fontname { + background-position: 0px -352px; + width: 21px; + height: 16px; +} +.ke-icon-fontsize { + background-position: 0px -368px; + width: 23px; + height: 16px; +} +.ke-icon-forecolor { + background-position: 0px -384px; + width: 20px; + height: 16px; +} +.ke-icon-hilitecolor { + background-position: 0px -400px; + width: 23px; + height: 16px; +} +.ke-icon-bold { + background-position: 0px -416px; + width: 16px; + height: 16px; +} +.ke-icon-italic { + background-position: 0px -432px; + width: 16px; + height: 16px; +} +.ke-icon-underline { + background-position: 0px -448px; + width: 16px; + height: 16px; +} +.ke-icon-strikethrough { + background-position: 0px -464px; + width: 16px; + height: 16px; +} +.ke-icon-removeformat { + background-position: 0px -480px; + width: 16px; + height: 16px; +} +.ke-icon-image { + background-position: 0px -496px; + width: 16px; + height: 16px; +} +.ke-icon-flash { + background-position: 0px -512px; + width: 16px; + height: 16px; +} +.ke-icon-media { + background-position: 0px -528px; + width: 16px; + height: 16px; +} +.ke-icon-div { + background-position: 0px -544px; + width: 16px; + height: 16px; +} +.ke-icon-formula { + background-position: 0px -576px; + width: 16px; + height: 16px; +} +.ke-icon-hr { + background-position: 0px -592px; + width: 16px; + height: 16px; +} +.ke-icon-emoticons { + background-position: 0px -608px; + width: 16px; + height: 16px; +} +.ke-icon-link { + background-position: 0px -624px; + width: 16px; + height: 16px; +} +.ke-icon-unlink { + background-position: 0px -640px; + width: 16px; + height: 16px; +} +.ke-icon-fullscreen { + background-position: 0px -656px; + width: 16px; + height: 16px; +} +.ke-icon-about { + background-position: 0px -672px; + width: 16px; + height: 16px; +} +.ke-icon-plainpaste { + background-position: 0px -704px; + width: 16px; + height: 16px; +} +.ke-icon-wordpaste { + background-position: 0px -720px; + width: 16px; + height: 16px; +} +.ke-icon-table { + background-position: 0px -784px; + width: 16px; + height: 16px; +} +.ke-icon-tablemenu { + background-position: 0px -768px; + width: 16px; + height: 16px; +} +.ke-icon-tableinsert { + background-position: 0px -784px; + width: 16px; + height: 16px; +} +.ke-icon-tabledelete { + background-position: 0px -800px; + width: 16px; + height: 16px; +} +.ke-icon-tablecolinsertleft { + background-position: 0px -816px; + width: 16px; + height: 16px; +} +.ke-icon-tablecolinsertright { + background-position: 0px -832px; + width: 16px; + height: 16px; +} +.ke-icon-tablerowinsertabove { + background-position: 0px -848px; + width: 16px; + height: 16px; +} +.ke-icon-tablerowinsertbelow { + background-position: 0px -864px; + width: 16px; + height: 16px; +} +.ke-icon-tablecoldelete { + background-position: 0px -880px; + width: 16px; + height: 16px; +} +.ke-icon-tablerowdelete { + background-position: 0px -896px; + width: 16px; + height: 16px; +} +.ke-icon-tablecellprop { + background-position: 0px -912px; + width: 16px; + height: 16px; +} +.ke-icon-tableprop { + background-position: 0px -928px; + width: 16px; + height: 16px; +} +.ke-icon-checked { + background-position: 0px -944px; + width: 16px; + height: 16px; +} +.ke-icon-code { + background-position: 0px -960px; + width: 16px; + height: 16px; +} +.ke-icon-map { + background-position: 0px -976px; + width: 16px; + height: 16px; +} +.ke-icon-baidumap { + background-position: 0px -976px; + width: 16px; + height: 16px; +} +.ke-icon-lineheight { + background-position: 0px -992px; + width: 16px; + height: 16px; +} +.ke-icon-clearhtml { + background-position: 0px -1008px; + width: 16px; + height: 16px; +} +.ke-icon-pagebreak { + background-position: 0px -1024px; + width: 16px; + height: 16px; +} +.ke-icon-insertfile { + background-position: 0px -1040px; + width: 16px; + height: 16px; +} +.ke-icon-quickformat { + background-position: 0px -1056px; + width: 16px; + height: 16px; +} +.ke-icon-template { + background-position: 0px -1072px; + width: 16px; + height: 16px; +} +.ke-icon-tablecellsplit { + background-position: 0px -1088px; + width: 16px; + height: 16px; +} +.ke-icon-tablerowmerge { + background-position: 0px -1104px; + width: 16px; + height: 16px; +} +.ke-icon-tablerowsplit { + background-position: 0px -1120px; + width: 16px; + height: 16px; +} +.ke-icon-tablecolmerge { + background-position: 0px -1136px; + width: 16px; + height: 16px; +} +.ke-icon-tablecolsplit { + background-position: 0px -1152px; + width: 16px; + height: 16px; +} +.ke-icon-anchor { + background-position: 0px -1168px; + width: 16px; + height: 16px; +} +.ke-icon-search { + background-position: 0px -1184px; + width: 16px; + height: 16px; +} +.ke-icon-new { + background-position: 0px -1200px; + width: 16px; + height: 16px; +} +.ke-icon-specialchar { + background-position: 0px -1216px; + width: 16px; + height: 16px; +} +.ke-icon-multiimage { + background-position: 0px -1232px; + width: 16px; + height: 16px; +} +.ke-icon-zijianju { + background-position: 0px -1248px; + width: 16px; + height: 16px; +} +.ke-icon-duansuo { + background-position: 0px -1264px; + width: 16px; + height: 16px; +} +/* container */ +.ke-container { + display: block; + /*border: 1px solid #CCCCCC;*/ + background-color: #FFF; + overflow: hidden; + padding: 0; +} +/* toolbar */ +.ke-toolbar { + border-bottom: 1px solid #CCC; + background-color: #F0F0EE; + text-align: left; + overflow: hidden; + zoom: 1; +} +.ke-toolbar-icon { + background-repeat: no-repeat; + font-size: 0; + line-height: 0; + overflow: hidden; + display: block; +} +.ke-toolbar-icon-url { + background-image: url(default.png); +} +.ke-toolbar .ke-outline { + border: 1px solid #F0F0EE; + /*margin: 1px;*/ + margin: 8px 9px; + padding: 1px 2px; + font-size: 0; + line-height: 0; + overflow: hidden; + cursor: pointer; + display: block; + float: left; +} +.ke-toolbar .ke-on { + border: 1px solid #5690D2; +} +.ke-toolbar .ke-selected { + border: 1px solid #5690D2; + background-color: #E9EFF6; +} +.ke-toolbar .ke-disabled { + cursor: default; +} +.ke-toolbar .ke-separator { + height: 16px; + margin: 2px 3px; + border-left: 1px solid #A0A0A0; + border-right: 1px solid #FFFFFF; + border-top:0; + border-bottom:0; + width: 0; + font-size: 0; + line-height: 0; + overflow: hidden; + display: block; + float: left; +} +.ke-toolbar .ke-hr { + overflow: hidden; + height: 1px; + clear: both; +} +/* edit */ +.ke-edit { + padding: 0; +} +.ke-edit-iframe, +.ke-edit-textarea { + border: 0; + margin: 0; + padding: 0; + overflow: auto; +} +.ke-edit-textarea { + font: 12px/1.5 "Consolas", "Monaco", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; + color: #000; + overflow: auto; + resize: none; +} +.ke-edit-textarea:focus { + outline: none; +} +/* statusbar */ +.ke-statusbar { + position: relative; + background-color: #F0F0EE; + border-top: 1px solid #CCCCCC; + font-size: 0; + line-height: 0; + *height: 12px; + overflow: hidden; + text-align: center; + cursor: s-resize; +} +.ke-statusbar-center-icon { + background-position: -0px -754px; + width: 15px; + height: 11px; + background-image: url(default.png); +} +.ke-statusbar-right-icon { + position: absolute; + right: 0; + bottom: 0; + cursor: se-resize; + background-position: -5px -741px; + width: 11px; + height: 11px; + background-image: url(default.png); +} +/* menu */ +.ke-menu { + border: 1px solid #A0A0A0; + background-color: #F1F1F1; + color: #222222; + padding: 2px; + font-family: "sans serif",tahoma,verdana,helvetica; + font-size: 12px; + text-align: left; + overflow: hidden; +} +.ke-menu-item { + border: 1px solid #F1F1F1; + background-color: #F1F1F1; + color: #222222; + height: 24px; + overflow: hidden; + cursor: pointer; +} +.ke-menu-item-on { + border: 1px solid #5690D2; + background-color: #E9EFF6; +} +.ke-menu-item-left { + width: 27px; + text-align: center; + overflow: hidden; +} +.ke-menu-item-center { + width: 0; + height: 24px; + border-left: 1px solid #E3E3E3; + border-right: 1px solid #FFFFFF; + border-top: 0; + border-bottom: 0; +} +.ke-menu-item-center-on { + border-left: 1px solid #E9EFF6; + border-right: 1px solid #E9EFF6; +} +.ke-menu-item-right { + border: 0; + padding: 0 0 0 5px; + line-height: 24px; + text-align: left; + overflow: hidden; +} +.ke-menu-separator { + margin: 2px 0; + height: 0; + overflow: hidden; + border-top: 1px solid #CCCCCC; + border-bottom: 1px solid #FFFFFF; + border-left: 0; + border-right: 0; +} +/* colorpicker */ +.ke-colorpicker { + border: 1px solid #A0A0A0; + background-color: #F1F1F1; + color: #222222; + padding: 2px; +} +.ke-colorpicker-table { + border:0; + margin:0; + padding:0; + border-collapse: separate; +} +.ke-colorpicker-cell { + font-size: 0; + line-height: 0; + border: 1px solid #F0F0EE; + cursor: pointer; + margin:3px; + padding:0; +} +.ke-colorpicker-cell-top { + font-family: "sans serif",tahoma,verdana,helvetica; + font-size: 12px; + line-height: 24px; + border: 1px solid #F0F0EE; + cursor: pointer; + margin:0; + padding:0; + text-align: center; +} +.ke-colorpicker-cell-on { + border: 1px solid #5690D2; +} +.ke-colorpicker-cell-selected { + border: 1px solid #2446AB; +} +.ke-colorpicker-cell-color { + width: 14px; + height: 14px; + margin: 3px; + padding: 0; + border: 0; +} +/* dialog */ +.ke-dialog { + position: absolute; + margin: 0; + padding: 0; +} +.ke-dialog .ke-header { + width: 100%; + margin-bottom: 10px; +} +.ke-dialog .ke-header .ke-left { + float: left; +} +.ke-dialog .ke-header .ke-right { + float: right; +} +.ke-dialog .ke-header label { + margin-right: 0; + cursor: pointer; + font-weight: normal; + display: inline; + vertical-align: top; +} +.ke-dialog-content { + background-color: #FFF; + width: 100%; + height: 100%; + color: #333; + border: 1px solid #A0A0A0; +} +.ke-dialog-shadow { + position: absolute; + z-index: -1; + top: 0; + left: 0; + width: 100%; + height: 100%; + box-shadow: 3px 3px 7px #999; + -moz-box-shadow: 3px 3px 7px #999; + -webkit-box-shadow: 3px 3px 7px #999; + filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius='3', MakeShadow='true', ShadowOpacity='0.4'); + background-color: #F0F0EE; +} +.ke-dialog-header { + border:0; + margin:0; + padding: 0 10px; + background: url(background.png) repeat scroll 0 0 #F0F0EE; + border-bottom: 1px solid #CFCFCF; + height: 24px; + font: 12px/24px "sans serif",tahoma,verdana,helvetica; + text-align: left; + color: #222; + cursor: move; +} +.ke-dialog-icon-close { + display: block; + background: url(default.png) no-repeat scroll 0px -688px; + width: 16px; + height: 16px; + position: absolute; + right: 6px; + top: 6px; + cursor: pointer; +} +.ke-dialog-body { + font: 12px/1.5 "sans serif",tahoma,verdana,helvetica; + text-align: left; + overflow: hidden; + width: 100%; +} +.ke-dialog-body textarea { + display: block; + overflow: auto; + padding: 0; + resize: none; +} +.ke-dialog-body textarea:focus, +.ke-dialog-body input:focus, +.ke-dialog-body select:focus { + outline: none; +} +.ke-dialog-body label { + margin-right: 10px; + cursor: pointer; + display: -moz-inline-stack; + display: inline-block; + vertical-align: middle; + zoom: 1; + *display: inline; +} +.ke-dialog-body img { + display: -moz-inline-stack; + display: inline-block; + vertical-align: middle; + zoom: 1; + *display: inline; +} +.ke-dialog-body select { + display: -moz-inline-stack; + display: inline-block; + vertical-align: middle; + zoom: 1; + *display: inline; + width: auto; +} +.ke-dialog-body .ke-textarea { + display: block; + width: 408px; + height: 260px; + font-family: "sans serif",tahoma,verdana,helvetica; + font-size: 12px; + border-color: #848484 #E0E0E0 #E0E0E0 #848484; + border-style: solid; + border-width: 1px; +} +.ke-dialog-body .ke-form { + margin: 0; + padding: 0; +} +.ke-dialog-loading { + position: absolute; + top: 0; + left: 1px; + z-index: 1; + text-align: center; +} +.ke-dialog-loading-content { + background: url("../common/loading.gif") no-repeat; + color: #666; + font-size: 14px; + font-weight: bold; + height: 31px; + line-height: 31px; + padding-left: 36px; +} +.ke-dialog-row { + margin-bottom: 10px; +} +.ke-dialog-footer { + font: 12px/1 "sans serif",tahoma,verdana,helvetica; + text-align: right; + padding:0 0 5px 0; + background-color: #FFF; + width: 100%; +} +.ke-dialog-preview, +.ke-dialog-yes { + margin: 5px; +} +.ke-dialog-no { + margin: 5px 10px 5px 5px; +} +.ke-dialog-mask { + background-color:#FFF; + filter:alpha(opacity=50); + opacity:0.5; +} +.ke-button-common { + background: url(background.png) no-repeat; + cursor: pointer; + height: 23px; + line-height: 23px; + overflow: visible; + display: inline-block; + vertical-align: top; + cursor: pointer; +} +.ke-button-outer { + background-position: 0 -25px; + padding: 0; + display: -moz-inline-stack; + display: inline-block; + vertical-align: middle; + zoom: 1; + *display: inline; +} +.ke-button { + background-position: right -25px; + padding: 0 14px 0 12px; + margin: 0 0 0 2px; + font-family: "sans serif",tahoma,verdana,helvetica; + border: 0 none; + color: #333; + font-size: 12px; + text-decoration: none; +} +/* inputbox */ +.ke-input-text { + background-color:#FFFFFF; + font-family: "sans serif",tahoma,verdana,helvetica; + font-size: 12px; + line-height: 17px; + height: 17px; + padding: 2px 4px; + border-color: #848484 #E0E0E0 #E0E0E0 #848484; + border-style: solid; + border-width: 1px; + display: -moz-inline-stack; + display: inline-block; + vertical-align: middle; + zoom: 1; + *display: inline; +} +.ke-input-number { + width: 50px; +} +.ke-input-color { + border: 1px solid #A0A0A0; + background-color: #FFFFFF; + font-size: 12px; + width: 60px; + height: 20px; + line-height: 20px; + padding-left: 5px; + overflow: hidden; + cursor: pointer; + display: -moz-inline-stack; + display: inline-block; + vertical-align: middle; + zoom: 1; + *display: inline; +} +.ke-upload-button { + position: relative; +} +.ke-upload-area { + position: relative; + overflow: hidden; + margin: 0; + padding: 0; + *height: 25px; +} +.ke-upload-area .ke-upload-file { + position: absolute; + font-size: 60px; + top: 0; + right: 0; + padding: 0; + margin: 0; + z-index: 811212; + border: 0 none; + opacity: 0; + filter: alpha(opacity=0); +} +/* tabs */ +.ke-tabs { + font: 12px/1 "sans serif",tahoma,verdana,helvetica; + border-bottom:1px solid #A0A0A0; + padding-left:5px; + margin-bottom:20px; +} +.ke-tabs-ul { + list-style-image:none; + list-style-position:outside; + list-style-type:none; + margin:0; + padding:0; +} +.ke-tabs-li { + position: relative; + border: 1px solid #A0A0A0; + background-color: #F0F0EE; + margin: 0 2px -1px 0; + padding: 0 20px; + float: left; + line-height: 25px; + text-align: center; + color: #555555; + cursor: pointer; +} +.ke-tabs-li-selected { + background-color: #FFF; + border-bottom: 1px solid #FFF; + color: #000; + cursor: default; +} +.ke-tabs-li-on { + background-color: #FFF; + color: #000; +} +/* progressbar */ +.ke-progressbar { + position: relative; + margin: 0; + padding: 0; +} +.ke-progressbar-bar { + border: 1px solid #6FA5DB; + width: 80px; + height: 5px; + margin: 10px 10px 0 10px; + padding: 0; +} +.ke-progressbar-bar-inner { + width: 0; + height: 5px; + background-color: #6FA5DB; + overflow: hidden; + margin: 0; + padding: 0; +} +.ke-progressbar-percent { + position: absolute; + top: 0; + left: 40%; + display: none; +} \ No newline at end of file diff --git a/public/js/ke/themes/default/default.png b/public/js/ke/themes/default/default.png new file mode 100644 index 0000000..4830e9d Binary files /dev/null and b/public/js/ke/themes/default/default.png differ diff --git a/public/js/lay-config.js b/public/js/lay-config.js new file mode 100644 index 0000000..0664223 --- /dev/null +++ b/public/js/lay-config.js @@ -0,0 +1,58 @@ +/** + * date:2019/08/16 + * author:Mr.Chung + * description:此处放layui自定义扩展 + * version:2.0.4 + */ + +window.rootPath = (function (src) { + src = document.scripts[document.scripts.length - 1].src; + return src.substring(0, src.lastIndexOf("/") + 1); +})(); + +layui.config({ + base: rootPath + "lay-module/", + version: true +}).extend({ + xphp: "layuimini/xphp", // layuimini后台扩展 + miniAdmin: "layuimini/miniAdmin", // layuimini后台扩展 + miniMenu: "layuimini/miniMenu", // layuimini菜单扩展 + miniTab: "layuimini/miniTab", // layuimini tab扩展 + miniTheme: "layuimini/miniTheme", // layuimini 主题扩展 + step: 'step-lay/step', // 分步表单扩展 + treetable: 'treetable-lay/treetable', //table树形扩展 + tableSelect: 'tableSelect/tableSelect', // table选择扩展 + echarts: 'echarts/echarts', // echarts图表扩展 + echartsTheme: 'echarts/echartsTheme', // echarts图表主题扩展 + wangEditor: 'wangEditor/wangEditor', // wangEditor富文本扩展 + layarea: 'layarea/layarea', // 省市县区三级联动下拉选择器 + selectM: 'selectM/selectM',//下拉多选 + selectN: 'selectN/selectN',//多个select循环无限下级选 + numinput: 'numinput/numinput',//数字输入 + regionCheckBox: 'regionCheckBox/regionCheckBox',//省市县选择 + openTable: 'openTable/openTable',//用途不大 + iPicker: 'ipicker/iPicker',//多个panel无限循环下级多选 + layselect: 'layselect/layselect',//下拉单选,支持联动 + selectY: 'selectY/selectY',//只需要pid,id,name,就可以无限级选择 + selectY2: 'selectY/selectY2',//只需要pid,id,name,就可以无限级选择 + tableFilter: 'tablefilter/tableFilter',//列数值筛选 + sliderVerify: 'sliderVerify/sliderVerify',//滑动验证 + cascader: 'cascader/cascader',//级联选择组件 + checkbox: 'checkbox/checkbox',//多选美化 + dropdownTable: 'dropdownTable/dropdownTable',//下拉表格选择组件 + hashes: 'hashes/hashes',//hash:md5 sha1 sha256 sha512 crc32 + inputTag: 'inputTag/inputTag',//标签输入 + jmSheet: 'jmSheet/jmSheet',//底部弹出sheet + location: 'location/location',//百度地图经纬度 + locationX: 'location/locationX',//百度地图经纬度x + tableMerge: 'tableMerge/tableMerge',//表格列合并 + textool: 'textool/textool.min',//文本输入工具 + moreMenus: 'moreMenus/moreMenus',//更多/隐藏 + sliderTime: 'sliderTime/sliderTime',//时间范围选择 + tableEdit: 'tableTree/tableEdit',//表格编辑 + tableTree: 'tableTree/tableTree',//表格树 + transferTable: 'transferTable/transferTable',//穿梭 + notice: "notice/notice", // layuimini后台扩展 + mods: "mods/mods", // jsannotice + passwordIntensity: "passwordIntensity/passwordIntensity", // jsannotice +}); \ No newline at end of file diff --git a/public/js/lay-module/cascader/cascader.css b/public/js/lay-module/cascader/cascader.css new file mode 100644 index 0000000..d54f99e --- /dev/null +++ b/public/js/lay-module/cascader/cascader.css @@ -0,0 +1,1653 @@ +.el-cascader-panel .layui-icon,.el-cascader .layui-icon{ + font-size: 12px; +} + +/* layui表单验证不通过颜色 */ +.layui-form-danger+.el-cascader .el-input__inner{ + border-color: #FF5722!important; +} + +/* 在layui表单中宽度自动撑满 */ +.layui-form .el-cascader{ + width: 100%; +} + +/*弹出面板*/ +.el-popper .popper__arrow,.el-popper .popper__arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid +} + +.el-popper .popper__arrow { + border-width: 6px; + filter: drop-shadow(0 2px 12px rgba(0,0,0,.03)) +} + +.el-popper .popper__arrow:after { + content: " "; + border-width: 6px +} + +.el-popper[x-placement^=top] { + margin-bottom: 12px +} + +.el-popper[x-placement^=top] .popper__arrow { + bottom: -6px; + left: 50%; + margin-right: 3px; + border-top-color: #ebeef5; + border-bottom-width: 0 +} + +.el-popper[x-placement^=top] .popper__arrow:after { + bottom: 1px; + margin-left: -6px; + border-top-color: #fff; + border-bottom-width: 0 +} + +.el-popper[x-placement^=bottom] { + margin-top: 12px +} + +.el-popper[x-placement^=bottom] .popper__arrow { + top: -6px; + left: 50%; + margin-right: 3px; + border-top-width: 0; + border-bottom-color: #ebeef5 +} + +.el-popper[x-placement^=bottom] .popper__arrow:after { + top: 1px; + margin-left: -6px; + border-top-width: 0; + border-bottom-color: #fff +} + +.el-popper[x-placement^=right] { + margin-left: 12px +} + +.el-popper[x-placement^=right] .popper__arrow { + top: 50%; + left: -6px; + margin-bottom: 3px; + border-right-color: #ebeef5; + border-left-width: 0 +} + +.el-popper[x-placement^=right] .popper__arrow:after { + bottom: -6px; + left: 1px; + border-right-color: #fff; + border-left-width: 0 +} + +.el-popper[x-placement^=left] { + margin-right: 12px +} + +.el-popper[x-placement^=left] .popper__arrow { + top: 50%; + right: -6px; + margin-bottom: 3px; + border-right-width: 0; + border-left-color: #ebeef5 +} + +.el-popper[x-placement^=left] .popper__arrow:after { + right: 1px; + bottom: -6px; + margin-left: -6px; + border-right-width: 0; + border-left-color: #fff +} + +.el-tag { + background-color: #ecf5ff; + display: inline-block; + height: 32px; + padding: 0 10px; + line-height: 30px; + font-size: 12px; + color: #409eff; + border: 1px solid #d9ecff; + border-radius: 4px; + box-sizing: border-box; + white-space: nowrap +} + +.el-tag.is-hit { + border-color: #409eff +} + +.el-tag .el-tag__close { + color: #409eff +} + +.el-tag .el-tag__close:hover { + color: #fff; + background-color: #409eff +} + +.el-tag.el-tag--info { + background-color: #f4f4f5; + border-color: #e9e9eb; + color: #909399 +} + +.el-tag.el-tag--info.is-hit { + border-color: #909399 +} + +.el-tag.el-tag--info .el-tag__close { + color: #909399 +} + +.el-tag.el-tag--info .el-tag__close:hover { + color: #fff; + background-color: #909399 +} + +.el-tag.el-tag--success { + background-color: #f0f9eb; + border-color: #e1f3d8; + color: #67c23a +} + +.el-tag.el-tag--success.is-hit { + border-color: #67c23a +} + +.el-tag.el-tag--success .el-tag__close { + color: #67c23a +} + +.el-tag.el-tag--success .el-tag__close:hover { + color: #fff; + background-color: #67c23a +} + +.el-tag.el-tag--warning { + background-color: #fdf6ec; + border-color: #faecd8; + color: #e6a23c +} + +.el-tag.el-tag--warning.is-hit { + border-color: #e6a23c +} + +.el-tag.el-tag--warning .el-tag__close { + color: #e6a23c +} + +.el-tag.el-tag--warning .el-tag__close:hover { + color: #fff; + background-color: #e6a23c +} + +.el-tag.el-tag--danger { + background-color: #fef0f0; + border-color: #fde2e2; + color: #f56c6c +} + +.el-tag.el-tag--danger.is-hit { + border-color: #f56c6c +} + +.el-tag.el-tag--danger .el-tag__close { + color: #f56c6c +} + +.el-tag.el-tag--danger .el-tag__close:hover { + color: #fff; + background-color: #f56c6c +} + +.el-tag .el-icon-close { + border-radius: 50%; + text-align: center; + position: relative; + cursor: pointer; + font-size: 12px; + height: 16px; + width: 16px; + line-height: 16px; + vertical-align: middle; + top: -1px; + right: -5px +} + +.el-tag .el-icon-close:before { + display: block +} + +.el-tag--dark { + background-color: #409eff; + color: #fff +} + +.el-tag--dark,.el-tag--dark.is-hit { + border-color: #409eff +} + +.el-tag--dark .el-tag__close { + color: #fff +} + +.el-tag--dark .el-tag__close:hover { + color: #fff; + background-color: #66b1ff +} + +.el-tag--dark.el-tag--info { + background-color: #909399; + border-color: #909399; + color: #fff +} + +.el-tag--dark.el-tag--info.is-hit { + border-color: #909399 +} + +.el-tag--dark.el-tag--info .el-tag__close { + color: #fff +} + +.el-tag--dark.el-tag--info .el-tag__close:hover { + color: #fff; + background-color: #a6a9ad +} + +.el-tag--dark.el-tag--success { + background-color: #67c23a; + border-color: #67c23a; + color: #fff +} + +.el-tag--dark.el-tag--success.is-hit { + border-color: #67c23a +} + +.el-tag--dark.el-tag--success .el-tag__close { + color: #fff +} + +.el-tag--dark.el-tag--success .el-tag__close:hover { + color: #fff; + background-color: #85ce61 +} + +.el-tag--dark.el-tag--warning { + background-color: #e6a23c; + border-color: #e6a23c; + color: #fff +} + +.el-tag--dark.el-tag--warning.is-hit { + border-color: #e6a23c +} + +.el-tag--dark.el-tag--warning .el-tag__close { + color: #fff +} + +.el-tag--dark.el-tag--warning .el-tag__close:hover { + color: #fff; + background-color: #ebb563 +} + +.el-tag--dark.el-tag--danger { + background-color: #f56c6c; + border-color: #f56c6c; + color: #fff +} + +.el-tag--dark.el-tag--danger.is-hit { + border-color: #f56c6c +} + +.el-tag--dark.el-tag--danger .el-tag__close { + color: #fff +} + +.el-tag--dark.el-tag--danger .el-tag__close:hover { + color: #fff; + background-color: #f78989 +} + +.el-tag--plain { + background-color: #fff; + border-color: #b3d8ff; + color: #409eff +} + +.el-tag--plain.is-hit { + border-color: #409eff +} + +.el-tag--plain .el-tag__close { + color: #409eff +} + +.el-tag--plain .el-tag__close:hover { + color: #fff; + background-color: #409eff +} + +.el-tag--plain.el-tag--info { + background-color: #fff; + border-color: #d3d4d6; + color: #909399 +} + +.el-tag--plain.el-tag--info.is-hit { + border-color: #909399 +} + +.el-tag--plain.el-tag--info .el-tag__close { + color: #909399 +} + +.el-tag--plain.el-tag--info .el-tag__close:hover { + color: #fff; + background-color: #909399 +} + +.el-tag--plain.el-tag--success { + background-color: #fff; + border-color: #c2e7b0; + color: #67c23a +} + +.el-tag--plain.el-tag--success.is-hit { + border-color: #67c23a +} + +.el-tag--plain.el-tag--success .el-tag__close { + color: #67c23a +} + +.el-tag--plain.el-tag--success .el-tag__close:hover { + color: #fff; + background-color: #67c23a +} + +.el-tag--plain.el-tag--warning { + background-color: #fff; + border-color: #f5dab1; + color: #e6a23c +} + +.el-tag--plain.el-tag--warning.is-hit { + border-color: #e6a23c +} + +.el-tag--plain.el-tag--warning .el-tag__close { + color: #e6a23c +} + +.el-tag--plain.el-tag--warning .el-tag__close:hover { + color: #fff; + background-color: #e6a23c +} + +.el-tag--plain.el-tag--danger { + background-color: #fff; + border-color: #fbc4c4; + color: #f56c6c +} + +.el-tag--plain.el-tag--danger.is-hit { + border-color: #f56c6c +} + +.el-tag--plain.el-tag--danger .el-tag__close { + color: #f56c6c +} + +.el-tag--plain.el-tag--danger .el-tag__close:hover { + color: #fff; + background-color: #f56c6c +} + +.el-tag--medium { + height: 28px; + line-height: 26px +} + +.el-tag--medium .el-icon-close { + transform: scale(.8) +} + +.el-tag--small { + height: 24px; + padding: 0 8px; + line-height: 22px +} + +.el-tag--small .el-icon-close { + transform: scale(.8) +} + +.el-tag--mini { + height: 20px; + padding: 0 5px; + line-height: 19px +} + +.el-tag--mini .el-icon-close { + margin-left: -3px; + transform: scale(.7) +} + +.el-checkbox { + color: #606266; + font-weight: 500; + font-size: 14px; + position: relative; + cursor: pointer; + display: inline-block; + white-space: nowrap; + user-select: none; + margin-right: 30px +} + +.el-checkbox.is-bordered { + padding: 9px 20px 9px 10px; + border-radius: 4px; + border: 1px solid #dcdfe6; + box-sizing: border-box; + line-height: normal; + height: 40px +} + +.el-checkbox.is-bordered.is-checked { + border-color: #409eff +} + +.el-checkbox.is-bordered.is-disabled { + border-color: #ebeef5; + cursor: not-allowed +} + +.el-checkbox.is-bordered+.el-checkbox.is-bordered { + margin-left: 10px +} + +.el-checkbox.is-bordered.el-checkbox--medium { + padding: 7px 20px 7px 10px; + border-radius: 4px; + height: 36px +} + +.el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__label { + line-height: 17px; + font-size: 14px +} + +.el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__inner { + height: 14px; + width: 14px +} + +.el-checkbox.is-bordered.el-checkbox--small { + padding: 5px 15px 5px 10px; + border-radius: 3px; + height: 32px +} + +.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__label { + line-height: 15px; + font-size: 12px +} + +.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner { + height: 12px; + width: 12px +} + +.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner:after { + height: 6px; + width: 2px +} + +.el-checkbox.is-bordered.el-checkbox--mini { + padding: 3px 15px 3px 10px; + border-radius: 3px; + height: 28px +} + +.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__label { + line-height: 12px; + font-size: 12px +} + +.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner { + height: 12px; + width: 12px +} + +.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner:after { + height: 6px; + width: 2px +} + +.el-checkbox__input { + white-space: nowrap; + cursor: pointer; + outline: none; + display: inline-block; + line-height: 1; + position: relative; + vertical-align: middle +} + +.el-checkbox__input.is-disabled .el-checkbox__inner { + background-color: #edf2fc; + border-color: #dcdfe6; + cursor: not-allowed +} + +.el-checkbox__input.is-disabled .el-checkbox__inner:after { + cursor: not-allowed; + border-color: #c0c4cc +} + +.el-checkbox__input.is-disabled .el-checkbox__inner+.el-checkbox__label { + cursor: not-allowed +} + +.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner { + background-color: #f2f6fc; + border-color: #dcdfe6 +} + +.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner:after { + border-color: #c0c4cc +} + +.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner { + background-color: #f2f6fc; + border-color: #dcdfe6 +} + +.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner:before { + background-color: #c0c4cc; + border-color: #c0c4cc +} + +.el-checkbox__input.is-disabled+span.el-checkbox__label { + color: #c0c4cc; + cursor: not-allowed +} + +.el-checkbox__input.is-checked .el-checkbox__inner { + background-color: #409eff; + border-color: #409eff; +} + +.el-checkbox__input.is-checked .el-checkbox__inner:after { + transform: rotate(45deg) scaleY(1); +} + +.el-checkbox__input.is-checked+.el-checkbox__label { + color: #409eff +} + +.el-checkbox__input.is-focus .el-checkbox__inner { + border-color: #409eff +} + +.el-checkbox__input.is-indeterminate .el-checkbox__inner { + background-color: #409eff; + border-color: #409eff +} + +.el-checkbox__input.is-indeterminate .el-checkbox__inner:before { + content: ""; + position: absolute; + display: block; + background-color: #fff; + height: 2px; + transform: scale(.5); + left: 0; + right: 0; + top: 5px +} + +.el-checkbox__input.is-indeterminate .el-checkbox__inner:after { + display: none +} + +.el-checkbox__inner { + display: inline-block; + position: relative; + border: 1px solid #dcdfe6; + border-radius: 2px; + box-sizing: border-box; + width: 14px; + height: 14px; + background-color: #fff; + z-index: 1; + transition: border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46); +} + +.el-checkbox__inner:hover { + border-color: #409eff +} + +.el-checkbox__inner:after { + box-sizing: content-box; + content: ""; + border: 1px solid #fff; + border-left: 0; + border-top: 0; + height: 7px; + left: 4px; + position: absolute; + top: 1px; + transform: rotate(45deg) scaleY(0); + width: 3px; + transition: transform .15s ease-in .05s; + transform-origin: center +} + +.el-checkbox__original { + opacity: 0; + outline: none; + position: absolute; + margin: 0; + width: 0; + height: 0; + z-index: -1 +} + +.el-checkbox__label { + display: inline-block; + padding-left: 10px; + line-height: 19px; + font-size: 14px +} + +.el-checkbox:last-of-type { + margin-right: 0 +} + +.el-checkbox-button,.el-checkbox-button__inner { + position: relative; + display: inline-block +} + +.el-checkbox-button__inner { + line-height: 1; + font-weight: 500; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + background: #fff; + border: 1px solid #dcdfe6; + border-left: 0; + color: #606266; + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: none; + margin: 0; + transition: all .3s cubic-bezier(.645,.045,.355,1); + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + padding: 12px 20px; + font-size: 14px; + border-radius: 0 +} + +.el-checkbox-button__inner.is-round { + padding: 12px 20px +} + +.el-checkbox-button__inner:hover { + color: #409eff +} + +.el-checkbox-button__inner [class*=el-icon-] { + line-height: .9 +} + +.el-checkbox-button__inner [class*=el-icon-]+span { + margin-left: 5px +} + +.el-checkbox-button__original { + opacity: 0; + outline: none; + position: absolute; + margin: 0; + z-index: -1 +} + +.el-checkbox-button.is-checked .el-checkbox-button__inner { + color: #fff; + background-color: #409eff; + border-color: #409eff; + box-shadow: -1px 0 0 0 #8cc5ff +} + +.el-checkbox-button.is-checked:first-child .el-checkbox-button__inner { + border-left-color: #409eff +} + +.el-checkbox-button.is-disabled .el-checkbox-button__inner { + color: #c0c4cc; + cursor: not-allowed; + background-image: none; + background-color: #fff; + border-color: #ebeef5; + box-shadow: none +} + +.el-checkbox-button.is-disabled:first-child .el-checkbox-button__inner { + border-left-color: #ebeef5 +} + +.el-checkbox-button:first-child .el-checkbox-button__inner { + border-left: 1px solid #dcdfe6; + border-radius: 4px 0 0 4px; + box-shadow: none!important +} + +.el-checkbox-button.is-focus .el-checkbox-button__inner { + border-color: #409eff +} + +.el-checkbox-button:last-child .el-checkbox-button__inner { + border-radius: 0 4px 4px 0 +} + +.el-checkbox-button--medium .el-checkbox-button__inner { + padding: 10px 20px; + font-size: 14px; + border-radius: 0 +} + +.el-checkbox-button--medium .el-checkbox-button__inner.is-round { + padding: 10px 20px +} + +.el-checkbox-button--small .el-checkbox-button__inner { + padding: 9px 15px; + font-size: 12px; + border-radius: 0 +} + +.el-checkbox-button--small .el-checkbox-button__inner.is-round { + padding: 9px 15px +} + +.el-checkbox-button--mini .el-checkbox-button__inner { + padding: 7px 15px; + font-size: 12px; + border-radius: 0 +} + +.el-checkbox-button--mini .el-checkbox-button__inner.is-round { + padding: 7px 15px +} + +.el-checkbox-group { + font-size: 0 +} + +.el-radio { + color: #606266; + font-weight: 500; + line-height: 1; + position: relative; + cursor: pointer; + display: inline-block; + white-space: nowrap; + outline: none; + font-size: 14px; + margin-right: 30px; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none +} + +.el-radio.is-bordered { + padding: 12px 20px 0 10px; + border-radius: 4px; + border: 1px solid #dcdfe6; + box-sizing: border-box; + height: 40px +} + +.el-radio.is-bordered.is-checked { + border-color: #409eff +} + +.el-radio.is-bordered.is-disabled { + cursor: not-allowed; + border-color: #ebeef5 +} + +.el-radio.is-bordered+.el-radio.is-bordered { + margin-left: 10px +} + +.el-radio--medium.is-bordered { + padding: 10px 20px 0 10px; + border-radius: 4px; + height: 36px +} + +.el-radio--medium.is-bordered .el-radio__label { + font-size: 14px +} + +.el-radio--medium.is-bordered .el-radio__inner { + height: 14px; + width: 14px +} + +.el-radio--small.is-bordered { + padding: 8px 15px 0 10px; + border-radius: 3px; + height: 32px +} + +.el-radio--small.is-bordered .el-radio__label { + font-size: 12px +} + +.el-radio--small.is-bordered .el-radio__inner { + height: 12px; + width: 12px +} + +.el-radio--mini.is-bordered { + padding: 6px 15px 0 10px; + border-radius: 3px; + height: 28px +} + +.el-radio--mini.is-bordered .el-radio__label { + font-size: 12px +} + +.el-radio--mini.is-bordered .el-radio__inner { + height: 12px; + width: 12px +} + +.el-radio:last-child { + margin-right: 0 +} + +.el-radio__input { + white-space: nowrap; + cursor: pointer; + outline: none; + display: inline-block; + line-height: 1; + position: relative; + vertical-align: middle +} + +.el-radio__input.is-disabled .el-radio__inner { + background-color: #f5f7fa; + border-color: #e4e7ed; + cursor: not-allowed +} + +.el-radio__input.is-disabled .el-radio__inner:after { + cursor: not-allowed; + background-color: #f5f7fa +} + +.el-radio__input.is-disabled .el-radio__inner+.el-radio__label { + cursor: not-allowed +} + +.el-radio__input.is-disabled.is-checked .el-radio__inner { + background-color: #f5f7fa; + border-color: #e4e7ed +} + +.el-radio__input.is-disabled.is-checked .el-radio__inner:after { + background-color: #c0c4cc +} + +.el-radio__input.is-disabled+span.el-radio__label { + color: #c0c4cc; + cursor: not-allowed +} + +.el-radio__input.is-checked .el-radio__inner { + border-color: #409eff; + background: #409eff +} + +.el-radio__input.is-checked .el-radio__inner:after { + transform: translate(-50%,-50%) scale(1) +} + +.el-radio__input.is-checked+.el-radio__label { + color: #409eff +} + +.el-radio__input.is-focus .el-radio__inner { + border-color: #409eff +} + +.el-radio__inner { + border: 1px solid #dcdfe6; + border-radius: 100%; + width: 14px; + height: 14px; + background-color: #fff; + position: relative; + cursor: pointer; + display: inline-block; + box-sizing: border-box +} + +.el-radio__inner:hover { + border-color: #409eff +} + +.el-radio__inner:after { + width: 4px; + height: 4px; + border-radius: 100%; + background-color: #fff; + content: ""; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%,-50%) scale(0); + transition: transform .15s ease-in +} + +.el-radio__original { + opacity: 0; + outline: none; + position: absolute; + z-index: -1; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: 0 +} + +.el-radio:focus:not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner { + /*box-shadow: 0 0 2px 2px #409eff*/ +} + +/*滚动条*/ +.el-scrollbar { + overflow: hidden; + position: relative +} + +.el-scrollbar:active>.el-scrollbar__bar,.el-scrollbar:focus>.el-scrollbar__bar,.el-scrollbar:hover>.el-scrollbar__bar { + opacity: 1; + transition: opacity .34s ease-out +} + +.el-scrollbar__wrap { + overflow: scroll; + height: 100% +} + +.el-scrollbar__wrap--hidden-default { + scrollbar-width: none +} + +.el-scrollbar__wrap--hidden-default::-webkit-scrollbar { + width: 0; + height: 0 +} + +.el-scrollbar__thumb { + position: relative; + display: block; + width: 0; + height: 0; + cursor: pointer; + border-radius: inherit; + background-color: rgba(144,147,153,.3); + transition: background-color .3s +} + +.el-scrollbar__thumb:hover { + background-color: rgba(144,147,153,.5) +} + +.el-scrollbar__bar { + position: absolute; + right: 2px; + bottom: 2px; + z-index: 1; + border-radius: 4px; + opacity: 0; + transition: opacity .12s ease-out +} + +.el-scrollbar__bar.is-vertical { + width: 6px; + top: 2px +} + +.el-scrollbar__bar.is-vertical>div { + width: 100% +} + +.el-scrollbar__bar.is-horizontal { + height: 6px; + left: 2px +} + +.el-scrollbar__bar.is-horizontal>div { + height: 100% +} + +.el-cascader { + display: inline-block; + position: relative; + font-size: 14px; + line-height: 40px +} + +.el-cascader:not(.is-disabled):hover .el-input__inner { + cursor: pointer; + border-color: #c0c4cc +} + +.el-cascader .el-input { + cursor: pointer +} + +.el-cascader .el-input .el-input__inner { + text-overflow: ellipsis +} + +.el-cascader .el-input .el-input__inner:focus { + border-color: #409eff +} + +.el-cascader .el-input .el-icon-arrow-down { + display: inline-block; + transition: transform .3s; + font-size: 14px +} + +.el-cascader .el-input .el-icon-arrow-down.is-reverse { + transform: rotate(180deg) +} + +.el-cascader .el-input .el-icon-circle-close:hover { + color: #909399 +} + +.el-cascader .el-input.is-focus .el-input__inner { + border-color: #409eff +} + +.el-cascader--medium { + font-size: 14px; + line-height: 36px +} + +.el-cascader--small { + font-size: 13px; + line-height: 32px +} + +.el-cascader--mini { + font-size: 12px; + line-height: 28px +} + +.el-cascader.is-disabled .el-cascader__label { + z-index: 2; + color: #c0c4cc +} + +.el-cascader__dropdown { + margin: 5px 0; + font-size: 14px; + background: #fff; + border: 1px solid #e4e7ed; + border-radius: 4px; + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1) +} + +.el-cascader__tags { + position: absolute; + left: 0; + right: 30px; + top: 50%; + transform: translateY(-50%); + display: flex; + flex-wrap: wrap; + line-height: normal; + text-align: left; + box-sizing: border-box +} + +.el-cascader__tags .el-tag { + display: inline-flex; + align-items: center; + max-width: 100%; + margin: 2px 0 2px 6px; + text-overflow: ellipsis; + background: #f0f2f5 +} + +.el-cascader__tags .el-tag:not(.is-hit) { + border-color: transparent +} + +.el-cascader__tags .el-tag > span { + flex: 1; + overflow: hidden; + text-overflow: ellipsis +} + +.el-cascader__tags .el-tag .el-icon-close { + flex: none; + background-color: #c0c4cc; + color: #fff +} + +.el-cascader__tags .el-tag .el-icon-close:hover { + background-color: #909399 +} + +.el-cascader__suggestion-panel { + border-radius: 4px +} + +.el-cascader__suggestion-list { + max-height: 204px; + margin: 0; + padding: 6px 0; + font-size: 14px; + color: #606266; + text-align: center +} + +.el-cascader__suggestion-item { + display: flex; + justify-content: space-between; + align-items: center; + height: 34px; + padding: 0 15px; + text-align: left; + outline: none; + cursor: pointer +} + +.el-cascader__suggestion-item:focus, .el-cascader__suggestion-item:hover { + background: #f5f7fa +} + +.el-cascader__suggestion-item.is-checked { + color: #409eff; + font-weight: 700 +} + +.el-cascader__suggestion-item .el-icon-check{ + margin-right: 15px; +} + +.el-cascader__suggestion-item > span { + margin-right: 10px +} + +.el-cascader__empty-text { + margin: 10px 0; + color: #c0c4cc +} + +.el-cascader__search-input { + flex: 1; + height: 24px; + min-width: 60px; + margin: 2px 0 2px 15px; + padding: 0; + color: #606266; + border: none; + outline: none; + box-sizing: border-box +} + +.el-cascader__search-input::placeholder { + color: #c0c4cc +} + +/*cascader面板*/ +.el-cascader-panel { + display: flex; + border-radius: 4px; + font-size: 14px +} + +.el-cascader-panel.is-bordered { + border: 1px solid #e4e7ed; + border-radius: 4px +} + +.el-cascader-menu { + min-width: 180px; + box-sizing: border-box; + color: #606266; + border-right: 1px solid #e4e7ed +} + +.el-cascader-menu:last-child { + border-right: none +} + +.el-cascader-menu:last-child .el-cascader-node { + padding-right: 26px +} + +.el-cascader-menu__wrap { + height: 204px +} + +.el-cascader-menu__list { + position: relative; + min-height: 100%; + padding: 6px 0; + list-style: none; + box-sizing: border-box; +} + +.el-cascader-menu__hover-zone { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none +} + +.el-cascader-menu__empty-text { + position: absolute; + top: 50%; + left: 46%; + transform: translate(-50%, -50%); + text-align: center; + color: #c0c4cc +} + +.el-cascader-node { + position: relative; + display: flex; + align-items: center; + padding: 0 25px 0 15px; + height: 34px; + line-height: 34px; + outline: none +} + +.el-cascader-node.is-selectable.in-active-path { + color: #606266 +} + +.el-cascader-node.in-active-path, .el-cascader-node.is-active, .el-cascader-node.is-selectable.in-checked-path { + color: #409eff; + font-weight: 700 +} + +.el-cascader-node:not(.is-disabled) { + cursor: pointer +} + +.el-cascader-node:not(.is-disabled):focus, .el-cascader-node:not(.is-disabled):hover { + background: #f5f7fa +} + +.el-cascader-node.is-disabled { + color: #c0c4cc; + cursor: not-allowed +} + +.el-cascader-node__prefix { + position: absolute; + left: 10px +} + +.el-cascader-node__postfix { + position: absolute; + right: 10px +} + +.el-cascader-node__label { + flex: 1; + padding: 0 10px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis +} + +.el-cascader-node > .el-radio { + margin-right: 0 +} + +.el-cascader-node > .el-radio .el-radio__label { + padding-left: 0 +} +/*输入框*/ +.el-input { + position: relative; + font-size: 14px; + display: inline-block; + width: 100% +} + +.el-input::-webkit-scrollbar { + z-index: 11; + width: 6px +} + +.el-input::-webkit-scrollbar:horizontal { + height: 6px +} + +.el-input::-webkit-scrollbar-thumb { + border-radius: 5px; + width: 6px; + background: #b4bccc +} + +.el-input::-webkit-scrollbar-corner,.el-input::-webkit-scrollbar-track { + background: #fff +} + +.el-input::-webkit-scrollbar-track-piece { + background: #fff; + width: 6px +} + +.el-input .el-input__clear { + color: #c0c4cc; + font-size: 14px; + cursor: pointer; + transition: color .2s cubic-bezier(.645,.045,.355,1) +} + +.el-input .el-input__clear:hover { + color: #909399 +} + +.el-input .el-input__count { + height: 100%; + display: inline-flex; + align-items: center; + color: #909399; + font-size: 12px +} + +.el-input .el-input__count .el-input__count-inner { + background: #fff; + line-height: normal; + display: inline-block; + padding: 0 5px +} + +.el-input__inner { + -webkit-appearance: none; + background-color: #fff; + background-image: none; + border-radius: 4px; + border: 1px solid #dcdfe6; + box-sizing: border-box; + color: #606266; + display: inline-block; + font-size: inherit; + height: 40px; + line-height: 40px; + outline: none; + padding: 0 15px; + transition: border-color .2s cubic-bezier(.645,.045,.355,1); + width: 100% +} + +.el-input__inner::-ms-reveal { + display: none +} + +.el-input__inner::placeholder { + color: #c0c4cc +} + +.el-input__inner:hover { + border-color: #c0c4cc +} + +.el-input__inner:focus { + outline: none; + border-color: #409eff +} + +.el-input__suffix { + position: absolute; + height: 100%; + right: 5px; + top: 0; + text-align: center; + color: #c0c4cc; + transition: all .3s; + pointer-events: none; + display: flex; + flex-direction: column; + justify-content: center; +} + +.el-input__suffix-inner { + pointer-events: all +} + +.el-input__prefix { + position: absolute; + left: 5px; + top: 0; + color: #c0c4cc +} + +.el-input__icon,.el-input__prefix { + height: 100%; + text-align: center; + transition: all .3s +} + +.el-input__icon { + width: 25px; + line-height: 40px +} + +.el-input__icon:after { + content: ""; + height: 100%; + width: 0; + display: inline-block; + vertical-align: middle +} + +.el-input__validateIcon { + pointer-events: none +} + +.el-input.is-active .el-input__inner { + outline: none; + border-color: #409eff +} + +.el-input.is-disabled .el-input__inner { + background-color: #f5f7fa; + border-color: #e4e7ed; + color: #c0c4cc; + cursor: not-allowed +} + +.el-input.is-disabled .el-input__inner::placeholder { + color: #c0c4cc +} + +.el-input.is-disabled .el-input__icon { + cursor: not-allowed +} + +.el-input.is-exceed .el-input__inner { + border-color: #f56c6c +} + +.el-input.is-exceed .el-input__suffix .el-input__count { + color: #f56c6c +} + +.el-input--suffix .el-input__inner { + padding-right: 30px +} + +.el-input--prefix .el-input__inner { + padding-left: 30px +} + +.el-input--medium { + font-size: 14px +} + +.el-input--medium .el-input__inner { + height: 36px; + line-height: 36px +} + +.el-input--medium .el-input__icon { + line-height: 36px +} + +.el-input--small { + font-size: 13px +} + +.el-input--small .el-input__inner { + height: 32px; + line-height: 32px +} + +.el-input--small .el-input__icon { + line-height: 32px +} + +.el-input--mini { + font-size: 12px +} + +.el-input--mini .el-input__inner { + height: 28px; + line-height: 28px +} + +.el-input--mini .el-input__icon { + line-height: 28px +} + +.el-input-group { + line-height: normal; + display: inline-table; + width: 100%; + border-collapse: separate; + border-spacing: 0 +} + +.el-input-group>.el-input__inner { + vertical-align: middle; + display: table-cell +} + +.el-input-group__append,.el-input-group__prepend { + background-color: #f5f7fa; + color: #909399; + vertical-align: middle; + display: table-cell; + position: relative; + border: 1px solid #dcdfe6; + border-radius: 4px; + padding: 0 20px; + width: 1px; + white-space: nowrap +} + +.el-input-group__append:focus,.el-input-group__prepend:focus { + outline: none +} + +.el-input-group__append .el-button,.el-input-group__append .el-select,.el-input-group__prepend .el-button,.el-input-group__prepend .el-select { + display: inline-block; + margin: -10px -20px +} + +.el-input-group__append button.el-button,.el-input-group__append div.el-select .el-input__inner,.el-input-group__append div.el-select:hover .el-input__inner,.el-input-group__prepend button.el-button,.el-input-group__prepend div.el-select .el-input__inner,.el-input-group__prepend div.el-select:hover .el-input__inner { + border-color: transparent; + background-color: transparent; + color: inherit; + border-top: 0; + border-bottom: 0 +} + +.el-input-group__append .el-button,.el-input-group__append .el-input,.el-input-group__prepend .el-button,.el-input-group__prepend .el-input { + font-size: inherit +} + +.el-input-group__prepend { + border-right: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.el-input-group__append { + border-left: 0 +} + +.el-input-group--prepend .el-input__inner,.el-input-group__append { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.el-input-group--prepend .el-select .el-input.is-focus .el-input__inner { + border-color: transparent +} + +.el-input-group--append .el-input__inner { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.el-input-group--append .el-select .el-input.is-focus .el-input__inner { + border-color: transparent +} + +.el-input__inner::-ms-clear { + display: none; + width: 0; + height: 0 +} + +::-webkit-scrollbar { + width: 0; +} diff --git a/public/js/lay-module/cascader/cascader.js b/public/js/lay-module/cascader/cascader.js new file mode 100644 index 0000000..0cbaee3 --- /dev/null +++ b/public/js/lay-module/cascader/cascader.js @@ -0,0 +1,2119 @@ +/** + * 仿element-ui,级联选择器 + * 已实现单选、多选、无关联选择 + * 其他功能:组件禁用、节点禁用、自定义属性、自定义空面板提示,自定义无选择时的提示、多选标签折叠、回显、搜索、动态加载、最大选中数量限制、禁用项固定等操作。 + * element-ui没有的功能:最大选中数量限制、禁用项固定 + * author: yixiaco + * gitee: https://gitee.com/yixiacoco/lay_cascader + * github: https://github.com/yixiaco/lay_cascader + */ +;!function (e) { + var src = e.document.currentScript.src; + var groupMatch = /(\S*\/)cascader(\.min)?\.js(\?.*)?/i.exec(src); + if (groupMatch && groupMatch.length >= 2 && (!groupMatch[3] || groupMatch[3].indexOf('css=true') !== -1)) { + layui.link(groupMatch[1] + 'cascader' + (groupMatch[2] || '') + '.css'); + } +}(window); +layui.define(["jquery"], function (exports) { + var $ = layui.jquery; + + /** + * 阻止事件冒泡 + * @param event + */ + function stopPropagation(event) { + event = event || window.event + if (event.stopPropagation) { + event.stopPropagation() + } else { + event.cancelBubble = true + } + } + + /** + * 级联各项节点对象 + * @param data 原始对象信息 + * @param cascader 级联对象 + * @param level 层级,从0开始 + * @param parentNode 父节点对象 + * @constructor + */ + function Node(data, cascader, level, parentNode) { + this.data = data; + this.cascader = cascader; + this.config = cascader.config; + this.props = cascader.props; + this.level = level; + this.parentNode = parentNode; + // 引入的icon图标 + this.icons = cascader.icons; + //该节点是否被选中 0:未选中,1:选中,2:不定 + this._checked = 0; + // 是否正在加载中 + this._loading = false; + // 每个Node的唯一标识 + this.nodeId = cascader.data.nodeId++; + } + + Node.prototype = { + constructor: Node, + /** 最顶级父节点 */ + get topParentNode() { + return !this.parentNode && this || this.topParentNode; + }, + /** 子节点 */ + childrenNode: undefined, + get loading() { + return this._loading; + }, + set loading(loading) { + var $li = this.$li; + if ($li) { + var rightIcon = this.icons.right; + var loadingIcon = this.icons.loading; + var $i = $li.find('i'); + if (loading) { + $i.addClass(loadingIcon); + $i.removeClass(rightIcon); + } else { + $i.addClass(rightIcon); + $i.removeClass(loadingIcon); + } + } + return this._loading = loading; + }, + /** 当前节点的显示文本 */ + get label() { + return this.data[this.props.label]; + }, + /** 当前节点的值 */ + get value() { + return this.data[this.props.value]; + }, + /** 是否禁用 */ + get disabled() { + var multiple = this.props.multiple; + var maxSize = this.config.maxSize; + var checkedNodeIds = this.cascader.data.checkedNodeIds; + var disabledName = this.props.disabled; + var checkStrictly = this.props.checkStrictly; + // 检查是否超过最大值限制 + if (multiple && maxSize !== 0) { + if (checkedNodeIds.length >= maxSize && checkedNodeIds.indexOf(this.nodeId) === -1) { + // 如果是关联的多选,需要查询叶子节点是否有被选中的项 + if (!checkStrictly) { + var leafChildren = this.getAllLeafChildren(); + var nodeIds = leafChildren.map(function (value) { + return value.nodeId + }); + // 如果叶子节点不包含,则直接返回true + if (!nodeIds.some(function (nodeId) { + return checkedNodeIds.indexOf(nodeId) !== -1; + })) { + return true; + } + } else { + return true; + } + } + } + if (!checkStrictly) { + var path = this.path; + return path.some(function (node) { + return node.data[disabledName]; + }); + } else { + return this.data[disabledName]; + } + }, + /** 子节点数据 */ + get children() { + return this.data[this.props.children]; + }, + set children(children) { + this.data[this.props.children] = children; + }, + /** 叶子节点 */ + get leaf() { + var leaf = this.data[this.props.leaf]; + if (typeof leaf === 'boolean') { + return leaf; + } + // 如果children不为空,则判断是否是子节点 + if (this.children) { + return this.children.length <= 0; + } + return true; + }, + /** 当前单选值 */ + get activeNodeId() { + return this.cascader.data.activeNodeId; + }, + /** 当前复选框值 */ + get checkedNodeIds() { + return this.cascader.data.checkedNodeIds; + }, + /** 路径 */ + get path() { + var parentNode = this.parentNode; + if (parentNode) { + return parentNode.path.concat([this]); + } else { + return [this]; + } + }, + /** 是否正在搜索中 */ + get isFiltering() { + return this.cascader.isFiltering; + }, + /** 输入框的tag标签 */ + get $tag() { + var cascader = this.cascader; + var showAllLevels = this.config.showAllLevels; + var disabled = this.config.disabled; + var nodeDisabled = this.disabled; + var disabledFixed = this.config.disabledFixed; + + var label = this.getPathLabel(showAllLevels); + var $tag = cascader.get$tag(label, !disabled && (!nodeDisabled || !disabledFixed)); + var self = this; + $tag.find('i').click(function (event) { + stopPropagation(event); + self.selectedValue(); + cascader.removeTag(self.value, self); + }); + return $tag; + }, + /** + * 完整路径的标签 + * @param showAllLevels + * @returns {string} + */ + getPathLabel: function (showAllLevels) { + var path = this.path; + var separator = this.config.separator; + + var label; + if (showAllLevels) { + label = path.map(function (node) { + return node.label; + }).join(separator); + } else { + label = path[path.length - 1].label; + } + return label; + }, + /** + * 初始化 + */ + init: function () { + var multiple = this.props.multiple; + var checkStrictly = this.props.checkStrictly; + var fromIcon = this.icons.from; + var rightIcon = this.icons.right; + var icon = ''; + var label = this.label; + if (!this.leaf) { + icon = rightIcon; + } + this.$li = $(''); + + // 节点渲染 + if (!multiple && !checkStrictly) { + this._renderRadio(); + } else if (!multiple && checkStrictly) { + this._renderRadioCheckStrictly(); + } else if (multiple && !checkStrictly) { + this._renderMultiple(); + } else if (multiple && checkStrictly) { + this._renderMultipleCheckStrictly(); + } + }, + /** + * 初始化可搜索li + */ + initSuggestionLi: function () { + var label = this.getPathLabel(true); + this.$suggestionLi = $('
  • ' + label + '
  • '); + // 节点渲染 + this._renderFiltering(); + }, + /** + * 绑定到菜单中 + * @param $list li节点 + */ + bind: function ($list) { + this.init(); + $list.append(this.$li); + }, + /** + * 绑定可搜索到列表中 + * @param $list + */ + bindSuggestion: function ($list) { + this.initSuggestionLi(); + $list.append(this.$suggestionLi); + }, + /** + * 可搜索渲染 + * @private + */ + _renderFiltering: function () { + var $li = this.$suggestionLi; + var nodeId = this.nodeId; + var fromIcon = this.icons.from; + var okIcon = this.icons.ok; + var self = this; + var cascader = this.cascader; + var multiple = this.props.multiple; + + var icon = ''; + $li.click(function (event) { + stopPropagation(event); + self.selectedValue(); + if (multiple) { + if (self.checkedNodeIds.indexOf(nodeId) === -1) { + $li.removeClass('is-checked'); + $li.find('.el-icon-check').remove(); + } else { + $li.addClass('is-checked'); + $li.append(icon); + } + } else { + // 关闭面板 + cascader.close(); + } + }); + + if (multiple && self.checkedNodeIds.indexOf(nodeId) !== -1 + || !multiple && self.activeNodeId === nodeId) { + $li.addClass('is-checked'); + $li.append(icon) + } + }, + /** + * 单选&&关联 + * @private + */ + _renderRadio: function () { + var $li = this.$li; + var nodeId = this.nodeId; + var fromIcon = this.icons.from; + var okIcon = this.icons.ok; + var level = this.level; + var leaf = this.leaf; + var self = this; + var cascader = this.cascader; + var activeNode = this.cascader.data.activeNode; + var parentNode = this.parentNode; + + if (self.activeNodeId && activeNode.path.some(function (node) { + return node.nodeId === nodeId; + })) { + if (self.activeNodeId === nodeId) { + $li.prepend(''); + } + $li.addClass('is-active'); + $li.addClass('in-checked-path'); + } + + // 是否禁用 + if (this.disabled) { + $li.addClass('is-disabled'); + return; + } + + $li.addClass('is-selectable'); + + if (parentNode) { + parentNode.$li.siblings().removeClass('in-active-path'); + parentNode.$li.addClass('in-active-path'); + } + + // 触发下一个节点 + this._liClick(function (event) { + stopPropagation(event); + var childrenNode = self.childrenNode; + if (leaf && event.type === 'click') { + self.selectedValue(); + // 关闭面板 + cascader.close(); + } + // 添加下级菜单 + cascader._appendMenu(childrenNode, level + 1, self); + }); + }, + /** + * 单选&&非关联 + * @private + */ + _renderRadioCheckStrictly: function () { + var $li = this.$li; + var nodeId = this.nodeId; + var level = this.level; + var leaf = this.leaf; + var self = this; + var cascader = this.cascader; + var activeNode = cascader.data.activeNode; + var parentNode = this.parentNode; + + $li.addClass('is-selectable'); + // 任意一级单选 + var $radio = $(''); + this.$radio = $radio; + $li.prepend($radio); + if (parentNode) { + parentNode.$li.siblings().removeClass('in-active-path'); + parentNode.$li.addClass('in-active-path'); + } + + // 触发下一个节点 + this._liClick(function (event) { + stopPropagation(event); + var childrenNode = self.childrenNode; + if (!self.disabled && leaf && event.type === 'click') { + self.selectedValue(); + } + // 添加下级菜单 + cascader._appendMenu(childrenNode, level + 1, self); + }); + + if (self.activeNodeId && activeNode.path.some(function (node) { + return node.nodeId === nodeId; + })) { + if (self.activeNodeId === nodeId) { + $radio.find('.el-radio__input').addClass('is-checked'); + } + $li.addClass('is-active'); + $li.addClass('in-checked-path'); + } + + if (this.disabled) { + $radio.addClass('is-disabled'); + $radio.find('.el-radio__input').addClass('is-disabled'); + return; + } + // 选中事件 + $radio.click(function (event) { + event.preventDefault(); + !leaf && self.selectedValue(); + }); + }, + /** + * 多选&&关联 + * @private + */ + _renderMultiple: function () { + var $li = this.$li; + var level = this.level; + var leaf = this.leaf; + var self = this; + var cascader = this.cascader; + var checked = this._checked; + var parentNode = this.parentNode; + + $li.addClass('el-cascader-node'); + + // 多选框 + var $checked = $(''); + this.$checked = $checked; + $li.prepend($checked); + + // 渲染 + if (checked === 1) { + this.$checked.find('.el-checkbox__input').addClass('is-checked'); + } else if (checked === 2) { + this.$checked.find('.el-checkbox__input').addClass('is-indeterminate'); + } + + if (parentNode) { + parentNode.$li.siblings().removeClass('in-active-path'); + parentNode.$li.addClass('in-active-path'); + } + + // 触发下一个节点 + this._liClick(function (event) { + stopPropagation(event); + var childrenNode = self.childrenNode; + if (!self.disabled && leaf && event.type === 'click') { + // 最后一级就默认选择 + self.selectedValue(); + } + // 添加下级菜单 + cascader._appendMenu(childrenNode, level + 1, self); + }); + + if (this.disabled) { + $li.addClass('is-disabled'); + $checked.addClass('is-disabled'); + $checked.find('.el-checkbox__input').addClass('is-disabled'); + return; + } + + // 选中事件 + $checked.click(function (event) { + event.preventDefault(); + if (!leaf) { + var childrenNode = self.childrenNode; + self.selectedValue(); + cascader._appendMenu(childrenNode, level + 1, self); + } + }); + }, + /** + * 多选&&非关联 + * @private + */ + _renderMultipleCheckStrictly: function () { + var $li = this.$li; + var level = this.level; + var leaf = this.leaf; + var self = this; + var cascader = this.cascader; + var checkedNodeIds = cascader.data.checkedNodeIds; + var checkedNodes = cascader.data.checkedNodes; + var nodeId = this.nodeId; + var parentNode = this.parentNode; + + $li.addClass('el-cascader-node is-selectable'); + + // 多选框 + var $checked = $(''); + this.$checked = $checked; + $li.prepend($checked); + + // 渲染 + var exist = checkedNodes.some(function (node) { + return node.path.some(function (node) { + return node.nodeId === nodeId; + }) + }); + if (exist) { + $li.addClass('in-checked-path'); + if (checkedNodeIds.indexOf(nodeId) !== -1) { + this.$checked.find('.el-checkbox__input').addClass('is-checked'); + } + } + + if (parentNode) { + parentNode.$li.siblings().removeClass('in-active-path'); + parentNode.$li.addClass('in-active-path'); + } + + // 触发下一个节点 + this._liClick(function (event) { + stopPropagation(event); + var childrenNode = self.childrenNode; + if (!self.disabled && leaf && event.type === 'click') { + // 最后一级就默认选择 + self.selectedValue(); + } + // 添加下级菜单 + cascader._appendMenu(childrenNode, level + 1, self); + }); + + if (this.disabled) { + $checked.addClass('is-disabled'); + $checked.find('.el-checkbox__input').addClass('is-disabled'); + return; + } + // 选中事件 + $checked.click(function (event) { + event.preventDefault(); + if (!leaf) { + self.selectedValue(); + var childrenNode = self.childrenNode; + // 添加下级菜单 + cascader._appendMenu(childrenNode, level + 1, self); + } + }); + }, + /** + * 向上传递 + * @param callback 执行方法,如果返回false,则中断执行 + * @param advance 是否先执行一次 + * @param self 自身 + */ + transferParent: function (callback, advance, self) { + if (!self) { + self = this; + } + if (this !== self || advance) { + var goOn = callback && callback(this); + if (goOn === false) { + return; + } + } + this.parentNode && this.parentNode.transferParent(callback, advance, self); + }, + /** + * 向下传递 + * @param callback 执行的方法,如果返回false,则中断执行 + * @param advance 是否先执行一次 + * @param self 自身 + */ + transferChildren: function (callback, advance, self) { + if (!self) { + self = this; + } + if (this !== self || advance) { + var goOn = callback && callback(this); + if (goOn === false) { + return; + } + } + var children = this.getChildren(); + if (children && children.length > 0) { + for (var index in children) { + children[index].transferChildren(callback, advance, self); + } + } + }, + /** + * 设置级联值 + */ + selectedValue: function () { + var nodeId = this.nodeId; + var cascader = this.cascader; + var multiple = this.props.multiple; + var checkStrictly = this.props.checkStrictly; + var leaf = this.leaf; + if (!multiple && (leaf || checkStrictly)) { + cascader._setActiveValue(nodeId, this); + } else if (multiple) { + var checkedNodeIds = cascader.data.checkedNodeIds; + var checkedNodes = cascader.data.checkedNodes; + var disabledFixed = this.config.disabledFixed; + var paths; + if (checkStrictly) { + var index = checkedNodeIds.indexOf(nodeId); + if (index === -1) { + paths = checkedNodes.concat([this]); + } else { + paths = checkedNodes.concat(); + paths.splice(index, 1); + } + } else { + var allLeafChildren = this.getAllLeafChildren(); + var checked; + if (this._checked !== 1 && disabledFixed) { + checked = this._getMultipleChecked(allLeafChildren); + } else { + checked = this._checked; + } + if (checked === 1) { + // 选中->未选中 + paths = checkedNodes.filter(function (node1) { + return !allLeafChildren.some(function (node2) { + return node1.nodeId === node2.nodeId; + }); + }); + } else { + // 未选中、部分选中->选中 + var add = allLeafChildren.filter(function (node) { + return checkedNodeIds.indexOf(node.nodeId) === -1; + }); + paths = checkedNodes.concat(add); + } + } + var nodeIds = paths.map(function (node) { + return node.nodeId; + }); + cascader._setCheckedValue(nodeIds, paths); + } + }, + _liLoad: function (event, callback) { + var leaf = this.leaf; + var lazy = this.props.lazy; + var lazyLoad = this.props.lazyLoad; + var children = this.children; + var self = this; + var cascader = this.cascader; + var level = this.level; + var multiple = this.props.multiple; + var checkStrictly = this.props.checkStrictly; + if (!leaf && (!children || children.length === 0) && lazy) { + if (!self.loading) { + self.loading = true; + lazyLoad(self, function (nodes) { + self.loading = false; + self.setChildren(cascader.initNodes(nodes, level + 1, self)); + self.children = nodes; + callback && callback(event); + // 多选&关联时,重新同步下父级节点的样式 + multiple && !checkStrictly && self.transferParent(function (node) { + node.syncStyle(); + }, true); + }); + } + } else { + callback && callback(event); + } + }, + /** + * 点击li事件 + * @param callback + * @private + */ + _liClick: function (callback) { + var leaf = this.leaf; + var $li = this.$li; + var self = this; + + function load(event) { + self._liLoad(event, callback); + } + + if (this.props.expandTrigger === "click" || leaf) { + $li.click(load); + } + if (this.props.expandTrigger === "hover") { + $li.mouseenter(load); + } + }, + setChildren: function (children) { + this.childrenNode = children; + }, + getChildren: function () { + return this.childrenNode; + }, + /** + * 同步样式 + */ + syncStyle: function () { + var multiple = this.props.multiple; + var checkStrictly = this.props.checkStrictly; + if (multiple) { + //多选 + if (checkStrictly) { + this._sync.syncMultipleCheckStrictly(this); + } else { + this._sync.syncMultiple(this); + } + } else { + //单选 + if (checkStrictly) { + this._sync.syncRadioCheckStrictly(this); + } else { + this._sync.syncRadio(this); + } + } + }, + /** + * 同步本节点样式 + */ + _sync: { + /** + * 同步单选关联样式 + */ + syncRadio: function (self) { + var $li = self.$li; + var fromIcon = self.icons.from; + var okIcon = self.icons.ok; + var multiple = self.props.multiple; + var checkStrictly = self.props.checkStrictly; + var nodeId = self.nodeId; + if (!$li || multiple || checkStrictly) { + return; + } + var activeNode = self.cascader.data.activeNode; + if (self.activeNodeId === nodeId) { + var ok = $li.find('.' + okIcon); + if (ok.length === 0) { + $li.prepend(''); + } + } else { + $li.find('.' + okIcon).remove(); + } + if (activeNode && activeNode.path.some(function (node) { + return node.nodeId === nodeId; + })) { + $li.addClass('is-active'); + $li.addClass('in-checked-path'); + } else { + $li.removeClass('is-active'); + $li.removeClass('in-checked-path'); + } + }, + /** + * 同步单选非关联样式 + */ + syncRadioCheckStrictly: function (self) { + var $li = self.$li; + var checkStrictly = self.props.checkStrictly; + var multiple = self.props.multiple; + if (!$li || multiple || !checkStrictly) { + return; + } + var $radio = self.$radio; + var activeNode = self.cascader.data.activeNode; + var nodeId = self.nodeId; + if (self.activeNodeId === nodeId) { + $radio.find('.el-radio__input').addClass('is-checked'); + } else { + $radio.find('.el-radio__input').removeClass('is-checked'); + } + if (activeNode && activeNode.path.some(function (node) { + return node.nodeId === nodeId; + })) { + $li.addClass('is-active'); + $li.addClass('in-checked-path'); + } else { + $li.removeClass('is-active'); + $li.removeClass('in-checked-path'); + } + }, + /** + * 同步多选关联样式 + */ + syncMultiple: function (self) { + var $li = self.$li; + var checkStrictly = self.props.checkStrictly; + var multiple = self.props.multiple; + var disabledFixed = self.config.disabledFixed; + if (!multiple || checkStrictly) { + return; + } + var allLeafChildren = self.getAllLeafChildren(disabledFixed); + // 全部未选中 0 + // 全部选中 1 + // 部分选中 2 + var checked = self._getMultipleChecked(allLeafChildren); + self._checked = checked; + if (!$li) { + return; + } + var $checkbox = self.$checked.find('.el-checkbox__input'); + if (checked === 0) { + $checkbox.removeClass('is-checked'); + $checkbox.removeClass('is-indeterminate'); + } else if (checked === 1) { + $checkbox.removeClass('is-indeterminate'); + $checkbox.addClass('is-checked'); + } else if (checked === 2) { + $checkbox.removeClass('is-checked'); + $checkbox.addClass('is-indeterminate'); + } + }, + /** + * 同步多选非关联样式 + */ + syncMultipleCheckStrictly: function (self) { + var $li = self.$li; + var checkStrictly = self.props.checkStrictly; + var multiple = self.props.multiple; + if (!$li || !multiple || !checkStrictly) { + return; + } + var checkedNodes = self.cascader.data.checkedNodes; + var checkedNodeIds = self.checkedNodeIds; + var nodeId = self.nodeId; + var exist = checkedNodes.some(function (node) { + return node.path.some(function (node) { + return node.nodeId === nodeId; + }); + }); + var $checkbox = self.$checked.find('.el-checkbox__input'); + if (checkedNodeIds.some(function (checkedNodeId) { + return checkedNodeId === nodeId; + })) { + // 选中 + $checkbox.addClass('is-checked'); + } else { + // 未选中 + $checkbox.removeClass('is-checked'); + } + if (exist) { + $li.addClass('in-checked-path'); + } else { + $li.removeClass('in-checked-path'); + } + } + }, + /** + * 获取叶子节点value值 + * @param disabled 是否包含禁用,默认不包含 + * @returns {Node[]|*[]} + */ + getAllLeafChildren: function (disabled) { + var leaf = this.leaf; + if (leaf) { + return [this]; + } else { + var leafs = []; + this.transferChildren(function (node) { + if (node.disabled && !disabled) { + return false; + } + node.leaf && leafs.push(node); + }); + return leafs; + } + }, + /** + * 展开当前节点 + */ + expandPanel: function () { + var path = this.path; + var cascader = this.cascader; + path.forEach(function (node, index, array) { + if (index !== array.length - 1) { + var childrenNode = node.childrenNode; + cascader._appendMenu(childrenNode, node.level + 1, node.parentNode); + } + }); + }, + _getMultipleChecked: function (leafNodes) { + var cascader = this.cascader; + var checkedNodeIds = cascader.data.checkedNodeIds; + var allLeafIds = leafNodes.map(function (node) { + return node.nodeId; + }); + // 全部未选中 0 + // 全部选中 1 + // 部分选中 2 + var checked = 0; + for (var i = 0; i < allLeafIds.length; i++) { + var child = allLeafIds[i]; + if (checked === 2) { + break; + } + if (checkedNodeIds.indexOf(child) !== -1) { + if (i > 0 && checked !== 1) { + checked = 2; + } else { + checked = 1; + } + } else { + // 当全部选中时,则改为部分选中,否则全部未选中 + checked = checked === 1 ? 2 : 0; + } + } + return checked; + } + }; + + function Cascader(config) { + this.config = $.extend(true, { + elem: '', //绑定元素 + value: null, //预设值 + options: [], //可选项数据源,键名可通过 Props 属性配置 + empty: '暂无数据', //无匹配选项时的内容 + placeholder: '请选择',//输入框占位文本 + disabled: false, //是否禁用 + clearable: false, //是否支持清空选项 + showAllLevels: true, //输入框中是否显示选中值的完整路径 + collapseTags: false, //多选模式下是否折叠Tag + minCollapseTagsNumber: 1, //最小折叠标签数 + separator: ' / ', //选项分隔符 + filterable: false, //是否可搜索选项 + filterMethod: function (node, keyword) { + return node.path.some(function (node) { + return node.label.indexOf(keyword) !== -1; + }); + }, //自定义搜索逻辑,第一个参数是节点node,第二个参数是搜索关键词keyword,通过返回布尔值表示是否命中 + debounce: 300, //搜索关键词输入的去抖延迟,毫秒 + beforeFilter: function (value) { + return true; + },//筛选之前的钩子,参数为输入的值,若返回 false,则停止筛选 + popperClass: '', // 自定义浮层类名 string + extendClass: false, //继承class样式 + extendStyle: false, //继承style样式 + disabledFixed: false, //固定禁用项,使禁用项不被清理删除,禁用项只能通过函数添加或初始值添加,默认禁用项不可被函数或初始值添加 + maxSize: 0, // 多选选中的最大数量,0表示不限制 + props: { + strictMode: false, //严格模式,设置value严格按照层级结构.例如:[[1,2,3],[1,2,4]] + expandTrigger: 'click', //次级菜单的展开方式 string click / hover 'click' + multiple: false, //是否多选 boolean - false + checkStrictly: false, //是否严格的遵守父子节点不互相关联 boolean - false + lazy: false, //是否动态加载子节点,需与 lazyLoad 方法结合使用 boolean - false + lazyLoad: function (node, resolve) { + }, //加载动态数据的方法,仅在 lazy 为 true 时有效 function(node, resolve),node为当前点击的节点,resolve为数据加载完成的回调(必须调用) + value: 'value', //指定选项的值为选项对象的某个属性值 string — 'value' + label: 'label', //指定选项标签为选项对象的某个属性值 string — 'label' + children: 'children', //指定选项的子选项为选项对象的某个属性值 string — 'children' + disabled: 'disabled', //指定选项的禁用为选项对象的某个属性值 string — 'disabled' + leaf: 'leaf' //指定选项的叶子节点的标志位为选项对象的某个属性值 string — 'leaf' + } + }, config); + this.data = { + nodeId: 1, //nodeId的自增值 + nodes: [], //存储Node对象 + menuData: [], //压入菜单的数据 + activeNodeId: null, //存放NodeId + activeNode: null, //存放Node + checkedNodeIds: [], //存放多个NodeId + checkedNodes: [] //存放多个Node + }; + // 面板是否展开 + this.showPanel = false; + this.event = { + // 值变更事件 + change: [], + // 打开事件 + open: [], + // 关闭事件 + close: [] + } + // 是否正在搜索中 + this.filtering = false; + // 初始化 + this._init(); + // 面板关闭事件id + this.closeEventId = 0; + // 是否进入maxSize模式 + this._maxSizeMode = null; + } + + Cascader.prototype = { + constructor: Cascader, + get props() { + return this.config.props; + }, + get isFiltering() { + return this.filtering; + }, + set isFiltering(filtering) { + if (this.filtering === filtering) { + return; + } + this.filtering = !!filtering; + var $panel = this.$panel; + if (this.filtering) { + $panel.find('.el-cascader-panel').hide(); + $panel.find('.el-cascader__suggestion-panel').show(); + } else { + $panel.find('.el-cascader-panel').show(); + $panel.find('.el-cascader__suggestion-panel').hide(); + this.$tagsInput && this.$tagsInput.val('') + } + }, + set maxSizeMode(maxSizeMode) { + if (this._maxSizeMode !== maxSizeMode) { + this._maxSizeMode = maxSizeMode; + this.refreshMenu(); + } + }, + icons: { + from: 'layui-icon', + down: 'layui-icon-down', + close: 'layui-icon-close', + right: 'layui-icon-right', + ok: 'layui-icon-ok', + loading: 'layui-icon-loading-1 layui-anim layui-anim-rotate layui-anim-loop' + }, + // 初始化 + _init: function () { + this._checkConfig(); + // 初始化输入框 + this._initInput(); + // 初始化面板 + this._initPanel(); + // 初始化选项值 + this.setOptions(this.config.options); + var self = this; + // 监听滚动条 + $(window).scroll(function () { + self._resetXY(); + }); + // 监听窗口 + $(window).resize(function () { + self._resetXY(); + }); + // 点击事件,展开面板 + this.$div.click(function (event) { + if (self.config.disabled) { + return; + } + var show = self.showPanel; + if (!show) { + self.open(); + } else { + self.close(); + } + }); + }, + /** + * 检查配置 + * @private + */ + _checkConfig: function () { + var elem = this.config.elem; + if (!elem || $(elem).length === 0) { + throw new Error("缺少elem节点选择器"); + } + var maxSize = this.config.maxSize; + if (typeof maxSize !== 'number' || maxSize < 0) { + throw new Error("maxSize应是一个大于等于0的有效的number值"); + } + if (!Array.isArray(this.config.options)) { + throw new Error("options不是一个有效的数组"); + } + }, + /** + * 初始化根目录 + * @private + */ + _initRoot: function () { + var lazy = this.props.lazy; + var lazyLoad = this.props.lazyLoad; + var self = this; + var nodes = this.data.nodes; + if (nodes.length > 0 || !lazy) { + this._appendMenu(nodes, 0); + } else if (lazy) { + this._appendMenu(nodes, 0); + lazyLoad({ + root: true, + level: 0 + }, function (nodes) { + self.data.nodes = self.initNodes(nodes, 0, null); + self._appendMenu(self.data.nodes, 0); + }); + } + }, + /** + * 设置选项值 + * @param options + */ + setOptions: function (options) { + this.config.options = options; + // 初始化节点 + this.data.nodes = this.initNodes(options, 0, null); + // 初始化根目录 + this._initRoot(); + // 初始化值 + this.setValue(this.config.value); + }, + // 面板定位 + _resetXY: function () { + var $div = this.$div; + var offset = $div.offset(); + var $panel = this.$panel; + if ($panel) { + var windowHeight = window.innerHeight; + var windowWidth = window.innerWidth; + var panelHeight = $panel.height(); + var panelWidth = $panel.width(); + var divHeight = $div.height(); + var boundingClientRect = $div[0].getBoundingClientRect(); + var $arrow = $panel.find('.popper__arrow'); + + // 距离右边界的偏差值 + var offsetDiff = Math.min(windowWidth - boundingClientRect.x - panelWidth - 5, 0); + + var bottomHeight = windowHeight - (boundingClientRect.top + divHeight); + if (bottomHeight < panelHeight && boundingClientRect.top > panelHeight + 20) { + $panel.attr('x-placement', 'top-start') + // 向上 + $panel.css({ + top: offset.top - 20 - panelHeight + 'px', + left: offset.left + offsetDiff + 'px' + }); + } else { + $panel.attr('x-placement', 'bottom-start'); + // 距离底部边界的偏差值 + var yOffset = Math.max(panelHeight - (windowHeight - boundingClientRect.y - divHeight - 15), 0); + // 向下 + $panel.css({ + top: offset.top + divHeight - yOffset + 'px', + left: offset.left + offsetDiff + 'px' + }); + } + // 箭头偏移 + $arrow.css("left", 35 - offsetDiff + "px"); + } + }, + get $menus() { + return this.$panel && this.$panel.find('.el-cascader-panel .el-cascader-menu'); + }, + // 初始化输入框 + _initInput: function () { + var $e = $(this.config.elem); + var self = this; + // 当绑定的元素带有value属性,并且对象未设置值时,设置一个初始值 + if (this.config.value === null && $e.attr('value')) { + this.config.value = $e.attr('value'); + } + var placeholder = this.config.placeholder; + var fromIcon = this.icons.from; + var downIcon = this.icons.down; + var multiple = this.props.multiple; + var extendClass = this.config.extendClass; + var extendStyle = this.config.extendStyle; + + this.$div = $('
    '); + if (extendStyle) { + var style = $e.attr('style'); + if (style) { + this.$div.attr('style', style); + } + } + if (extendClass) { + var className = $e.attr('class'); + if (className) { + className.split(' ').forEach(function (name) { + self.$div.addClass(name); + }); + } + } + this.$input = $('
    ' + + '' + + '' + + '' + + '' + + '' + + '
    ') + this.$div.append(this.$input); + this.$inputRow = this.$input.find('.el-input__inner'); + // 多选标签 + if (multiple) { + this.$tags = $('
    '); + this.$div.append(this.$tags); + } + var element = this._initHideElement($e); + // 在后面插入元素 + element.after(this.$div); + this.$icon = this.$input.find('i'); + this._initFilterableInputEvent(); + this.disabled(this.config.disabled); + }, + /** + * 初始化隐藏元素input,主要用于layui的表单验证 + * @param $e + * @private + */ + _initHideElement: function ($e) { + var tagName = $e.prop("tagName").toLowerCase(); + // 保存原始元素 + if (tagName === 'input') { + $e.hide(); + $e.attr('type', 'hidden') + this.$ec = $e; + return $e; + } else { + var attributes = $e[0].attributes; + var $input = $(''); + var keys = Object.keys(attributes); + for (var key in keys) { + var attribute = attributes[key]; + $input.attr(attribute.name, attribute.value); + } + $input.hide(); + $input.attr('type', 'hidden') + this.$ec = $input; + $e.before($input); + $e.remove(); + return $input; + } + }, + /** + * 初始化可搜索监听事件 + * @private + */ + _initFilterableInputEvent: function () { + var filterable = this.config.filterable; + if (!filterable) { + return; + } + var timeoutID; + var multiple = this.props.multiple; + var debounce = this.config.debounce; + var placeholder = this.config.placeholder; + var beforeFilter = this.config.beforeFilter; + var filterMethod = this.config.filterMethod; + var checkStrictly = this.props.checkStrictly; + var self = this; + + function filter(event) { + var input = this; + if (timeoutID) { + clearTimeout(timeoutID); + } + timeoutID = setTimeout(function () { + timeoutID = null; + var val = $(input).val(); + if (!val) { + self.isFiltering = false; + return; + } + self.open(); + if (typeof beforeFilter === 'function' && beforeFilter(val)) { + self.isFiltering = true; + var nodes = self.getNodes(); + var filterNodes = nodes.filter(function (node) { + var disabled; + if (checkStrictly) { + disabled = node.disabled; + } else { + disabled = node.path.some(function (node) { + return node.disabled; + }); + } + if ((node.leaf || checkStrictly) && !disabled) { + if (typeof filterMethod === 'function' && filterMethod(node, val)) { + // 命中 + return true; + } + } + return false; + }); + self._setSuggestionMenu(filterNodes); + } + }, debounce); + } + + if (multiple) { + // 多选可搜索 + this.$tagsInput = $(''); + var $tagsInput = this.$tagsInput; + this.$tags.append($tagsInput); + $tagsInput.on('keydown', filter); + $tagsInput.click(function (event) { + if (self.isFiltering) { + stopPropagation(event); + } + }); + } else { + var $inputRow = this.$inputRow; + // 单选可搜索 + $inputRow.removeAttr('readonly'); + $inputRow.on('keydown', filter); + $inputRow.click(function (event) { + if (self.isFiltering) { + stopPropagation(event); + } + }); + } + }, + // 初始化面板(panel(1)) + _initPanel: function () { + var $panel = this.$panel; + var popperClass = this.config.popperClass || ''; + if (!$panel) { + // z-index:解决和layer.open默认19891016的冲突 + this.$panel = $(''); + $panel = this.$panel; + $panel.appendTo('body'); + $panel.click(function (event) { + // 阻止事件冒泡 + stopPropagation(event); + }); + // 初始化可搜索面板 + this._initSuggestionPanel(); + } + }, + /** + * 添加菜单(panel(1)->menu(n)) + * @param nodes 当前层级数据 + * @param level 层级,从0开始 + * @param parentNode 父级节点 + * @param _menuItem 刷新时,传入的当前菜单的item数据 + * @private + */ + _appendMenu: function (nodes, level, parentNode, _menuItem) { + this._removeMenu(level); + + if (parentNode && parentNode.leaf) { + return; + } + + var menuData = this.data.menuData; + var $div = $(''); + // 重新添加菜单 + this.$panel.find('.el-cascader-panel').append($div); + // 渲染细项 + this._appendLi($div, nodes); + var menuItem = {nodes: nodes, level: level, parentNode: parentNode, scrollbar: {top: 0, left: 0}}; + if (_menuItem) { + menuItem.scrollbar = _menuItem.scrollbar + } + // 渲染滚动条 + this._initScrollbar($div, menuItem); + // 重新定位面板 + this._resetXY(); + menuData.push(menuItem); + }, + /** + * 移除菜单 + * @param level + * @private + */ + _removeMenu: function (level) { + // 除了上一层的所有菜单全部移除 + var number = level - 1; + if (number !== -1) { + this.$panel.find('.el-cascader-panel .el-cascader-menu:gt(' + number + ')').remove(); + } else { + this.$panel.find('.el-cascader-panel .el-cascader-menu').remove(); + } + // 保存菜单数据 + var menuData = this.data.menuData; + if (menuData.length > level) { + menuData.splice(level, menuData.length - level); + } + }, + /** + * 添加细项(panel(1)->menu(n)->li(n)) + * @param $menu 当前菜单对象 + * @param nodes 当前层级数据 + * @private + */ + _appendLi: function ($menu, nodes) { + var $list = $menu.find('.el-cascader-menu__list'); + if (!nodes || nodes.length === 0) { + var isEmpty = this.config.empty; + $list.append('
    ' + isEmpty + '
    '); + return; + } + $.each(nodes, function (index, node) { + node.bind($list); + }); + }, + /** + * 刷新菜单面板 + */ + refreshMenu: function () { + // 先复制一个数组,避免刷新菜单时,数组的数据被改变 + var data = this.data.menuData.concat([]); + var self = this; + data.forEach(function (data) { + self._appendMenu(data.nodes, data.level, data.parentNode, data); + }) + }, + /** + * 初始化可搜索面板 + * @private + */ + _initSuggestionPanel: function () { + var filterable = this.config.filterable; + if (!filterable) { + return; + } + var $suggestionPanel = this.$suggestionPanel; + if (!$suggestionPanel) { + this.$suggestionPanel = $(''); + $suggestionPanel = this.$suggestionPanel; + this.$panel.find('.popper__arrow').before($suggestionPanel); + $suggestionPanel.click(function (event) { + // 阻止事件冒泡 + stopPropagation(event); + }); + } + }, + /** + * 设置可搜索菜单 + * @param nodes + * @private + */ + _setSuggestionMenu: function (nodes) { + var $suggestionPanel = this.$suggestionPanel; + var $list = $suggestionPanel.find('.el-cascader__suggestion-list'); + $list.empty(); + $suggestionPanel.find('.el-scrollbar__bar').remove(); + if (!nodes || nodes.length === 0) { + $list.append('
  • 无匹配数据
  • '); + return; + } + $.each(nodes, function (index, node) { + node.bindSuggestion($list); + }); + this._initScrollbar($suggestionPanel, {scrollbar: {top: 0, left: 0}}); + this._resetXY(); + }, + /** + * 初始化节点数据 + * @param data 原始数据 + * @param level 层级 + * @param parentNode 父级节点 + * @returns {*[]} + */ + initNodes: function (data, level, parentNode) { + var nodes = []; + for (var key in data) { + var datum = data[key]; + var node = new Node(datum, this, level, parentNode); + // 过滤value为null或者undefined的数据 + if (node.value === null || node.value === undefined) { + continue; + } + nodes.push(node); + if (node.children && node.children.length > 0) { + node.setChildren(this.initNodes(node.children, level + 1, node)); + } + } + return nodes; + }, + /** + * 设置单选值 + * @param nodeId 节点id + * @param node 节点对象 + * @private + */ + _setActiveValue: function (nodeId, node) { + if (this.data.activeNodeId !== nodeId) { + var activeNode = this.data.activeNode; + this.data.activeNodeId = nodeId; + this.data.activeNode = node; + activeNode && activeNode.transferParent(function (node) { + node.syncStyle(); + }, true); + node && node.transferParent(function (node) { + node.syncStyle(); + }, true); + // 填充路径 + this.change(node && node.value, node); + if (nodeId !== null) { + this._setClear(); + } + } + }, + /** + * 设置多选值 + * @param nodeIds 值数组 + * @param nodes 节点数组 + * @private + */ + _setCheckedValue: function (nodeIds, nodes) { + var checkedNodes = this.data.checkedNodes; + var maxSize = this.config.maxSize; + var maxSizeMode; + if (nodeIds.length > 0 && maxSize !== 0 && nodeIds.length >= maxSize) { + nodeIds = nodeIds.slice(0, maxSize); + nodes = nodes.slice(0, maxSize); + maxSizeMode = true + } else { + maxSizeMode = false + } + this.data.checkedNodeIds = nodeIds || []; + this.data.checkedNodes = nodes || []; + var syncPath = []; + var syncNodeIds = []; + checkedNodes.forEach(function (node) { + node.path.forEach(function (node) { + if (syncNodeIds.indexOf(node.nodeId) === -1) { + syncPath.push(node); + syncNodeIds.push(node.nodeId); + } + }); + }); + nodes.forEach(function (node) { + node.path.forEach(function (node) { + if (syncNodeIds.indexOf(node.nodeId) === -1) { + syncPath.push(node); + syncNodeIds.push(node.nodeId); + } + }); + }); + syncPath.forEach(function (node) { + node.syncStyle(); + }); + // 填充路径 + this.change(nodes.map(function (node) { + return node.value; + }), nodes); + this._setClear(); + this.maxSizeMode = maxSizeMode; + }, + /** + * 设置值 + * @param value + */ + setValue: function (value) { + if (this.data.activeNodeId || this.data.checkedNodeIds.length > 0) { + // 清空值 + this.clearCheckedNodes(); + } + if (!value) { + return; + } + var strictMode = this.props.strictMode; + if (strictMode) { + if (!Array.isArray(value)) { + throw new Error("严格模式下,value必须是一个包含父子节点数组结构."); + } + } + var nodes = this.getNodes(this.data.nodes); + var checkStrictly = this.props.checkStrictly; + var multiple = this.props.multiple; + var disabledFixed = this.config.disabledFixed; + if (multiple) { + var paths = nodes.filter(function (node) { + if ((checkStrictly || node.leaf) && (!node.disabled || disabledFixed)) { + if (strictMode) { + // 严格模式下 + // some:命中一个就为true + // every:全部命中为true + return value.some(function (levelValue) { + if (!Array.isArray(levelValue)) { + throw new Error("多选严格模式下,value必须是一个二维数组结构."); + } + var path = node.path; + return levelValue.length === path.length && levelValue.every(function (rowValue, index) { + return path[index].value === rowValue; + }); + }) + } else { + return value.indexOf(node.value) !== -1; + } + } + return false; + }); + var nodeIds = paths.map(function (node) { + return node.nodeId; + }); + this._setCheckedValue(nodeIds, paths); + // 展开第一个节点 + if (paths.length > 0) { + var first = paths[0]; + first.expandPanel(); + } + } else { + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + if ((checkStrictly || node.leaf)) { + var is = false; + if (strictMode) { + // 严格模式下 + // every:全部命中为true + var path = node.path; + is = value.length === path.length && value.every(function (rowValue, index) { + return path[index].value === rowValue; + }); + } else if (node.value === value) { + is = true; + } + if (is) { + this._setActiveValue(node.nodeId, node); + // 展开节点 + node.expandPanel(); + break; + } + } + } + } + }, + /** + * 递归获取扁平的节点 + * @param nodes + * @param container + * @returns {*[]} + */ + getNodes: function (nodes, container) { + if (!container) { + container = []; + } + if (!nodes) { + nodes = this.data.nodes; + } + var self = this; + nodes.forEach(function (node) { + container.push(node); + var children = node.getChildren(); + if (children) { + self.getNodes(children, container); + } + }); + return container; + }, + /** + * 初始化滚动条 + * @param $menu 菜单的dom节点对象 + * @param menuItem 当前菜单数据 + * @private + */ + _initScrollbar: function ($menu, menuItem) { + var $div = $('
    '); + $menu.append($div); + var vertical = $($div[1]).find('.el-scrollbar__thumb'); + var onhoriztal = $($div[0]).find('.el-scrollbar__thumb'); + var scrollbar = $menu.find('.el-scrollbar__wrap'); + var $panel = this.$panel; + var $lis = $menu.find('li'); + var height = Math.max($panel.height(), $menu.height()); + var hScale = (height - 6) / ($lis.height() * $lis.length); + var wScale = $panel.width() / $lis.width(); + + // 滚动条监听事件 + function _scrollbarEvent(scrollTop, scrollLeft) { + if (hScale < 1) { + vertical.css('height', hScale * 100 + '%'); + vertical.css('transform', 'translateY(' + scrollTop / $menu.height() * 100 + '%)'); + } + if (wScale < 1) { + onhoriztal.css('width', wScale * 100 + '%'); + onhoriztal.css('transform', 'translateY(' + scrollLeft / $menu.width() * 100 + '%)'); + } + } + + // 拖动事件 + vertical.mousedown(function (event) { + event.stopImmediatePropagation(); + stopPropagation(event); + // 禁止文本选择事件 + var selectstart = function () { + return false; + }; + $(document).bind("selectstart", selectstart); + var y = event.clientY; + var scrollTop = scrollbar.scrollTop(); + // 移动事件 + var mousemove = function (event) { + event.stopImmediatePropagation(); + var number = scrollTop + (event.clientY - y) / hScale; + scrollbar.scrollTop(number); + }; + $(document).bind('mousemove', mousemove); + // 鼠标松开事件 + $(document).one('mouseup', function (event) { + stopPropagation(event); + event.stopImmediatePropagation(); + $(document).off('mousemove', mousemove); + $(document).off('selectstart', selectstart); + }); + }); + // 监听滚动条事件 + scrollbar.scroll(function () { + var scroll = $(this); + menuItem.scrollbar.top = scroll.scrollTop() + menuItem.scrollbar.left = scroll.scrollLeft() + _scrollbarEvent(menuItem.scrollbar.top, menuItem.scrollbar.left); + }); + + // 初始化滚动条 + scrollbar.scrollTop(menuItem.scrollbar.top); + _scrollbarEvent(menuItem.scrollbar.top, menuItem.scrollbar.left); + }, + // 填充路径 + _fillingPath: function () { + var multiple = this.props.multiple; + var showAllLevels = this.config.showAllLevels; + var separator = this.config.separator; + var collapseTags = this.config.collapseTags; + var $inputRow = this.$inputRow; + var placeholder = this.config.placeholder; + var self = this; + if (!multiple) { + var activeNode = this.data.activeNode; + var path = activeNode && activeNode.path || []; + if (showAllLevels) { + this._$inputRowSetValue(path.map(function (node) { + return node.label; + }).join(separator)); + } else { + this._$inputRowSetValue(activeNode && activeNode.label || ""); + } + } else { + // 复选框 + + // 删除标签 + this.$tags.find('.el-tag').remove(); + var $tagsInput = this.$tagsInput; + // 清除高度 + $inputRow.css('height', ''); + var checkedNodes = this.data.checkedNodes; + var minCollapseTagsNumber = Math.max(this.config.minCollapseTagsNumber, 1); + if (checkedNodes.length > 0) { + var tags = []; + var paths = checkedNodes; + if (collapseTags) { + // 折叠tags + paths = checkedNodes.slice(0, Math.min(checkedNodes.length, minCollapseTagsNumber)); + } + paths.forEach(function (node) { + tags.push(node.$tag); + }); + // 判断标签是否折叠 + if (collapseTags) { + // 判断标签最小折叠数 + if (checkedNodes.length > minCollapseTagsNumber) { + tags.push(self.get$tag('+ ' + (checkedNodes.length - minCollapseTagsNumber), false)); + } + } + tags.forEach(function (tag) { + if ($tagsInput) { + $tagsInput.before(tag) + } else { + self.$tags.append(tag); + } + }); + } + var tagHeight = self.$tags.height(); + var inputHeight = $inputRow.height(); + if (tagHeight > inputHeight) { + $inputRow.css('height', tagHeight + 4 + 'px'); + } + // 重新定位 + this._resetXY(); + if (checkedNodes.length > 0) { + $inputRow.removeAttr('placeholder'); + $tagsInput && $tagsInput.removeAttr('placeholder', placeholder); + } else { + $inputRow.attr('placeholder', placeholder); + $tagsInput && $tagsInput.attr('placeholder', placeholder); + } + } + }, + /** + * 设置单选输入框的值 + * @param label + * @private + */ + _$inputRowSetValue: function (label) { + label = label || ""; + var $inputRow = this.$inputRow; + $inputRow.attr('value', label); //防止被重置 + $inputRow.val(label); + }, + /** + * 获取复选框标签对象 + * @param label + * @param showCloseIcon 是否显示关闭的icon + * @returns {jQuery|HTMLElement|*} + */ + get$tag: function (label, showCloseIcon) { + var fromIcon = this.icons.from; + var closeIcon = this.icons.close; + var icon = showCloseIcon ? '' : ''; + return $('' + label + '' + icon + ''); + }, + // 设置可清理 + _setClear: function () { + var self = this; + + function enter() { + self.$icon.removeClass(self.icons.down); + self.$icon.addClass(self.icons.close); + } + + function out() { + self.$icon.removeClass(self.icons.close); + self.$icon.addClass(self.icons.down); + } + + self.$div.mouseenter(function () { + enter(); + }); + self.$div.mouseleave(function () { + out(); + }); + self.$icon.off('click'); + var multiple = this.props.multiple; + var clear; + if (multiple) { + clear = this.data.checkedNodeIds.length > 0; + } else { + clear = !!this.data.activeNodeId; + } + if (clear && !this.config.disabled && this.config.clearable) { + self.$icon.one('click', function (event) { + stopPropagation(event); + self.close(); + self.clearCheckedNodes(); + out(); + self.$icon.off('mouseenter'); + self.$div.off('mouseenter'); + self.$div.off('mouseleave'); + }); + } else { + out(); + self.$icon.off('mouseenter'); + self.$div.off('mouseenter'); + self.$div.off('mouseleave'); + } + }, + // 禁用 + disabled: function (isDisabled) { + this.config.disabled = !!isDisabled; + if (this.config.disabled) { + this.$div.addClass('is-disabled'); + this.$div.find('.el-input--suffix').addClass('is-disabled'); + this.$inputRow.attr('disabled', 'disabled'); + this.$tagsInput && this.$tagsInput.attr('disabled', 'disabled').hide() + } else { + this.$div.removeClass('is-disabled'); + this.$div.find('.el-input--suffix').removeClass('is-disabled'); + this.$inputRow.removeAttr('disabled'); + this.$tagsInput && this.$tagsInput.removeAttr('disabled').show(); + } + // 重新设置是否可被清理 + this._setClear(); + // 重新填充路径 + this._fillingPath(); + }, + /** + * 当选中节点变化时触发 选中节点的值 + * @param value 值 + * @param node 节点 + */ + change: function (value, node) { + var multiple = this.props.multiple; + if (multiple) { + if (value && value.length > 0) { + this.$ec.attr('value', JSON.stringify(value)); + // this.$ec.val(JSON.stringify(value)); + } else { + this.$ec.removeAttr('value'); + // this.$ec.val(''); + } + } else { + this.$ec.attr('value', value || ""); + // this.$ec.val(value); + } + // 填充路径 + this._fillingPath(); + this.event.change.forEach(function (e) { + typeof e === 'function' && e(value, node) + }) + }, + /** + * 当失去焦点时触发 (event: Event) + * @param eventId 不为空时,必须与closeEventId值相等,防止旧事件触发 + */ + close: function (eventId) { + if (this.showPanel && (!eventId || this.closeEventId === eventId)) { + this.showPanel = false; + this.$div.find('.layui-icon-down').removeClass('is-reverse'); + this.$panel.slideUp(100); + this.visibleChange(false); + // 聚焦颜色 + this.$input.removeClass('is-focus'); + // 可搜索 + var filterable = this.config.filterable; + if (filterable) { + this.isFiltering = false; + this._fillingPath(); + } + this.event.close.forEach(function (e) { + typeof e === 'function' && e() + }) + } + }, + /** + * 当获得焦点时触发 (event: Event) + */ + open: function () { + if (!this.showPanel) { + this.showPanel = true; + this.closeEventId++; + var self = this; + // 当前传播事件结束后,添加点击背景关闭面板事件 + setTimeout(function () { + $(document).one('click', self.close.bind(self, self.closeEventId)); + }); + // 重新定位面板 + this._resetXY(); + // 箭头icon翻转 + this.$div.find('.layui-icon-down').addClass('is-reverse'); + this.$panel.slideDown(200); + this.visibleChange(true); + // 聚焦颜色 + this.$input.addClass('is-focus'); + this.event.open.forEach(function (e) { + typeof e === 'function' && e() + }) + } + }, + /** + * 下拉框出现/隐藏时触发 + * @param visible 出现则为 true,隐藏则为 false + */ + visibleChange: function (visible) { + }, + /** + * 在多选模式下,移除Tag时触发 移除的Tag对应的节点的值 + * @param tagValue 节点的值 + * @param node 节点对象 + */ + removeTag: function (tagValue, node) { + }, + /** + * 获取选中的节点值 + * @returns {null|[]} + */ + getCheckedValues: function () { + var strictMode = this.props.strictMode; + if (this.props.multiple) { + var checkedNodes = this.data.checkedNodes; + if (strictMode) { + return checkedNodes.map(function (node) { + return node.path.map(function (node1) { + return node1.value; + }); + }); + } + return checkedNodes.map(function (node) { + return node.value; + }); + } else { + var activeNode = this.data.activeNode; + if (strictMode) { + return activeNode && activeNode.path.map(function (node) { + return node.value; + }) + } + return activeNode && activeNode.value; + } + }, + /** + * 获取选中的节点 + * @returns {null|[]} + */ + getCheckedNodes: function () { + var strictMode = this.props.strictMode; + if (this.props.multiple) { + var checkedNodes = this.data.checkedNodes; + if (strictMode) { + return checkedNodes && checkedNodes.map(function (node) { + return node.path; + }); + } + return checkedNodes; + } else { + var activeNode = this.data.activeNode; + if (strictMode) { + return activeNode && activeNode.path; + } + return activeNode; + } + }, + /** + * 清空选中的节点 + * @param force 强制清理禁用固定节点 + */ + clearCheckedNodes: function (force) { + var multiple = this.props.multiple; + if (multiple) { + var disabledFixed = this.config.disabledFixed; + if (!force && disabledFixed) { + //禁用项被固定,则过滤出禁用项的选值出来 + var checkedNodes = this.data.checkedNodes; + var disNodes = checkedNodes.filter(function (node) { + return node.disabled; + }); + var nodeIds = disNodes.map(function (node) { + return node.nodeId; + }); + this._setCheckedValue(nodeIds, disNodes); + } else { + this._setCheckedValue([], []); + } + } else { + this._setActiveValue(null, null); + } + } + }; + + var thisCas = function () { + var self = this; + return { + /** + * 设置选项值 + * @param options + */ + setOptions: function (options) { + self.setOptions(options); + }, + /** + * 覆盖当前值 + * @param value 单选时传对象,多选时传数组 + */ + setValue: function (value) { + self.setValue(value); + }, + /** + * 当节点变更时,执行回调 + * @param callback function(value,node){} + */ + changeEvent: function (callback) { + self.event.change.push(callback); + }, + /** + * 当面板关闭时,执行回调 + * @param callback function(){} + */ + closeEvent: function (callback) { + self.event.close.push(callback); + }, + /** + * 当面板打开时,执行回调 + * @param callback function(){} + */ + openEvent: function (callback) { + self.event.open.push(callback); + }, + /** + * 禁用组件 + * @param disabled true/false + */ + disabled: function (disabled) { + self.disabled(disabled); + }, + /** + * 收起面板 + */ + close: function () { + self.close(); + }, + /** + * 展开面板 + */ + open: function () { + self.open(); + }, + /** + * 获取选中的节点,如需获取路径,使用node.path获取,将获取各级节点的node对象 + * @returns {[]|*} + */ + getCheckedNodes: function () { + return self.getCheckedNodes(); + }, + /** + * 获取选中的值 + * @returns {[]|*} + */ + getCheckedValues: function () { + return self.getCheckedValues(); + }, + /** + * 清空选中的节点 + * @param force 强制清理禁用固定节点 + */ + clearCheckedNodes: function (force) { + self.clearCheckedNodes(force); + }, + /** + * 展开面板到节点所在的层级 + * @param value 节点值,只能传单个值,不允许传数组 + */ + expandNode: function (value) { + var nodes = self.getNodes(self.data.nodes); + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + if (node.value === value) { + node.expandPanel(); + break; + } + } + }, + /** + * 获取当前配置副本 + */ + getConfig: function () { + return $.extend(true, {}, self.config); + }, + /** + * 获取数据对象副本 + * @returns {*} + */ + getData: function () { + return $.extend(true, {}, self.data); + } + }; + }; + + exports('cascader', function (option) { + var ins = new Cascader(option); + return thisCas.call(ins); + }); +}).link(layui.cache.base+"cascader/cascader.css?v="+(new Date).getTime()); \ No newline at end of file diff --git a/public/js/lay-module/checkbox/checkbox.css b/public/js/lay-module/checkbox/checkbox.css new file mode 100644 index 0000000..617b33e --- /dev/null +++ b/public/js/lay-module/checkbox/checkbox.css @@ -0,0 +1,10 @@ +.checkBox .block{float:left; margin:5px;padding:6px 15px;height:18px;text-align:center; border:1px solid #ccc;position:relative;cursor:pointer;overflow:hidden;} +.checkBox .block .choice{position:absolute;right:0px;bottom:0px;display:none;} +.checkBox .block.on{border:1px solid #1E9FFF;} +.checkBox .block.on .choice{position:absolute;right:0px;bottom:0px;display:block;} +.checkBox .block.on .choice .triangle{position:absolute;right:0px;bottom:0px;border-bottom:18px solid #1E9FFF;border-left: 18px solid transparent;} +.checkBox .block.on .choice .right{position:absolute;right:5px;bottom:5px;width:10px;height:10px;} +.checkBox .block .del{width:18px; height:18px; background:#fff; border:0px solid #ccc; border-radius:3px; overflow:hidden; cursor:pointer; position:absolute; bottom:-20px; right:0px; transition: background .3s ease, border .2s ease, bottom .2s ease;display:block;} +.checkBox .block .del:hover{background:#FF5722; border:1px solid #FF5722;} +.checkBox .block:hover .del{bottom:0px;} +.checkBox .block.on .del{display:none} \ No newline at end of file diff --git a/public/js/lay-module/checkbox/checkbox.js b/public/js/lay-module/checkbox/checkbox.js new file mode 100644 index 0000000..4e2b574 --- /dev/null +++ b/public/js/lay-module/checkbox/checkbox.js @@ -0,0 +1,69 @@ +layui.define('jquery', function(exports){ + "use strict"; + var $ = layui.$ + ,hint = layui.hint(); + var CheckBox = function(options){ + this.options = options; + }; + //初始化 + CheckBox.prototype.init = function(elem){ + var that = this; + elem.addClass('checkBox'); //添加checkBox样式 + that.checkbox(elem); + }; + //树节点解析 + CheckBox.prototype.checkbox = function(elem,children){ + var that = this, options = that.options; + var nodes = children || options.nodes; + layui.each(nodes, function(index, item){ + var li = $(['
  • '+item.name,''+(item.on?'':'')+'
  • '].join('')); + elem.append(li); + //触发点击节点回调 + typeof options.click === 'function' && that.click(li, item); + //触发删除节点回调 + typeof options.del === 'function' && that.del(li, item); + }); + }; + //点击节点回调 + CheckBox.prototype.click = function(elem, item){ + var that = this, options = that.options; + elem.on('click', function(e){ + elem.toggleClass("on"); + if(elem.hasClass("on")){ + item.on = true; + elem.children("span.hide").html(''); + }else{ + item.on = false; + elem.children("span.hide").html(''); + } + layui.stope(e); + options.click(item); + }); + }; + //点击节点回调 + CheckBox.prototype.del = function(elem, item){ + var that = this, options = that.options; + elem.children('i.del').on('click', function(e){ + var index = layer.confirm('确定删除 ['+item.name+'] 吗?', { + btn: ['删除','取消'] + }, function(){ + layer.close(index); + if(options.del(item)){ + elem.closest(".block").remove(); + layui.stope(e); + } + }); + return false; + }); + }; + + //暴露接口 + exports('checkbox', function(options){ + var checkbox = new CheckBox(options = options || {}); + var elem = $(options.elem); + if(!elem[0]){ + return hint.error('layui.checkbox 没有找到'+ options.elem +'元素'); + } + checkbox.init(elem); + }); +}).link(layui.cache.base+"checkbox/checkbox.css?v="+(new Date).getTime()); \ No newline at end of file diff --git a/public/js/lay-module/dropdownTable/dropdownTable.js b/public/js/lay-module/dropdownTable/dropdownTable.js new file mode 100644 index 0000000..9b3c3f2 --- /dev/null +++ b/public/js/lay-module/dropdownTable/dropdownTable.js @@ -0,0 +1,677 @@ +layui.define(['jquery', 'dropdown', 'table', 'form'], function (exports) { + "use strict"; + + var dropdown = layui.dropdown, //下拉菜单 + table = layui.table, //table组件 + $ = layui.jquery, //jQuery + form = layui.form, //表单 + moduleName = 'dropdownTable', //模块名 + + dropdownTable = { + version: '1.0.4', + config: { + selectType: 'radio', + emptyMsg: '点击此处选择' + }, + index: layui[moduleName] ? (layui[moduleName].index + 10000) : 0, + set: function (options) { + this.config = $.extend(true, {}, that.config, options); + let config = this.config, + selectType = config.selectType, + selectField = { type: selectType, fixed: 'left' }, + cols = config?.selectTable?.cols ?? [[]]; + cols.forEach((item) => { + if (JSON.stringify(item).indexOf(JSON.stringify(selectField)) < 0) { + item.splice(0, 0, selectField); + } + }); + return that; + } + }, + //操作当前实例 + thisModule = function () { + let that = this, + options = that.config, + id = options.id || that.index; + thisModule.that[id] = that; //记录当前实例对象 + return { + config: options, + reload: function (options) { + that.reload.call(that, options); + } + }; + }, + //构造器 + Class = function (options) { + let index = ++dropdownTable.index; + this.index = index; + this.tableId = 'dropdown-select-table-' + index; //初始化当前下拉表格的ID + this.dropdownId = 'dropdown-' + index; //初始化当前下拉组件的ID + //其中selectedIds为初始化被选择的值,selectedDisplayValues为初始化被选中值的显示值 + this.config = $.extend(true, {}, dropdownTable.config, options, { + selectedIds: [], + selectedDisplayValues: [] + }); + let config = this.config, + selectType = config.selectType, + selectField = { type: selectType, fixed: 'left' }, + selectTableCols = config?.selectTable?.cols ?? [[]]; + if (selectType === 'checkbox') { + selectTableCols.forEach((item) => { + if (JSON.stringify(item).indexOf(JSON.stringify(selectField)) < 0) { + item.splice(0, 0, selectField); + } + }); + } + this.render(); + }; + + //重载表格实例 + Class.prototype.reloadSelectTable = function (selectTableConfig) { + let config = this.config; + layui.each(selectTableConfig, function (key, item) { + if (layui.type(item) === 'array') { + delete config.selectTable[key]; + } + }); + $.extend(true, config.selectTable, selectTableConfig); + this.clearSelected(); + }; + + //清空选中 + Class.prototype.clearSelected = function () { + let config = this.config, + bindInput = config.bindInput; + $(bindInput).val(""); + $(bindInput)[0].dataset.displayValue = ''; + $.extend(config, { selectedIds: [], selectedDisplayValues: [] }); + this.setSelectedDisplayAndValue(); + } + + //渲染 + Class.prototype.render = function () { + let that = this, + options = that.config, + _this = options.elem = $(options.elem); + if (!_this[0]) { + return; + } + + that.initElemArea(); + that.iniDropdown(); + }; + + //初始化绑定元素区域 + Class.prototype.initElemArea = function () { + let that = this, + config = this.config, + elem = config.elem, + searchName = this.config?.searchName ?? 'keywords'; + $(elem).css({ + 'display': 'inline-block', + 'width': '80%', + 'height': '100%', + 'min-height': '35px', + 'border': '1px solid #eee', + 'line-height': '35px', + 'box-sizing': 'border-box', + 'padding': '0 10px', + 'cursor': 'pointer' + }); + $(elem).hover(() => { + $(elem).css({ 'border-color': '#e2e2e2' }); + }, () => { + $(elem).css({ 'border-color': '#eee' }); + }); + + let selectButton = $("") + selectButton.css({ + 'display': 'inline-block', + 'width': '20%', + 'min-width': '45px', + 'height': '100%', + 'box-sizing': 'border-box', + 'font-size': '12px', + 'padding': '0', + 'line-height': '36px' + }); + if (config?.needPopup ?? true) { + $(elem).after(selectButton); + } else { + $(elem).css({ 'width': '100%' }); + } + + let selectContent = ` +
    +
    +
    +
    +
    选择
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    +
    选择结果
    +
    + +
    +
    + +
    +
    +
    +
    + + + ` + selectButton.click(() => { + layer.open({ + title: '选择', + type: 1, + area: ['70%', '80%'], + maxmin: true, + btn: ['确认', '取消'], + content: selectContent, + success: () => { + renderSelectedTable(); + renderDataTable(); + bindTableEvent(); + initSelectedTable(); + bindDataTableSearchFormEvent(); + }, + yes: index => { + let selectedData = table.getData('selected-table') ?? []; + let selectedIds = selectedData.map(item => item.id); + config.selectedIds = selectedIds; + config.selectedDisplayValues = selectedData.map(item => item.displayValue); + that.setSelectedDisplayAndValue(); + layer.close(index); + let onSelected = config.onSelected; + if ($.isFunction(onSelected)) { + onSelected(selectedIds); + } + } + }); + }); + + function renderDataTable() { + let tableNeedConfig = { + id: 'data-table', + elem: '#data-table', + done: function (res) { + setDataTableSelected(); + } + }, + tableConfig = $.extend({}, config.selectTable, tableNeedConfig); + table.render(tableConfig); + } + + function renderSelectedTable() { + table.render({ + elem: '#selected-table', + cols: [ + [ + { field: 'id', title: 'ID', width: 80, sort: true }, + { field: 'displayValue', title: '值' }, + { field: 'originValue', title: '原始值', hide: true }, + { fixed: 'right', title: '操作', width: 70, toolbar: '#selected-table-opt-bar' } + ] + ], + data: [] + }); + } + + function bindDataTableSearchFormEvent() { + $('#data-table-search-button').click(() => { + let where = {}, + searchValue = $('#data_table_' + searchName).val(); + where[searchName] = searchValue; + table.reloadData('data-table', { page: { curr: 1 }, where: where }); + }); + } + + function bindTableEvent() { + table.on('row(data-table)', (obj) => { + let data = obj.data; + selectedTableReload({ + 'id': data[config.selectTable.uniqueId], + 'displayValue': data[config.selectTable.displayField], + 'originValue': JSON.stringify(data) + }); + }); + + table.on('tool(selected-table)', obj => { + if (obj.event === 'del') { + obj.del(); + setDataTableSelected(); + } + }); + } + + function selectedTableReload(selectedData) { + let originData = table.getData('selected-table') ?? []; + if (originData.map(item => item.id).includes(selectedData.id)) { + originData = originData.filter(item => item.id !== selectedData.id); + } else { + if (config.selectType === 'radio') { + originData = []; + } + + originData.push(selectedData) + } + + table.reload('selected-table', { + data: originData + }); + setDataTableSelected(); + } + + function setDataTableSelected() { + let selectedIds = (table.getData('selected-table') ?? []).map(item => item.id + ''); + let data = (table.getData('data-table') ?? []); + data.forEach((item, index) => { + if (config.selectType === 'checkbox') { + table.setRowChecked('data-table', { + type: 'checkbox', + index: index, + checked: selectedIds.includes(item.id + '') + }); + } else if (selectedIds.includes(item.id + '')) { + table.setRowChecked('data-table', { + type: 'radio', + index: index, + checked: true + }); + } + }); + } + + function initSelectedTable() { + let bindInput = config.bindInput, + bindInputValue = $(bindInput).val() ?? '', + bindInputDisplayValue = $(bindInput)[0].dataset.displayValue ?? ''; + + config.selectedIds = bindInputValue.split("$").filter(item => item !== ''); + config.selectedDisplayValues = bindInputDisplayValue.split("$").filter(item => item !== ''); + config.selectedIds.forEach((id, index) => { + selectedTableReload({ + 'id': id, + 'displayValue': config.selectedDisplayValues[index] + }); + }); + } + + } + + //初始化下拉组件 + Class.prototype.iniDropdown = function () { + let that = this, + config = that.config, + elem = config.elem; + + dropdown.render({ + id: config.id ?? that.index, + elem: elem, + trigger: 'click', + content: that.iniSelectTableHtml(), + style: config?.style ?? 'min-width:450px;box-sizing:border-box; padding:10px;width:' + $(elem).width() + 'px', + ready: () => { + that.iniSelectTable(); + that.bindRowCheckToSelectTable(); + that.bindSelectBoxEventToSelectTable(); + that.bindSearchToSelectTable(); + } + }); + }; + + //初始化table的html文件 + Class.prototype.iniSelectTableHtml = function () { + let index = this.index, + tableId = this.tableId, + searchName = this.config?.searchName ?? 'keywords', + selectTableSearchFormId = 'select-table-search-' + index, + selectTableSearchButtonId = 'select-table-search-button-' + index; + + $.extend(this.config, { + selectTableSearchFormId: selectTableSearchFormId, + selectTableSearchButtonId: selectTableSearchButtonId + }); + return [ + '
    ', + '', + '
    ', + '
    ', + ].join(''); + } + + //初始化表格 + Class.prototype.iniSelectTable = function () { + let that = this, + config = that.config, + tableId = that.tableId, + selectTable = config.selectTable, + tableNeedConfig = { + id: tableId, + elem: '#' + tableId, + toolbar: '#' + config.selectTableSearchFormId, + defaultToolbar: ['filter'], + done: function (res) { + that.iniDefaultSelected(res.data) + } + }, + tableConfig = $.extend({}, selectTable, tableNeedConfig); + table.render(tableConfig); + }; + + //处理默认选中值的问题 + Class.prototype.iniDefaultSelected = function (tableData = []) { + let that = this, + tableId = that.tableId, + config = that.config, + selectType = config.selectType, + bindInput = config.bindInput, + bindInputValue = $(bindInput).val() ?? '', + bindInputDisplayValue = $(bindInput)[0].dataset.displayValue ?? ''; + + config.selectedIds = bindInputValue.split("$").filter(item => item !== ''); + config.selectedDisplayValues = bindInputDisplayValue.split("$").filter(item => item !== ''); + + if (tableData.length === 0) { + that.setSelectedDisplayAndValue(); + return; + } + + table.setRowChecked(tableId, { index: 'all', checked: false }); + let selectedIds = config.selectedIds; + if (selectedIds.length > 0) { + let tableIdData = tableData.map(item => item[config.selectTable.uniqueId].toString()); + if (selectType === 'radio' && selectedIds.length === 1) { + let selectedItemIndex = tableIdData.indexOf(selectedIds[0].toString()); + if (selectedItemIndex >= 0) { + table.setRowChecked(tableId, { type: selectType, index: selectedItemIndex, checked: true }) + } + } else { + tableIdData.forEach(function (item, itemIndex) { + table.setRowChecked(tableId, { + type: selectType, + index: itemIndex, + checked: selectedIds.includes(item) + }); + }); + } + } + that.setSelectedDisplayAndValue(); + } + + //处理选择框事件 + Class.prototype.bindSelectBoxEventToSelectTable = function () { + let that = this, + options = that.config, + selectType = options.selectType, + uniqueId = options.selectTable.uniqueId, + displayField = options.selectTable.displayField, + tableId = that.tableId; + + if (selectType === 'radio') { //单选 + table.on('radio(' + tableId + ')', function (obj) { + dealRadioSelectedData(obj.checked, obj.data); + }); + } else { //复选框 + table.on('checkbox(' + tableId + ')', function (obj) { + let eventType = obj.type, + checked = obj.checked; + + if (eventType === 'all') { + table.getData(tableId).forEach(function (rowData) { + dealCheckboxSelectedData(checked, rowData); + }); + } + + if (eventType === 'one') { + dealCheckboxSelectedData(checked, obj.data); + } + }); + } + + function dealRadioSelectedData(checked, rowData) { + let rowId = rowData[uniqueId].toString(); + that.config.selectedIds = []; + that.config.selectedDisplayValues = []; + if (checked) { + that.config.selectedIds.push(rowId); + that.config.selectedDisplayValues.push(rowData[displayField]); + } + that.setSelectedDisplayAndValue(); + } + + function dealCheckboxSelectedData(checked, rowData) { + let rowId = rowData[uniqueId].toString(); + //处理选中 + if (checked && !that.config.selectedIds.includes(rowId)) { + that.config.selectedIds.push(rowId); + that.config.selectedDisplayValues.push(rowData[displayField]); + } + + //处理未选中 + if (!checked) { + let cancelIndex = that.config.selectedIds.indexOf(rowId); + if (rowId > -1) { + that.config.selectedIds.splice(cancelIndex, 1); + that.config.selectedDisplayValues.splice(cancelIndex, 1); + } + } + that.setSelectedDisplayAndValue(); + } + } + + //绑定行点击事件到选择表中 + Class.prototype.bindRowCheckToSelectTable = function () { + let that = this, + tableId = that.tableId, + options = that.config, + selectType = options.selectType, + uniqueId = options.selectTable.uniqueId, + displayField = options.selectTable.displayField; + + table.on('row(' + tableId + ')', function (obj) { + let rowId = obj.data[uniqueId].toString(), + checked; + + if (!that.config.selectedIds.includes(rowId)) { + if (selectType === 'radio') { + that.config.selectedIds = []; + that.config.selectedDisplayValues = []; + } + that.config.selectedIds.push(rowId); + that.config.selectedDisplayValues.push(obj.data[displayField]); + checked = true; + } else { + let cancelSelectedIdIndex = that.config.selectedIds.indexOf(rowId); + that.config.selectedIds.splice(cancelSelectedIdIndex, 1); + that.config.selectedDisplayValues.splice(cancelSelectedIdIndex, 1); + checked = false; + } + obj.setRowChecked({ + type: selectType, + checked: checked + }); + //加入选中事件 + let onSelected = options.onSelected; + if (checked && $.isFunction(onSelected)) { + onSelected(new Array(obj.data[options.selectTable.uniqueId])); + } + that.setSelectedDisplayAndValue(); + }); + }; + + //绑定表格的搜索事件 + Class.prototype.bindSearchToSelectTable = function () { + let tableId = this.tableId, + selectTableSearchButtonId = this.config.selectTableSearchButtonId; + form.on('submit(' + selectTableSearchButtonId + ')', function (data) { + table.reloadData(tableId, { page: { curr: 1 }, where: data.field }); + return false; + }); + } + + //设置显示值和实际存储值 + Class.prototype.setSelectedDisplayAndValue = function () { + let that = this, + tableId = that.tableId, + options = that.config, + elem = options.elem, + bindInput = options.bindInput, + selectedIds = options?.selectedIds ?? [], + selectDisplayValues = options.selectedDisplayValues, + emptyMsg = options.emptyMsg; + + elem.find("span").remove(); + if (selectedIds.length > 0 && selectedIds.length === selectDisplayValues.length) { + selectedIds.forEach((item, itemIndex) => { + let selectedDisplayValue = selectDisplayValues[itemIndex], + closeObject = $('×'), + displayHtml = [ + '' + selectedDisplayValue, + '', + ].join(''), + displayObject = $(displayHtml).append(closeObject); + + displayObject.attr({ + "style": "margin-left:5px;font-size:14px;padding:4px 6px" + }); + + //绑定鼠标事件 + closeObject.on('mouseenter mouseleave', function (event) { + let optType = event.type, + fontColor = optType === 'mouseleave' ? 'white' : 'red'; + $(this).attr({ "style": "color:" + fontColor + ";margin-left:5px;cursor:pointer" }); + }); + + closeObject.on('click', function () { + let _this = this, + tableIdData = table.getData(tableId).map(item => item[options.selectTable.uniqueId]), + selectedId = _this.dataset.selectedId, + cancelSelectedIdIndex = that.config.selectedIds.indexOf(selectedId); + that.config.selectedIds.splice(cancelSelectedIdIndex, 1); + that.config.selectedDisplayValues.splice(cancelSelectedIdIndex, 1); + if (tableIdData.includes(selectedId)) { + if (options.selectType === 'radio') { + table.setRowChecked(tableId, { + type: 'radio', + index: 'all', + checked: false + }); + } else { + table.setRowChecked(tableId, { + type: options.selectType, + index: tableIdData.indexOf(selectedId), + checked: false + }); + } + } + that.setSelectedDisplayAndValue(); + }); + elem.append(displayObject); + }); + } else { + elem.append('' + emptyMsg + ''); + } + $(bindInput).val(selectedIds.join("$")); + $(bindInput)[0].dataset.displayValue = selectDisplayValues.join("$"); + } + + //记录所有实例 + thisModule.that = {}; //记录所有实例对象 + + //获取当前实例对象 + thisModule.getThis = function (id) { + let that = thisModule.that[id]; + if (!that) { + hint.error(id ? (moduleName + ' instance with ID \'' + id + '\' not found') : 'ID argument required'); + } + + return that; + } + + //重载实例 + dropdownTable.reloadSelectTable = function (id, selectTableConfig) { + let that = thisModule.getThis(id); + that.reloadSelectTable(selectTableConfig); + return thisModule.call(that); + } + + //请空已选择 + dropdownTable.clearSelected = function (id) { + let that = thisModule.getThis(id); + that.clearSelected(); + } + + //核心入口 + dropdownTable.render = function (options) { + let inst = new Class(options); + if (dropdownTable.index === 1) { + console.log("欢迎使用Hg科技的dropdownTable组件,version:" + dropdownTable.version + ",期待您的建议!"); + } + + inst.iniDefaultSelected(); + return thisModule.call(inst); + } + + exports(moduleName, dropdownTable); +}); diff --git a/public/js/lay-module/echarts/echarts.js b/public/js/lay-module/echarts/echarts.js new file mode 100644 index 0000000..fcb939c --- /dev/null +++ b/public/js/lay-module/echarts/echarts.js @@ -0,0 +1,19 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.echarts={})}(this,function(t){"use strict";function e(t){var e={},i={},n=t.match(/Firefox\/([\d.]+)/),r=t.match(/MSIE\s([\d.]+)/)||t.match(/Trident\/.+?rv:(([\d.]+))/),a=t.match(/Edge\/([\d.]+)/),o=/micromessenger/i.test(t);return n&&(i.firefox=!0,i.version=n[1]),r&&(i.ie=!0,i.version=r[1]),a&&(i.edge=!0,i.version=a[1]),o&&(i.weChat=!0),{browser:i,os:e,node:!1,canvasSupported:!!document.createElement("canvas").getContext,svgSupported:"undefined"!=typeof SVGRect,touchEventsSupported:"ontouchstart"in window&&!i.ie&&!i.edge,pointerEventsSupported:"onpointerdown"in window&&(i.edge||i.ie&&i.version>=11),domSupported:"undefined"!=typeof document}}function i(t,e){"createCanvas"===t&&(dg=null),ug[t]=e}function n(t){if(null==t||"object"!=typeof t)return t;var e=t,i=ng.call(t);if("[object Array]"===i){if(!R(t)){e=[];for(var r=0,a=t.length;a>r;r++)e[r]=n(t[r])}}else if(ig[i]){if(!R(t)){var o=t.constructor;if(t.constructor.from)e=o.from(t);else{e=new o(t.length);for(var r=0,a=t.length;a>r;r++)e[r]=n(t[r])}}}else if(!eg[i]&&!R(t)&&!T(t)){e={};for(var s in t)t.hasOwnProperty(s)&&(e[s]=n(t[s]))}return e}function r(t,e,i){if(!S(e)||!S(t))return i?n(e):t;for(var a in e)if(e.hasOwnProperty(a)){var o=t[a],s=e[a];!S(s)||!S(o)||_(s)||_(o)||T(s)||T(o)||M(s)||M(o)||R(s)||R(o)?!i&&a in t||(t[a]=n(e[a],!0)):r(o,s,i)}return t}function a(t,e){for(var i=t[0],n=1,a=t.length;a>n;n++)i=r(i,t[n],e);return i}function o(t,e){for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i]);return t}function s(t,e,i){for(var n in e)e.hasOwnProperty(n)&&(i?null!=e[n]:null==t[n])&&(t[n]=e[n]);return t}function l(){return dg||(dg=cg().getContext("2d")),dg}function h(t,e){if(t){if(t.indexOf)return t.indexOf(e);for(var i=0,n=t.length;n>i;i++)if(t[i]===e)return i}return-1}function u(t,e){function i(){}var n=t.prototype;i.prototype=e.prototype,t.prototype=new i;for(var r in n)t.prototype[r]=n[r];t.prototype.constructor=t,t.superClass=e}function c(t,e,i){t="prototype"in t?t.prototype:t,e="prototype"in e?e.prototype:e,s(t,e,i)}function d(t){return t?"string"==typeof t?!1:"number"==typeof t.length:void 0}function f(t,e,i){if(t&&e)if(t.forEach&&t.forEach===ag)t.forEach(e,i);else if(t.length===+t.length)for(var n=0,r=t.length;r>n;n++)e.call(i,t[n],n,t);else for(var a in t)t.hasOwnProperty(a)&&e.call(i,t[a],a,t)}function p(t,e,i){if(t&&e){if(t.map&&t.map===lg)return t.map(e,i);for(var n=[],r=0,a=t.length;a>r;r++)n.push(e.call(i,t[r],r,t));return n}}function g(t,e,i,n){if(t&&e){if(t.reduce&&t.reduce===hg)return t.reduce(e,i,n);for(var r=0,a=t.length;a>r;r++)i=e.call(n,i,t[r],r,t);return i}}function v(t,e,i){if(t&&e){if(t.filter&&t.filter===og)return t.filter(e,i);for(var n=[],r=0,a=t.length;a>r;r++)e.call(i,t[r],r,t)&&n.push(t[r]);return n}}function m(t,e,i){if(t&&e)for(var n=0,r=t.length;r>n;n++)if(e.call(i,t[n],n,t))return t[n]}function y(t,e){var i=sg.call(arguments,2);return function(){return t.apply(e,i.concat(sg.call(arguments)))}}function x(t){var e=sg.call(arguments,1);return function(){return t.apply(this,e.concat(sg.call(arguments)))}}function _(t){return"[object Array]"===ng.call(t)}function w(t){return"function"==typeof t}function b(t){return"[object String]"===ng.call(t)}function S(t){var e=typeof t;return"function"===e||!!t&&"object"==e}function M(t){return!!eg[ng.call(t)]}function I(t){return!!ig[ng.call(t)]}function T(t){return"object"==typeof t&&"number"==typeof t.nodeType&&"object"==typeof t.ownerDocument}function C(t){return t!==t}function A(){for(var t=0,e=arguments.length;e>t;t++)if(null!=arguments[t])return arguments[t]}function D(t,e){return null!=t?t:e}function k(t,e,i){return null!=t?t:null!=e?e:i}function P(){return Function.call.apply(sg,arguments)}function L(t){if("number"==typeof t)return[t,t,t,t];var e=t.length;return 2===e?[t[0],t[1],t[0],t[1]]:3===e?[t[0],t[1],t[2],t[1]]:t}function O(t,e){if(!t)throw new Error(e)}function z(t){return null==t?null:"function"==typeof t.trim?t.trim():t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")}function E(t){t[fg]=!0}function R(t){return t[fg]}function B(t){function e(t,e){i?n.set(t,e):n.set(e,t)}var i=_(t);this.data={};var n=this;t instanceof B?t.each(e):t&&f(t,e)}function N(t){return new B(t)}function F(t,e){for(var i=new t.constructor(t.length+e.length),n=0;n=0;if(r){var a="touchend"!=n?e.targetTouches[0]:e.changedTouches[0];a&&de(t,a,e,i)}else de(t,e,e,i),e.zrDelta=e.wheelDelta?e.wheelDelta/120:-(e.detail||0)/3;var o=e.button;return null==e.which&&void 0!==o&&Mg.test(e.type)&&(e.which=1&o?1:2&o?3:4&o?2:0),e}function ge(t,e,i){Sg?t.addEventListener(e,i):t.attachEvent("on"+e,i)}function ve(t,e,i){Sg?t.removeEventListener(e,i):t.detachEvent("on"+e,i)}function me(t){return t.which>1}function ye(t,e,i){return{type:t,event:i,target:e.target,topTarget:e.topTarget,cancelBubble:!1,offsetX:i.zrX,offsetY:i.zrY,gestureEvent:i.gestureEvent,pinchX:i.pinchX,pinchY:i.pinchY,pinchScale:i.pinchScale,wheelDelta:i.zrDelta,zrByTouch:i.zrByTouch,which:i.which,stop:xe}}function xe(){Ig(this.event)}function _e(){}function we(t,e,i){if(t[t.rectHover?"rectContain":"contain"](e,i)){for(var n,r=t;r;){if(r.clipPath&&!r.clipPath.contain(e,i))return!1;r.silent&&(n=!0),r=r.parent}return n?Tg:!0}return!1}function be(){var t=new Dg(6);return Se(t),t}function Se(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t}function Me(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t}function Ie(t,e,i){var n=e[0]*i[0]+e[2]*i[1],r=e[1]*i[0]+e[3]*i[1],a=e[0]*i[2]+e[2]*i[3],o=e[1]*i[2]+e[3]*i[3],s=e[0]*i[4]+e[2]*i[5]+e[4],l=e[1]*i[4]+e[3]*i[5]+e[5];return t[0]=n,t[1]=r,t[2]=a,t[3]=o,t[4]=s,t[5]=l,t}function Te(t,e,i){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4]+i[0],t[5]=e[5]+i[1],t}function Ce(t,e,i){var n=e[0],r=e[2],a=e[4],o=e[1],s=e[3],l=e[5],h=Math.sin(i),u=Math.cos(i);return t[0]=n*u+o*h,t[1]=-n*h+o*u,t[2]=r*u+s*h,t[3]=-r*h+u*s,t[4]=u*a+h*l,t[5]=u*l-h*a,t}function Ae(t,e,i){var n=i[0],r=i[1];return t[0]=e[0]*n,t[1]=e[1]*r,t[2]=e[2]*n,t[3]=e[3]*r,t[4]=e[4]*n,t[5]=e[5]*r,t}function De(t,e){var i=e[0],n=e[2],r=e[4],a=e[1],o=e[3],s=e[5],l=i*o-a*n;return l?(l=1/l,t[0]=o*l,t[1]=-a*l,t[2]=-n*l,t[3]=i*l,t[4]=(n*s-o*r)*l,t[5]=(a*r-i*s)*l,t):null}function ke(t){var e=be();return Me(e,t),e}function Pe(t){return t>Lg||-Lg>t}function Le(t){this._target=t.target,this._life=t.life||1e3,this._delay=t.delay||0,this._initialized=!1,this.loop=null==t.loop?!1:t.loop,this.gap=t.gap||0,this.easing=t.easing||"Linear",this.onframe=t.onframe,this.ondestroy=t.ondestroy,this.onrestart=t.onrestart,this._pausedTime=0,this._paused=!1}function Oe(t){return t=Math.round(t),0>t?0:t>255?255:t}function ze(t){return t=Math.round(t),0>t?0:t>360?360:t}function Ee(t){return 0>t?0:t>1?1:t}function Re(t){return Oe(t.length&&"%"===t.charAt(t.length-1)?parseFloat(t)/100*255:parseInt(t,10))}function Be(t){return Ee(t.length&&"%"===t.charAt(t.length-1)?parseFloat(t)/100:parseFloat(t))}function Ne(t,e,i){return 0>i?i+=1:i>1&&(i-=1),1>6*i?t+(e-t)*i*6:1>2*i?e:2>3*i?t+(e-t)*(2/3-i)*6:t}function Fe(t,e,i){return t+(e-t)*i}function Ve(t,e,i,n,r){return t[0]=e,t[1]=i,t[2]=n,t[3]=r,t}function We(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t}function Ge(t,e){Yg&&We(Yg,e),Yg=Xg.put(t,Yg||e.slice())}function He(t,e){if(t){e=e||[];var i=Xg.get(t);if(i)return We(e,i);t+="";var n=t.replace(/ /g,"").toLowerCase();if(n in Zg)return We(e,Zg[n]),Ge(t,e),e;if("#"!==n.charAt(0)){var r=n.indexOf("("),a=n.indexOf(")");if(-1!==r&&a+1===n.length){var o=n.substr(0,r),s=n.substr(r+1,a-(r+1)).split(","),l=1;switch(o){case"rgba":if(4!==s.length)return void Ve(e,0,0,0,1);l=Be(s.pop());case"rgb":return 3!==s.length?void Ve(e,0,0,0,1):(Ve(e,Re(s[0]),Re(s[1]),Re(s[2]),l),Ge(t,e),e);case"hsla":return 4!==s.length?void Ve(e,0,0,0,1):(s[3]=Be(s[3]),Ze(s,e),Ge(t,e),e);case"hsl":return 3!==s.length?void Ve(e,0,0,0,1):(Ze(s,e),Ge(t,e),e);default:return}}Ve(e,0,0,0,1)}else{if(4===n.length){var h=parseInt(n.substr(1),16);return h>=0&&4095>=h?(Ve(e,(3840&h)>>4|(3840&h)>>8,240&h|(240&h)>>4,15&h|(15&h)<<4,1),Ge(t,e),e):void Ve(e,0,0,0,1)}if(7===n.length){var h=parseInt(n.substr(1),16);return h>=0&&16777215>=h?(Ve(e,(16711680&h)>>16,(65280&h)>>8,255&h,1),Ge(t,e),e):void Ve(e,0,0,0,1)}}}}function Ze(t,e){var i=(parseFloat(t[0])%360+360)%360/360,n=Be(t[1]),r=Be(t[2]),a=.5>=r?r*(n+1):r+n-r*n,o=2*r-a;return e=e||[],Ve(e,Oe(255*Ne(o,a,i+1/3)),Oe(255*Ne(o,a,i)),Oe(255*Ne(o,a,i-1/3)),1),4===t.length&&(e[3]=t[3]),e}function Xe(t){if(t){var e,i,n=t[0]/255,r=t[1]/255,a=t[2]/255,o=Math.min(n,r,a),s=Math.max(n,r,a),l=s-o,h=(s+o)/2;if(0===l)e=0,i=0;else{i=.5>h?l/(s+o):l/(2-s-o);var u=((s-n)/6+l/2)/l,c=((s-r)/6+l/2)/l,d=((s-a)/6+l/2)/l;n===s?e=d-c:r===s?e=1/3+u-d:a===s&&(e=2/3+c-u),0>e&&(e+=1),e>1&&(e-=1)}var f=[360*e,i,h];return null!=t[3]&&f.push(t[3]),f}}function Ye(t,e){var i=He(t);if(i){for(var n=0;3>n;n++)i[n]=0>e?i[n]*(1-e)|0:(255-i[n])*e+i[n]|0,i[n]>255?i[n]=255:t[n]<0&&(i[n]=0);return Qe(i,4===i.length?"rgba":"rgb")}}function je(t){var e=He(t);return e?((1<<24)+(e[0]<<16)+(e[1]<<8)+ +e[2]).toString(16).slice(1):void 0}function qe(t,e,i){if(e&&e.length&&t>=0&&1>=t){i=i||[];var n=t*(e.length-1),r=Math.floor(n),a=Math.ceil(n),o=e[r],s=e[a],l=n-r;return i[0]=Oe(Fe(o[0],s[0],l)),i[1]=Oe(Fe(o[1],s[1],l)),i[2]=Oe(Fe(o[2],s[2],l)),i[3]=Ee(Fe(o[3],s[3],l)),i}}function Ue(t,e,i){if(e&&e.length&&t>=0&&1>=t){var n=t*(e.length-1),r=Math.floor(n),a=Math.ceil(n),o=He(e[r]),s=He(e[a]),l=n-r,h=Qe([Oe(Fe(o[0],s[0],l)),Oe(Fe(o[1],s[1],l)),Oe(Fe(o[2],s[2],l)),Ee(Fe(o[3],s[3],l))],"rgba");return i?{color:h,leftIndex:r,rightIndex:a,value:n}:h}}function $e(t,e,i,n){return t=He(t),t?(t=Xe(t),null!=e&&(t[0]=ze(e)),null!=i&&(t[1]=Be(i)),null!=n&&(t[2]=Be(n)),Qe(Ze(t),"rgba")):void 0}function Ke(t,e){return t=He(t),t&&null!=e?(t[3]=Ee(e),Qe(t,"rgba")):void 0}function Qe(t,e){if(t&&t.length){var i=t[0]+","+t[1]+","+t[2];return("rgba"===e||"hsva"===e||"hsla"===e)&&(i+=","+t[3]),e+"("+i+")"}}function Je(t,e){return t[e]}function ti(t,e,i){t[e]=i}function ei(t,e,i){return(e-t)*i+t}function ii(t,e,i){return i>.5?e:t}function ni(t,e,i,n,r){var a=t.length;if(1==r)for(var o=0;a>o;o++)n[o]=ei(t[o],e[o],i);else for(var s=a&&t[0].length,o=0;a>o;o++)for(var l=0;s>l;l++)n[o][l]=ei(t[o][l],e[o][l],i)}function ri(t,e,i){var n=t.length,r=e.length;if(n!==r){var a=n>r;if(a)t.length=r;else for(var o=n;r>o;o++)t.push(1===i?e[o]:$g.call(e[o]))}for(var s=t[0]&&t[0].length,o=0;ol;l++)isNaN(t[o][l])&&(t[o][l]=e[o][l])}function ai(t,e,i){if(t===e)return!0;var n=t.length;if(n!==e.length)return!1;if(1===i){for(var r=0;n>r;r++)if(t[r]!==e[r])return!1}else for(var a=t[0].length,r=0;n>r;r++)for(var o=0;a>o;o++)if(t[r][o]!==e[r][o])return!1;return!0}function oi(t,e,i,n,r,a,o,s,l){var h=t.length;if(1==l)for(var u=0;h>u;u++)s[u]=si(t[u],e[u],i[u],n[u],r,a,o);else for(var c=t[0].length,u=0;h>u;u++)for(var d=0;c>d;d++)s[u][d]=si(t[u][d],e[u][d],i[u][d],n[u][d],r,a,o)}function si(t,e,i,n,r,a,o){var s=.5*(i-t),l=.5*(n-e);return(2*(e-i)+s+l)*o+(-3*(e-i)-2*s-l)*a+s*r+e}function li(t){if(d(t)){var e=t.length;if(d(t[0])){for(var i=[],n=0;e>n;n++)i.push($g.call(t[n]));return i}return $g.call(t)}return t}function hi(t){return t[0]=Math.floor(t[0]),t[1]=Math.floor(t[1]),t[2]=Math.floor(t[2]),"rgba("+t.join(",")+")"}function ui(t){var e=t[t.length-1].value;return d(e&&e[0])?2:1}function ci(t,e,i,n,r,a){var o=t._getter,s=t._setter,l="spline"===e,h=n.length;if(h){var u,c=n[0].value,f=d(c),p=!1,g=!1,v=f?ui(n):0;n.sort(function(t,e){return t.time-e.time}),u=n[h-1].time;for(var m=[],y=[],x=n[0].value,_=!0,w=0;h>w;w++){m.push(n[w].time/u);var b=n[w].value;if(f&&ai(b,x,v)||!f&&b===x||(_=!1),x=b,"string"==typeof b){var S=He(b);S?(b=S,p=!0):g=!0}y.push(b)}if(a||!_){for(var M=y[h-1],w=0;h-1>w;w++)f?ri(y[w],M,v):!isNaN(y[w])||isNaN(M)||g||p||(y[w]=M);f&&ri(o(t._target,r),M,v);var I,T,C,A,D,k,P=0,L=0;if(p)var O=[0,0,0,0];var z=function(t,e){var i;if(0>e)i=0;else if(L>e){for(I=Math.min(P+1,h-1),i=I;i>=0&&!(m[i]<=e);i--);i=Math.min(i,h-2)}else{for(i=P;h>i&&!(m[i]>e);i++);i=Math.min(i-1,h-2)}P=i,L=e;var n=m[i+1]-m[i];if(0!==n)if(T=(e-m[i])/n,l)if(A=y[i],C=y[0===i?i:i-1],D=y[i>h-2?h-1:i+1],k=y[i>h-3?h-1:i+2],f)oi(C,A,D,k,T,T*T,T*T*T,o(t,r),v);else{var a;if(p)a=oi(C,A,D,k,T,T*T,T*T*T,O,1),a=hi(O);else{if(g)return ii(A,D,T);a=si(C,A,D,k,T,T*T,T*T*T)}s(t,r,a)}else if(f)ni(y[i],y[i+1],T,o(t,r),v);else{var a;if(p)ni(y[i],y[i+1],T,O,1),a=hi(O);else{if(g)return ii(y[i],y[i+1],T);a=ei(y[i],y[i+1],T)}s(t,r,a)}},E=new Le({target:t._target,life:u,loop:t._loop,delay:t._delay,onframe:z,ondestroy:i});return e&&"spline"!==e&&(E.easing=e),E}}}function di(t,e,i,n,r,a,o,s){function l(){u--,u||a&&a()}b(n)?(a=r,r=n,n=0):w(r)?(a=r,r="linear",n=0):w(n)?(a=n,n=0):w(i)?(a=i,i=500):i||(i=500),t.stopAnimation(),fi(t,"",t,e,i,n,s);var h=t.animators.slice(),u=h.length;u||a&&a();for(var c=0;c0&&t.animate(e,!1).when(null==r?500:r,s).delay(a||0)}function pi(t,e,i,n){if(e){var r={};r[e]={},r[e][i]=n,t.attr(r)}else t.attr(i,n)}function gi(t,e,i,n){0>i&&(t+=i,i=-i),0>n&&(e+=n,n=-n),this.x=t,this.y=e,this.width=i,this.height=n}function vi(t){for(var e=0;t>=hv;)e|=1&t,t>>=1;return t+e}function mi(t,e,i,n){var r=e+1;if(r===i)return 1;if(n(t[r++],t[e])<0){for(;i>r&&n(t[r],t[r-1])<0;)r++;yi(t,e,r)}else for(;i>r&&n(t[r],t[r-1])>=0;)r++;return r-e}function yi(t,e,i){for(i--;i>e;){var n=t[e];t[e++]=t[i],t[i--]=n}}function xi(t,e,i,n,r){for(n===e&&n++;i>n;n++){for(var a,o=t[n],s=e,l=n;l>s;)a=s+l>>>1,r(o,t[a])<0?l=a:s=a+1;var h=n-s;switch(h){case 3:t[s+3]=t[s+2];case 2:t[s+2]=t[s+1];case 1:t[s+1]=t[s];break;default:for(;h>0;)t[s+h]=t[s+h-1],h--}t[s]=o}}function _i(t,e,i,n,r,a){var o=0,s=0,l=1;if(a(t,e[i+r])>0){for(s=n-r;s>l&&a(t,e[i+r+l])>0;)o=l,l=(l<<1)+1,0>=l&&(l=s);l>s&&(l=s),o+=r,l+=r}else{for(s=r+1;s>l&&a(t,e[i+r-l])<=0;)o=l,l=(l<<1)+1,0>=l&&(l=s);l>s&&(l=s);var h=o;o=r-l,l=r-h}for(o++;l>o;){var u=o+(l-o>>>1);a(t,e[i+u])>0?o=u+1:l=u}return l}function wi(t,e,i,n,r,a){var o=0,s=0,l=1;if(a(t,e[i+r])<0){for(s=r+1;s>l&&a(t,e[i+r-l])<0;)o=l,l=(l<<1)+1,0>=l&&(l=s);l>s&&(l=s);var h=o;o=r-l,l=r-h}else{for(s=n-r;s>l&&a(t,e[i+r+l])>=0;)o=l,l=(l<<1)+1,0>=l&&(l=s);l>s&&(l=s),o+=r,l+=r}for(o++;l>o;){var u=o+(l-o>>>1);a(t,e[i+u])<0?l=u:o=u+1}return l}function bi(t,e){function i(t,e){l[c]=t,h[c]=e,c+=1}function n(){for(;c>1;){var t=c-2;if(t>=1&&h[t-1]<=h[t]+h[t+1]||t>=2&&h[t-2]<=h[t]+h[t-1])h[t-1]h[t+1])break;a(t)}}function r(){for(;c>1;){var t=c-2;t>0&&h[t-1]=r?o(n,r,a,u):s(n,r,a,u)))}function o(i,n,r,a){var o=0;for(o=0;n>o;o++)d[o]=t[i+o];var s=0,l=r,h=i;if(t[h++]=t[l++],0!==--a){if(1===n){for(o=0;a>o;o++)t[h+o]=t[l+o];return void(t[h+a]=d[s])}for(var c,f,p,g=u;;){c=0,f=0,p=!1;do if(e(t[l],d[s])<0){if(t[h++]=t[l++],f++,c=0,0===--a){p=!0;break}}else if(t[h++]=d[s++],c++,f=0,1===--n){p=!0;break}while(g>(c|f));if(p)break;do{if(c=wi(t[l],d,s,n,0,e),0!==c){for(o=0;c>o;o++)t[h+o]=d[s+o];if(h+=c,s+=c,n-=c,1>=n){p=!0;break}}if(t[h++]=t[l++],0===--a){p=!0;break}if(f=_i(d[s],t,l,a,0,e),0!==f){for(o=0;f>o;o++)t[h+o]=t[l+o];if(h+=f,l+=f,a-=f,0===a){p=!0;break}}if(t[h++]=d[s++],1===--n){p=!0;break}g--}while(c>=uv||f>=uv);if(p)break;0>g&&(g=0),g+=2}if(u=g,1>u&&(u=1),1===n){for(o=0;a>o;o++)t[h+o]=t[l+o];t[h+a]=d[s]}else{if(0===n)throw new Error;for(o=0;n>o;o++)t[h+o]=d[s+o]}}else for(o=0;n>o;o++)t[h+o]=d[s+o]}function s(i,n,r,a){var o=0;for(o=0;a>o;o++)d[o]=t[r+o];var s=i+n-1,l=a-1,h=r+a-1,c=0,f=0;if(t[h--]=t[s--],0!==--n){if(1===a){for(h-=n,s-=n,f=h+1,c=s+1,o=n-1;o>=0;o--)t[f+o]=t[c+o];return void(t[h]=d[l])}for(var p=u;;){var g=0,v=0,m=!1;do if(e(d[l],t[s])<0){if(t[h--]=t[s--],g++,v=0,0===--n){m=!0;break}}else if(t[h--]=d[l--],v++,g=0,1===--a){m=!0;break}while(p>(g|v));if(m)break;do{if(g=n-wi(d[l],t,i,n,n-1,e),0!==g){for(h-=g,s-=g,n-=g,f=h+1,c=s+1,o=g-1;o>=0;o--)t[f+o]=t[c+o];if(0===n){m=!0;break}}if(t[h--]=d[l--],1===--a){m=!0;break}if(v=a-_i(t[s],d,0,a,a-1,e),0!==v){for(h-=v,l-=v,a-=v,f=h+1,c=l+1,o=0;v>o;o++)t[f+o]=d[c+o];if(1>=a){m=!0;break}}if(t[h--]=t[s--],0===--n){m=!0;break}p--}while(g>=uv||v>=uv);if(m)break;0>p&&(p=0),p+=2}if(u=p,1>u&&(u=1),1===a){for(h-=n,s-=n,f=h+1,c=s+1,o=n-1;o>=0;o--)t[f+o]=t[c+o];t[h]=d[l]}else{if(0===a)throw new Error;for(c=h-(a-1),o=0;a>o;o++)t[c+o]=d[o]}}else for(c=h-(a-1),o=0;a>o;o++)t[c+o]=d[o]}var l,h,u=uv,c=0,d=[];l=[],h=[],this.mergeRuns=n,this.forceMergeRuns=r,this.pushRun=i}function Si(t,e,i,n){i||(i=0),n||(n=t.length);var r=n-i;if(!(2>r)){var a=0;if(hv>r)return a=mi(t,i,n,e),void xi(t,i,n,i+a,e);var o=new bi(t,e),s=vi(r);do{if(a=mi(t,i,n,e),s>a){var l=r;l>s&&(l=s),xi(t,i,i+l,i+a,e),a=l}o.pushRun(i,a),o.mergeRuns(),r-=a,i+=a}while(0!==r);o.forceMergeRuns()}}function Mi(t,e){return t.zlevel===e.zlevel?t.z===e.z?t.z2-e.z2:t.z-e.z:t.zlevel-e.zlevel}function Ii(t,e,i){var n=null==e.x?0:e.x,r=null==e.x2?1:e.x2,a=null==e.y?0:e.y,o=null==e.y2?0:e.y2;e.global||(n=n*i.width+i.x,r=r*i.width+i.x,a=a*i.height+i.y,o=o*i.height+i.y),n=isNaN(n)?0:n,r=isNaN(r)?1:r,a=isNaN(a)?0:a,o=isNaN(o)?0:o;var s=t.createLinearGradient(n,a,r,o);return s}function Ti(t,e,i){var n=i.width,r=i.height,a=Math.min(n,r),o=null==e.x?.5:e.x,s=null==e.y?.5:e.y,l=null==e.r?.5:e.r;e.global||(o=o*n+i.x,s=s*r+i.y,l*=a);var h=t.createRadialGradient(o,s,0,o,s,l);return h}function Ci(){return!1}function Ai(t,e,i){var n=cg(),r=e.getWidth(),a=e.getHeight(),o=n.style;return o&&(o.position="absolute",o.left=0,o.top=0,o.width=r+"px",o.height=a+"px",n.setAttribute("data-zr-dom-id",t)),n.width=r*i,n.height=a*i,n}function Di(t){if("string"==typeof t){var e=bv.get(t);return e&&e.image}return t}function ki(t,e,i,n,r){if(t){if("string"==typeof t){if(e&&e.__zrImageSrc===t||!i)return e;var a=bv.get(t),o={hostEl:i,cb:n,cbPayload:r};return a?(e=a.image,!Li(e)&&a.pending.push(o)):(!e&&(e=new Image),e.onload=e.onerror=Pi,bv.put(t,e.__cachedImgObj={image:e,pending:[o]}),e.src=e.__zrImageSrc=t),e}return t}return e}function Pi(){var t=this.__cachedImgObj;this.onload=this.onerror=this.__cachedImgObj=null;for(var e=0;ea;a++)r=Math.max(Yi(n[a],e).width,r);return Mv>Iv&&(Mv=0,Sv={}),Mv++,Sv[i]=r,r}function Ei(t,e,i,n,r,a,o){return a?Bi(t,e,i,n,r,a,o):Ri(t,e,i,n,r,o)}function Ri(t,e,i,n,r,a){var o=ji(t,e,r,a),s=zi(t,e);r&&(s+=r[1]+r[3]);var l=o.outerHeight,h=Ni(0,s,i),u=Fi(0,l,n),c=new gi(h,u,s,l);return c.lineHeight=o.lineHeight,c}function Bi(t,e,i,n,r,a,o){var s=qi(t,{rich:a,truncate:o,font:e,textAlign:i,textPadding:r}),l=s.outerWidth,h=s.outerHeight,u=Ni(0,l,i),c=Fi(0,h,n);return new gi(u,c,l,h)}function Ni(t,e,i){return"right"===i?t-=e:"center"===i&&(t-=e/2),t}function Fi(t,e,i){return"middle"===i?t-=e/2:"bottom"===i&&(t-=e),t}function Vi(t,e,i){var n=e.x,r=e.y,a=e.height,o=e.width,s=a/2,l="left",h="top";switch(t){case"left":n-=i,r+=s,l="right",h="middle";break;case"right":n+=i+o,r+=s,h="middle";break;case"top":n+=o/2,r-=i,l="center",h="bottom";break;case"bottom":n+=o/2,r+=a+i,l="center";break;case"inside":n+=o/2,r+=s,l="center",h="middle";break;case"insideLeft":n+=i,r+=s,h="middle";break;case"insideRight":n+=o-i,r+=s,l="right",h="middle";break;case"insideTop":n+=o/2,r+=i,l="center";break;case"insideBottom":n+=o/2,r+=a-i,l="center",h="bottom";break;case"insideTopLeft":n+=i,r+=i;break;case"insideTopRight":n+=o-i,r+=i,l="right";break;case"insideBottomLeft":n+=i,r+=a-i,h="bottom";break;case"insideBottomRight":n+=o-i,r+=a-i,l="right",h="bottom"}return{x:n,y:r,textAlign:l,textVerticalAlign:h}}function Wi(t,e,i,n,r){if(!e)return"";var a=(t+"").split("\n");r=Gi(e,i,n,r);for(var o=0,s=a.length;s>o;o++)a[o]=Hi(a[o],r);return a.join("\n")}function Gi(t,e,i,n){n=o({},n),n.font=e;var i=D(i,"...");n.maxIterations=D(n.maxIterations,2);var r=n.minChar=D(n.minChar,0);n.cnCharWidth=zi("国",e);var a=n.ascCharWidth=zi("a",e);n.placeholder=D(n.placeholder,"");for(var s=t=Math.max(0,t-1),l=0;r>l&&s>=a;l++)s-=a;var h=zi(i);return h>s&&(i="",h=0),s=t-h,n.ellipsis=i,n.ellipsisWidth=h,n.contentWidth=s,n.containerWidth=t,n}function Hi(t,e){var i=e.containerWidth,n=e.font,r=e.contentWidth;if(!i)return"";var a=zi(t,n);if(i>=a)return t;for(var o=0;;o++){if(r>=a||o>=e.maxIterations){t+=e.ellipsis;break}var s=0===o?Zi(t,r,e.ascCharWidth,e.cnCharWidth):a>0?Math.floor(t.length*r/a):0;t=t.substr(0,s),a=zi(t,n)}return""===t&&(t=e.placeholder),t}function Zi(t,e,i,n){for(var r=0,a=0,o=t.length;o>a&&e>r;a++){var s=t.charCodeAt(a);r+=s>=0&&127>=s?i:n}return a}function Xi(t){return zi("国",t)}function Yi(t,e){return Av.measureText(t,e)}function ji(t,e,i,n){null!=t&&(t+="");var r=Xi(e),a=t?t.split("\n"):[],o=a.length*r,s=o;if(i&&(s+=i[0]+i[2]),t&&n){var l=n.outerHeight,h=n.outerWidth;if(null!=l&&s>l)t="",a=[];else if(null!=h)for(var u=Gi(h-(i?i[1]+i[3]:0),e,n.ellipsis,{minChar:n.minChar,placeholder:n.placeholder}),c=0,d=a.length;d>c;c++)a[c]=Hi(a[c],u)}return{lines:a,height:o,outerHeight:s,lineHeight:r}}function qi(t,e){var i={lines:[],width:0,height:0};if(null!=t&&(t+=""),!t)return i;for(var n,r=Tv.lastIndex=0;null!=(n=Tv.exec(t));){var a=n.index;a>r&&Ui(i,t.substring(r,a)),Ui(i,n[2],n[1]),r=Tv.lastIndex}rf)return{lines:[],width:0,height:0};x.textWidth=zi(x.text,b);var M=_.textWidth,I=null==M||"auto"===M;if("string"==typeof M&&"%"===M.charAt(M.length-1))x.percentWidth=M,h.push(x),M=0;else{if(I){M=x.textWidth;var T=_.textBackgroundColor,C=T&&T.image;C&&(C=Di(C),Li(C)&&(M=Math.max(M,C.width*S/C.height)))}var A=w?w[1]+w[3]:0;M+=A;var P=null!=d?d-m:null;null!=P&&M>P&&(!I||A>P?(x.text="",x.textWidth=M=0):(x.text=Wi(x.text,P-A,b,c.ellipsis,{minChar:c.minChar}),x.textWidth=zi(x.text,b),M=x.textWidth+A))}m+=x.width=M,_&&(v=Math.max(v,x.lineHeight))}g.width=m,g.lineHeight=v,s+=v,l=Math.max(l,m)}i.outerWidth=i.width=D(e.textWidth,l),i.outerHeight=i.height=D(e.textHeight,s),u&&(i.outerWidth+=u[1]+u[3],i.outerHeight+=u[0]+u[2]);for(var p=0;pl&&(o+=l,l=-l),0>h&&(s+=h,h=-h),"number"==typeof u?i=n=r=a=u:u instanceof Array?1===u.length?i=n=r=a=u[0]:2===u.length?(i=r=u[0],n=a=u[1]):3===u.length?(i=u[0],n=a=u[1],r=u[2]):(i=u[0],n=u[1],r=u[2],a=u[3]):i=n=r=a=0;var c;i+n>l&&(c=i+n,i*=l/c,n*=l/c),r+a>l&&(c=r+a,r*=l/c,a*=l/c),n+r>h&&(c=n+r,n*=h/c,r*=h/c),i+a>h&&(c=i+a,i*=h/c,a*=h/c),t.moveTo(o+i,s),t.lineTo(o+l-n,s),0!==n&&t.arc(o+l-n,s+n,n,-Math.PI/2,0),t.lineTo(o+l,s+h-r),0!==r&&t.arc(o+l-r,s+h-r,r,0,Math.PI/2),t.lineTo(o+a,s+h),0!==a&&t.arc(o+a,s+h-a,a,Math.PI/2,Math.PI),t.lineTo(o,s+i),0!==i&&t.arc(o+i,s+i,i,Math.PI,1.5*Math.PI)}function Qi(t){return Ji(t),f(t.rich,Ji),t}function Ji(t){if(t){t.font=$i(t);var e=t.textAlign;"middle"===e&&(e="center"),t.textAlign=null==e||Dv[e]?e:"left";var i=t.textVerticalAlign||t.textBaseline;"center"===i&&(i="middle"),t.textVerticalAlign=null==i||kv[i]?i:"top";var n=t.textPadding;n&&(t.textPadding=L(t.textPadding))}}function tn(t,e,i,n,r,a){n.rich?nn(t,e,i,n,r):en(t,e,i,n,r,a)}function en(t,e,i,n,r,a){var o=a&&a.style,s=o&&"text"===a.type,l=n.font||Cv;s&&l===(o.font||Cv)||(e.font=l);var h=t.__computedFont;t.__styleFont!==l&&(t.__styleFont=l,h=t.__computedFont=e.font);var u=n.textPadding,c=t.__textCotentBlock;(!c||t.__dirtyText)&&(c=t.__textCotentBlock=ji(i,h,u,n.truncate));var d=c.outerHeight,f=c.lines,p=c.lineHeight,g=un(d,n,r),v=g.baseX,m=g.baseY,y=g.textAlign||"left",x=g.textVerticalAlign;an(e,n,r,v,m);var _=Fi(m,d,x),w=v,b=_,S=sn(n);if(S||u){var M=zi(i,h),I=M;u&&(I+=u[1]+u[3]);var T=Ni(v,I,y);S&&ln(t,e,n,T,_,I,d),u&&(w=gn(v,y,u),b+=u[0])}e.textAlign=y,e.textBaseline="middle";for(var C=0;CT&&(_=b[T],!_.textAlign||"left"===_.textAlign);)on(t,e,_,n,M,m,C,"left"),I-=_.width,C+=_.width,T++;for(;D>=0&&(_=b[D],"right"===_.textAlign);)on(t,e,_,n,M,m,A,"right"),I-=_.width,A-=_.width,D--;for(C+=(a-(C-v)-(y-A)-I)/2;D>=T;)_=b[T],on(t,e,_,n,M,m,C+_.width/2,"center"),C+=_.width,T++;m+=M}}function an(t,e,i,n,r){if(i&&e.textRotation){var a=e.textOrigin;"center"===a?(n=i.width/2+i.x,r=i.height/2+i.y):a&&(n=a[0]+i.x,r=a[1]+i.y),t.translate(n,r),t.rotate(-e.textRotation),t.translate(-n,-r)}}function on(t,e,i,n,r,a,o,s){var l=n.rich[i.styleName]||{};l.text=i.text;var h=i.textVerticalAlign,u=a+r/2;"top"===h?u=a+i.height/2:"bottom"===h&&(u=a+r-i.height/2),!i.isLineHolder&&sn(l)&&ln(t,e,l,"right"===s?o-i.width:"center"===s?o-i.width/2:o,u-i.height/2,i.width,i.height);var c=i.textPadding;c&&(o=gn(o,s,c),u-=i.height/2-c[2]-i.textHeight/2),cn(e,"shadowBlur",k(l.textShadowBlur,n.textShadowBlur,0)),cn(e,"shadowColor",l.textShadowColor||n.textShadowColor||"transparent"),cn(e,"shadowOffsetX",k(l.textShadowOffsetX,n.textShadowOffsetX,0)),cn(e,"shadowOffsetY",k(l.textShadowOffsetY,n.textShadowOffsetY,0)),cn(e,"textAlign",s),cn(e,"textBaseline","middle"),cn(e,"font",i.font||Cv);var d=dn(l.textStroke||n.textStroke,p),f=fn(l.textFill||n.textFill),p=D(l.textStrokeWidth,n.textStrokeWidth);d&&(cn(e,"lineWidth",p),cn(e,"strokeStyle",d),e.strokeText(i.text,o,u)),f&&(cn(e,"fillStyle",f),e.fillText(i.text,o,u))}function sn(t){return t.textBackgroundColor||t.textBorderWidth&&t.textBorderColor}function ln(t,e,i,n,r,a,o){var s=i.textBackgroundColor,l=i.textBorderWidth,h=i.textBorderColor,u=b(s);if(cn(e,"shadowBlur",i.textBoxShadowBlur||0),cn(e,"shadowColor",i.textBoxShadowColor||"transparent"),cn(e,"shadowOffsetX",i.textBoxShadowOffsetX||0),cn(e,"shadowOffsetY",i.textBoxShadowOffsetY||0),u||l&&h){e.beginPath();var c=i.textBorderRadius;c?Ki(e,{x:n,y:r,width:a,height:o,r:c}):e.rect(n,r,a,o),e.closePath()}if(u)if(cn(e,"fillStyle",s),null!=i.fillOpacity){var d=e.globalAlpha;e.globalAlpha=i.fillOpacity*i.opacity,e.fill(),e.globalAlpha=d}else e.fill();else if(w(s))cn(e,"fillStyle",s(i)),e.fill();else if(S(s)){var f=s.image;f=ki(f,null,t,hn,s),f&&Li(f)&&e.drawImage(f,n,r,a,o)}if(l&&h)if(cn(e,"lineWidth",l),cn(e,"strokeStyle",h),null!=i.strokeOpacity){var d=e.globalAlpha;e.globalAlpha=i.strokeOpacity*i.opacity,e.stroke(),e.globalAlpha=d}else e.stroke()}function hn(t,e){e.image=t}function un(t,e,i){var n=e.x||0,r=e.y||0,a=e.textAlign,o=e.textVerticalAlign;if(i){var s=e.textPosition;if(s instanceof Array)n=i.x+pn(s[0],i.width),r=i.y+pn(s[1],i.height);else{var l=Vi(s,i,e.textDistance);n=l.x,r=l.y,a=a||l.textAlign,o=o||l.textVerticalAlign}var h=e.textOffset;h&&(n+=h[0],r+=h[1])}return{baseX:n,baseY:r,textAlign:a,textVerticalAlign:o}}function cn(t,e,i){return t[e]=fv(t,e,i),t[e]}function dn(t,e){return null==t||0>=e||"transparent"===t||"none"===t?null:t.image||t.colorStops?"#000":t}function fn(t){return null==t||"none"===t?null:t.image||t.colorStops?"#000":t}function pn(t,e){return"string"==typeof t?t.lastIndexOf("%")>=0?parseFloat(t)/100*e:parseFloat(t):t}function gn(t,e,i){return"right"===e?t-i[1]:"center"===e?t+i[3]/2-i[1]/2:t+i[3]}function vn(t,e){return null!=t&&(t||e.textBackgroundColor||e.textBorderWidth&&e.textBorderColor||e.textPadding)}function mn(t){t=t||{},rv.call(this,t);for(var e in t)t.hasOwnProperty(e)&&"style"!==e&&(this[e]=t[e]);this.style=new gv(t.style,this),this._rect=null,this.__clipPaths=[] +}function yn(t){mn.call(this,t)}function xn(t){return parseInt(t,10)}function _n(t){return t?t.__builtin__?!0:"function"!=typeof t.resize||"function"!=typeof t.refresh?!1:!0:!1}function wn(t,e,i){return Nv.copy(t.getBoundingRect()),t.transform&&Nv.applyTransform(t.transform),Fv.width=e,Fv.height=i,!Nv.intersect(Fv)}function bn(t,e){if(t==e)return!1;if(!t||!e||t.length!==e.length)return!0;for(var i=0;in;n++){var a=i[n];!t.emphasis[e].hasOwnProperty(a)&&t[e].hasOwnProperty(a)&&(t.emphasis[e][a]=t[e][a])}}}function Vn(t){return!rm(t)||am(t)||t instanceof Date?t:t.value}function Wn(t){return rm(t)&&!(t instanceof Array)}function Gn(t,e){e=(e||[]).slice();var i=p(t||[],function(t){return{exist:t}});return nm(e,function(t,n){if(rm(t)){for(var r=0;r=i.length&&i.push({option:t})}}),i}function Hn(t){var e=N();nm(t,function(t){var i=t.exist;i&&e.set(i.id,t)}),nm(t,function(t){var i=t.option;O(!i||null==i.id||!e.get(i.id)||e.get(i.id)===t,"id duplicates: "+(i&&i.id)),i&&null!=i.id&&e.set(i.id,t),!t.keyInfo&&(t.keyInfo={})}),nm(t,function(t,i){var n=t.exist,r=t.option,a=t.keyInfo;if(rm(r)){if(a.name=null!=r.name?r.name+"":n?n.name:om+i,n)a.id=n.id;else if(null!=r.id)a.id=r.id+"";else{var o=0;do a.id="\x00"+a.name+"\x00"+o++;while(e.get(a.id))}e.set(a.id,t)}})}function Zn(t){var e=t.name;return!(!e||!e.indexOf(om))}function Xn(t){return rm(t)&&t.id&&0===(t.id+"").indexOf("\x00_ec_\x00")}function Yn(t,e){return null!=e.dataIndexInside?e.dataIndexInside:null!=e.dataIndex?_(e.dataIndex)?p(e.dataIndex,function(e){return t.indexOfRawIndex(e)}):t.indexOfRawIndex(e.dataIndex):null!=e.name?_(e.name)?p(e.name,function(e){return t.indexOfName(e)}):t.indexOfName(e.name):void 0}function jn(){var t="__\x00ec_inner_"+lm++ +"_"+Math.random().toFixed(5);return function(e){return e[t]||(e[t]={})}}function qn(t,e,i){if(b(e)){var n={};n[e+"Index"]=0,e=n}var r=i&&i.defaultMainType;!r||Un(e,r+"Index")||Un(e,r+"Id")||Un(e,r+"Name")||(e[r+"Index"]=0);var a={};return nm(e,function(n,r){var n=e[r];if("dataIndex"===r||"dataIndexInside"===r)return void(a[r]=n);var o=r.match(/^(\w+)(Index|Id|Name)$/)||[],s=o[1],l=(o[2]||"").toLowerCase();if(!(!s||!l||null==n||"index"===l&&"none"===n||i&&i.includeMainTypes&&h(i.includeMainTypes,s)<0)){var u={mainType:s};("index"!==l||"all"!==n)&&(u[l]=n);var c=t.queryComponents(u);a[s+"Models"]=c,a[s+"Model"]=c[0]}}),a}function Un(t,e){return t&&t.hasOwnProperty(e)}function $n(t,e,i){t.setAttribute?t.setAttribute(e,i):t[e]=i}function Kn(t,e){return t.getAttribute?t.getAttribute(e):t[e]}function Qn(t){return"auto"===t?tg.domSupported?"html":"richText":t||"html"}function Jn(t){var e={main:"",sub:""};return t&&(t=t.split(hm),e.main=t[0]||"",e.sub=t[1]||""),e}function tr(t){O(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(t),'componentType "'+t+'" illegal')}function er(t){t.$constructor=t,t.extend=function(t){var e=this,i=function(){t.$constructor?t.$constructor.apply(this,arguments):e.apply(this,arguments)};return o(i.prototype,t),i.extend=this.extend,i.superCall=nr,i.superApply=rr,u(i,this),i.superClass=e,i}}function ir(t){var e=["__\x00is_clz",cm++,Math.random().toFixed(3)].join("_");t.prototype[e]=!0,t.isInstance=function(t){return!(!t||!t[e])}}function nr(t,e){var i=P(arguments,2);return this.superClass.prototype[e].apply(t,i)}function rr(t,e,i){return this.superClass.prototype[e].apply(t,i)}function ar(t,e){function i(t){var e=n[t.main];return e&&e[um]||(e=n[t.main]={},e[um]=!0),e}e=e||{};var n={};if(t.registerClass=function(t,e){if(e)if(tr(e),e=Jn(e),e.sub){if(e.sub!==um){var r=i(e);r[e.sub]=t}}else n[e.main]=t;return t},t.getClass=function(t,e,i){var r=n[t];if(r&&r[um]&&(r=e?r[e]:null),i&&!r)throw new Error(e?"Component "+t+"."+(e||"")+" not exists. Load it first.":t+".type should be specified.");return r},t.getClassesByMainType=function(t){t=Jn(t);var e=[],i=n[t.main];return i&&i[um]?f(i,function(t,i){i!==um&&e.push(t)}):e.push(i),e},t.hasClass=function(t){return t=Jn(t),!!n[t.main]},t.getAllClassMainTypes=function(){var t=[];return f(n,function(e,i){t.push(i)}),t},t.hasSubTypes=function(t){t=Jn(t);var e=n[t.main];return e&&e[um]},t.parseClassType=Jn,e.registerWhenExtend){var r=t.extend;r&&(t.extend=function(e){var i=r.call(this,e);return t.registerClass(i,e.type)})}return t}function or(t){return t>-xm&&xm>t}function sr(t){return t>xm||-xm>t}function lr(t,e,i,n,r){var a=1-r;return a*a*(a*t+3*r*e)+r*r*(r*n+3*a*i)}function hr(t,e,i,n,r){var a=1-r;return 3*(((e-t)*a+2*(i-e)*r)*a+(n-i)*r*r)}function ur(t,e,i,n,r,a){var o=n+3*(e-i)-t,s=3*(i-2*e+t),l=3*(e-t),h=t-r,u=s*s-3*o*l,c=s*l-9*o*h,d=l*l-3*s*h,f=0;if(or(u)&&or(c))if(or(s))a[0]=0;else{var p=-l/s;p>=0&&1>=p&&(a[f++]=p)}else{var g=c*c-4*u*d;if(or(g)){var v=c/u,p=-s/o+v,m=-v/2;p>=0&&1>=p&&(a[f++]=p),m>=0&&1>=m&&(a[f++]=m)}else if(g>0){var y=ym(g),x=u*s+1.5*o*(-c+y),_=u*s+1.5*o*(-c-y);x=0>x?-mm(-x,bm):mm(x,bm),_=0>_?-mm(-_,bm):mm(_,bm);var p=(-s-(x+_))/(3*o);p>=0&&1>=p&&(a[f++]=p)}else{var w=(2*u*s-3*o*c)/(2*ym(u*u*u)),b=Math.acos(w)/3,S=ym(u),M=Math.cos(b),p=(-s-2*S*M)/(3*o),m=(-s+S*(M+wm*Math.sin(b)))/(3*o),I=(-s+S*(M-wm*Math.sin(b)))/(3*o);p>=0&&1>=p&&(a[f++]=p),m>=0&&1>=m&&(a[f++]=m),I>=0&&1>=I&&(a[f++]=I)}}return f}function cr(t,e,i,n,r){var a=6*i-12*e+6*t,o=9*e+3*n-3*t-9*i,s=3*e-3*t,l=0;if(or(o)){if(sr(a)){var h=-s/a;h>=0&&1>=h&&(r[l++]=h)}}else{var u=a*a-4*o*s;if(or(u))r[0]=-a/(2*o);else if(u>0){var c=ym(u),h=(-a+c)/(2*o),d=(-a-c)/(2*o);h>=0&&1>=h&&(r[l++]=h),d>=0&&1>=d&&(r[l++]=d)}}return l}function dr(t,e,i,n,r,a){var o=(e-t)*r+t,s=(i-e)*r+e,l=(n-i)*r+i,h=(s-o)*r+o,u=(l-s)*r+s,c=(u-h)*r+h;a[0]=t,a[1]=o,a[2]=h,a[3]=c,a[4]=c,a[5]=u,a[6]=l,a[7]=n}function fr(t,e,i,n,r,a,o,s,l,h,u){var c,d,f,p,g,v=.005,m=1/0;Sm[0]=l,Sm[1]=h;for(var y=0;1>y;y+=.05)Mm[0]=lr(t,i,r,o,y),Mm[1]=lr(e,n,a,s,y),p=xg(Sm,Mm),m>p&&(c=y,m=p);m=1/0;for(var x=0;32>x&&!(_m>v);x++)d=c-v,f=c+v,Mm[0]=lr(t,i,r,o,d),Mm[1]=lr(e,n,a,s,d),p=xg(Mm,Sm),d>=0&&m>p?(c=d,m=p):(Im[0]=lr(t,i,r,o,f),Im[1]=lr(e,n,a,s,f),g=xg(Im,Sm),1>=f&&m>g?(c=f,m=g):v*=.5);return u&&(u[0]=lr(t,i,r,o,c),u[1]=lr(e,n,a,s,c)),ym(m)}function pr(t,e,i,n){var r=1-n;return r*(r*t+2*n*e)+n*n*i}function gr(t,e,i,n){return 2*((1-n)*(e-t)+n*(i-e))}function vr(t,e,i,n,r){var a=t-2*e+i,o=2*(e-t),s=t-n,l=0;if(or(a)){if(sr(o)){var h=-s/o;h>=0&&1>=h&&(r[l++]=h)}}else{var u=o*o-4*a*s;if(or(u)){var h=-o/(2*a);h>=0&&1>=h&&(r[l++]=h)}else if(u>0){var c=ym(u),h=(-o+c)/(2*a),d=(-o-c)/(2*a);h>=0&&1>=h&&(r[l++]=h),d>=0&&1>=d&&(r[l++]=d)}}return l}function mr(t,e,i){var n=t+i-2*e;return 0===n?.5:(t-e)/n}function yr(t,e,i,n,r){var a=(e-t)*n+t,o=(i-e)*n+e,s=(o-a)*n+a;r[0]=t,r[1]=a,r[2]=s,r[3]=s,r[4]=o,r[5]=i}function xr(t,e,i,n,r,a,o,s,l){var h,u=.005,c=1/0;Sm[0]=o,Sm[1]=s;for(var d=0;1>d;d+=.05){Mm[0]=pr(t,i,r,d),Mm[1]=pr(e,n,a,d);var f=xg(Sm,Mm);c>f&&(h=d,c=f)}c=1/0;for(var p=0;32>p&&!(_m>u);p++){var g=h-u,v=h+u;Mm[0]=pr(t,i,r,g),Mm[1]=pr(e,n,a,g);var f=xg(Mm,Sm);if(g>=0&&c>f)h=g,c=f;else{Im[0]=pr(t,i,r,v),Im[1]=pr(e,n,a,v);var m=xg(Im,Sm);1>=v&&c>m?(h=v,c=m):u*=.5}}return l&&(l[0]=pr(t,i,r,h),l[1]=pr(e,n,a,h)),ym(c)}function _r(t,e,i){if(0!==t.length){var n,r=t[0],a=r[0],o=r[0],s=r[1],l=r[1];for(n=1;nu;u++){var p=d(t,i,r,o,zm[u]);l[0]=Tm(p,l[0]),h[0]=Cm(p,h[0])}for(f=c(e,n,a,s,Em),u=0;f>u;u++){var g=d(e,n,a,s,Em[u]);l[1]=Tm(g,l[1]),h[1]=Cm(g,h[1])}l[0]=Tm(t,l[0]),h[0]=Cm(t,h[0]),l[0]=Tm(o,l[0]),h[0]=Cm(o,h[0]),l[1]=Tm(e,l[1]),h[1]=Cm(e,h[1]),l[1]=Tm(s,l[1]),h[1]=Cm(s,h[1])}function Sr(t,e,i,n,r,a,o,s){var l=mr,h=pr,u=Cm(Tm(l(t,i,r),1),0),c=Cm(Tm(l(e,n,a),1),0),d=h(t,i,r,u),f=h(e,n,a,c);o[0]=Tm(t,r,d),o[1]=Tm(e,a,f),s[0]=Cm(t,r,d),s[1]=Cm(e,a,f)}function Mr(t,e,i,n,r,a,o,s,l){var h=oe,u=se,c=Math.abs(r-a);if(1e-4>c%km&&c>1e-4)return s[0]=t-i,s[1]=e-n,l[0]=t+i,void(l[1]=e+n);if(Pm[0]=Dm(r)*i+t,Pm[1]=Am(r)*n+e,Lm[0]=Dm(a)*i+t,Lm[1]=Am(a)*n+e,h(s,Pm,Lm),u(l,Pm,Lm),r%=km,0>r&&(r+=km),a%=km,0>a&&(a+=km),r>a&&!o?a+=km:a>r&&o&&(r+=km),o){var d=a;a=r,r=d}for(var f=0;a>f;f+=Math.PI/2)f>r&&(Om[0]=Dm(f)*i+t,Om[1]=Am(f)*n+e,h(s,Om,s),u(l,Om,l))}function Ir(t,e,i,n,r,a,o){if(0===r)return!1;var s=r,l=0,h=t;if(o>e+s&&o>n+s||e-s>o&&n-s>o||a>t+s&&a>i+s||t-s>a&&i-s>a)return!1;if(t===i)return Math.abs(a-t)<=s/2;l=(e-n)/(t-i),h=(t*n-i*e)/(t-i);var u=l*a-o+h,c=u*u/(l*l+1);return s/2*s/2>=c}function Tr(t,e,i,n,r,a,o,s,l,h,u){if(0===l)return!1;var c=l;if(u>e+c&&u>n+c&&u>a+c&&u>s+c||e-c>u&&n-c>u&&a-c>u&&s-c>u||h>t+c&&h>i+c&&h>r+c&&h>o+c||t-c>h&&i-c>h&&r-c>h&&o-c>h)return!1;var d=fr(t,e,i,n,r,a,o,s,h,u,null);return c/2>=d}function Cr(t,e,i,n,r,a,o,s,l){if(0===o)return!1;var h=o;if(l>e+h&&l>n+h&&l>a+h||e-h>l&&n-h>l&&a-h>l||s>t+h&&s>i+h&&s>r+h||t-h>s&&i-h>s&&r-h>s)return!1;var u=xr(t,e,i,n,r,a,s,l,null);return h/2>=u}function Ar(t){return t%=Um,0>t&&(t+=Um),t}function Dr(t,e,i,n,r,a,o,s,l){if(0===o)return!1;var h=o;s-=t,l-=e;var u=Math.sqrt(s*s+l*l);if(u-h>i||i>u+h)return!1;if(Math.abs(n-r)%$m<1e-4)return!0;if(a){var c=n;n=Ar(r),r=Ar(c)}else n=Ar(n),r=Ar(r);n>r&&(r+=$m);var d=Math.atan2(l,s);return 0>d&&(d+=$m),d>=n&&r>=d||d+$m>=n&&r>=d+$m}function kr(t,e,i,n,r,a){if(a>e&&a>n||e>a&&n>a)return 0;if(n===e)return 0;var o=e>n?1:-1,s=(a-e)/(n-e);(1===s||0===s)&&(o=e>n?.5:-.5);var l=s*(i-t)+t;return l===r?1/0:l>r?o:0}function Pr(t,e){return Math.abs(t-e)e&&h>n&&h>a&&h>s||e>h&&n>h&&a>h&&s>h)return 0;var u=ur(e,n,a,s,h,ty);if(0===u)return 0;for(var c,d,f=0,p=-1,g=0;u>g;g++){var v=ty[g],m=0===v||1===v?.5:1,y=lr(t,i,r,o,v);l>y||(0>p&&(p=cr(e,n,a,s,ey),ey[1]1&&Lr(),c=lr(e,n,a,s,ey[0]),p>1&&(d=lr(e,n,a,s,ey[1]))),f+=2==p?vc?m:-m:vd?m:-m:d>s?m:-m:vc?m:-m:c>s?m:-m)}return f}function zr(t,e,i,n,r,a,o,s){if(s>e&&s>n&&s>a||e>s&&n>s&&a>s)return 0;var l=vr(e,n,a,s,ty);if(0===l)return 0;var h=mr(e,n,a);if(h>=0&&1>=h){for(var u=0,c=pr(e,n,a,h),d=0;l>d;d++){var f=0===ty[d]||1===ty[d]?.5:1,p=pr(t,i,r,ty[d]);o>p||(u+=ty[d]c?f:-f:c>a?f:-f)}return u}var f=0===ty[0]||1===ty[0]?.5:1,p=pr(t,i,r,ty[0]);return o>p?0:e>a?f:-f}function Er(t,e,i,n,r,a,o,s){if(s-=e,s>i||-i>s)return 0;var l=Math.sqrt(i*i-s*s);ty[0]=-l,ty[1]=l;var h=Math.abs(n-r);if(1e-4>h)return 0;if(1e-4>h%Qm){n=0,r=Qm;var u=a?1:-1;return o>=ty[0]+t&&o<=ty[1]+t?u:0}if(a){var l=n;n=Ar(r),r=Ar(l)}else n=Ar(n),r=Ar(r);n>r&&(r+=Qm);for(var c=0,d=0;2>d;d++){var f=ty[d];if(f+t>o){var p=Math.atan2(s,f),u=a?1:-1;0>p&&(p=Qm+p),(p>=n&&r>=p||p+Qm>=n&&r>=p+Qm)&&(p>Math.PI/2&&p<1.5*Math.PI&&(u=-u),c+=u)}}return c}function Rr(t,e,i,n,r){for(var a=0,o=0,s=0,l=0,h=0,u=0;u1&&(i||(a+=kr(o,s,l,h,n,r))),1==u&&(o=t[u],s=t[u+1],l=o,h=s),c){case Km.M:l=t[u++],h=t[u++],o=l,s=h;break;case Km.L:if(i){if(Ir(o,s,t[u],t[u+1],e,n,r))return!0}else a+=kr(o,s,t[u],t[u+1],n,r)||0;o=t[u++],s=t[u++];break;case Km.C:if(i){if(Tr(o,s,t[u++],t[u++],t[u++],t[u++],t[u],t[u+1],e,n,r))return!0}else a+=Or(o,s,t[u++],t[u++],t[u++],t[u++],t[u],t[u+1],n,r)||0;o=t[u++],s=t[u++];break;case Km.Q:if(i){if(Cr(o,s,t[u++],t[u++],t[u],t[u+1],e,n,r))return!0}else a+=zr(o,s,t[u++],t[u++],t[u],t[u+1],n,r)||0;o=t[u++],s=t[u++];break;case Km.A:var d=t[u++],f=t[u++],p=t[u++],g=t[u++],v=t[u++],m=t[u++],y=(t[u++],1-t[u++]),x=Math.cos(v)*p+d,_=Math.sin(v)*g+f;u>1?a+=kr(o,s,x,_,n,r):(l=x,h=_);var w=(n-d)*g/p+d;if(i){if(Dr(d,f,g,v,v+m,y,e,w,r))return!0}else a+=Er(d,f,g,v,v+m,y,w,r);o=Math.cos(v+m)*p+d,s=Math.sin(v+m)*g+f;break;case Km.R:l=o=t[u++],h=s=t[u++];var b=t[u++],S=t[u++],x=l+b,_=h+S;if(i){if(Ir(l,h,x,h,e,n,r)||Ir(x,h,x,_,e,n,r)||Ir(x,_,l,_,e,n,r)||Ir(l,_,l,h,e,n,r))return!0}else a+=kr(x,h,x,_,n,r),a+=kr(l,_,l,h,n,r);break;case Km.Z:if(i){if(Ir(o,s,l,h,e,n,r))return!0}else a+=kr(o,s,l,h,n,r);o=l,s=h}}return i||Pr(s,h)||(a+=kr(o,s,l,h,n,r)||0),0!==a}function Br(t,e,i){return Rr(t,0,!1,e,i)}function Nr(t,e,i,n){return Rr(t,e,!0,i,n)}function Fr(t){mn.call(this,t),this.path=null}function Vr(t,e,i,n,r,a,o,s,l,h,u){var c=l*(fy/180),d=dy(c)*(t-i)/2+cy(c)*(e-n)/2,f=-1*cy(c)*(t-i)/2+dy(c)*(e-n)/2,p=d*d/(o*o)+f*f/(s*s);p>1&&(o*=uy(p),s*=uy(p));var g=(r===a?-1:1)*uy((o*o*s*s-o*o*f*f-s*s*d*d)/(o*o*f*f+s*s*d*d))||0,v=g*o*f/s,m=g*-s*d/o,y=(t+i)/2+dy(c)*v-cy(c)*m,x=(e+n)/2+cy(c)*v+dy(c)*m,_=vy([1,0],[(d-v)/o,(f-m)/s]),w=[(d-v)/o,(f-m)/s],b=[(-1*d-v)/o,(-1*f-m)/s],S=vy(w,b);gy(w,b)<=-1&&(S=fy),gy(w,b)>=1&&(S=0),0===a&&S>0&&(S-=2*fy),1===a&&0>S&&(S+=2*fy),u.addData(h,y,x,o,s,_,S,c,a)}function Wr(t){if(!t)return new qm;for(var e,i=0,n=0,r=i,a=n,o=new qm,s=qm.CMD,l=t.match(my),h=0;hg;g++)f[g]=parseFloat(f[g]);for(var v=0;p>v;){var m,y,x,_,w,b,S,M=i,I=n;switch(d){case"l":i+=f[v++],n+=f[v++],u=s.L,o.addData(u,i,n);break;case"L":i=f[v++],n=f[v++],u=s.L,o.addData(u,i,n);break;case"m":i+=f[v++],n+=f[v++],u=s.M,o.addData(u,i,n),r=i,a=n,d="l";break;case"M":i=f[v++],n=f[v++],u=s.M,o.addData(u,i,n),r=i,a=n,d="L";break;case"h":i+=f[v++],u=s.L,o.addData(u,i,n);break;case"H":i=f[v++],u=s.L,o.addData(u,i,n);break;case"v":n+=f[v++],u=s.L,o.addData(u,i,n);break;case"V":n=f[v++],u=s.L,o.addData(u,i,n);break;case"C":u=s.C,o.addData(u,f[v++],f[v++],f[v++],f[v++],f[v++],f[v++]),i=f[v-2],n=f[v-1];break;case"c":u=s.C,o.addData(u,f[v++]+i,f[v++]+n,f[v++]+i,f[v++]+n,f[v++]+i,f[v++]+n),i+=f[v-2],n+=f[v-1];break;case"S":m=i,y=n;var T=o.len(),C=o.data;e===s.C&&(m+=i-C[T-4],y+=n-C[T-3]),u=s.C,M=f[v++],I=f[v++],i=f[v++],n=f[v++],o.addData(u,m,y,M,I,i,n);break;case"s":m=i,y=n;var T=o.len(),C=o.data;e===s.C&&(m+=i-C[T-4],y+=n-C[T-3]),u=s.C,M=i+f[v++],I=n+f[v++],i+=f[v++],n+=f[v++],o.addData(u,m,y,M,I,i,n);break;case"Q":M=f[v++],I=f[v++],i=f[v++],n=f[v++],u=s.Q,o.addData(u,M,I,i,n);break;case"q":M=f[v++]+i,I=f[v++]+n,i+=f[v++],n+=f[v++],u=s.Q,o.addData(u,M,I,i,n);break;case"T":m=i,y=n;var T=o.len(),C=o.data;e===s.Q&&(m+=i-C[T-4],y+=n-C[T-3]),i=f[v++],n=f[v++],u=s.Q,o.addData(u,m,y,i,n);break;case"t":m=i,y=n;var T=o.len(),C=o.data;e===s.Q&&(m+=i-C[T-4],y+=n-C[T-3]),i+=f[v++],n+=f[v++],u=s.Q,o.addData(u,m,y,i,n);break;case"A":x=f[v++],_=f[v++],w=f[v++],b=f[v++],S=f[v++],M=i,I=n,i=f[v++],n=f[v++],u=s.A,Vr(M,I,i,n,b,S,x,_,w,u,o);break;case"a":x=f[v++],_=f[v++],w=f[v++],b=f[v++],S=f[v++],M=i,I=n,i+=f[v++],n+=f[v++],u=s.A,Vr(M,I,i,n,b,S,x,_,w,u,o)}}("z"===d||"Z"===d)&&(u=s.Z,o.addData(u),i=r,n=a),e=u}return o.toStatic(),o}function Gr(t,e){var i=Wr(t);return e=e||{},e.buildPath=function(t){if(t.setData){t.setData(i.data);var e=t.getContext();e&&t.rebuildPath(e)}else{var e=t;i.rebuildPath(e)}},e.applyTransform=function(t){hy(i,t),this.dirty(!0)},e}function Hr(t,e){return new Fr(Gr(t,e))}function Zr(t,e){return Fr.extend(Gr(t,e))}function Xr(t,e){for(var i=[],n=t.length,r=0;n>r;r++){var a=t[r];a.path||a.createPathProxy(),a.__dirtyPath&&a.buildPath(a.path,a.shape,!0),i.push(a.path)}var o=new Fr(e);return o.createPathProxy(),o.buildPath=function(t){t.appendPath(i);var e=t.getContext();e&&t.rebuildPath(e)},o}function Yr(t,e,i,n,r,a,o){var s=.5*(i-t),l=.5*(n-e);return(2*(e-i)+s+l)*o+(-3*(e-i)-2*s-l)*a+s*r+e}function jr(t,e,i){var n=e.points,r=e.smooth;if(n&&n.length>=2){if(r&&"spline"!==r){var a=Ty(n,r,i,e.smoothConstraint);t.moveTo(n[0][0],n[0][1]);for(var o=n.length,s=0;(i?o:o-1)>s;s++){var l=a[2*s],h=a[2*s+1],u=n[(s+1)%o];t.bezierCurveTo(l[0],l[1],h[0],h[1],u[0],u[1])}}else{"spline"===r&&(n=Iy(n,i)),t.moveTo(n[0][0],n[0][1]);for(var s=1,c=n.length;c>s;s++)t.lineTo(n[s][0],n[s][1])}i&&t.closePath()}}function qr(t,e,i){var n=t.cpx2,r=t.cpy2;return null===n||null===r?[(i?hr:lr)(t.x1,t.cpx1,t.cpx2,t.x2,e),(i?hr:lr)(t.y1,t.cpy1,t.cpy2,t.y2,e)]:[(i?gr:pr)(t.x1,t.cpx1,t.x2,e),(i?gr:pr)(t.y1,t.cpy1,t.y2,e)]}function Ur(t){mn.call(this,t),this._displayables=[],this._temporaryDisplayables=[],this._cursor=0,this.notClear=!0}function $r(t){return Fr.extend(t)}function Kr(t,e){return Zr(t,e)}function Qr(t,e,i,n){var r=Hr(t,e);return i&&("center"===n&&(i=ta(i,r.getBoundingRect())),ea(r,i)),r}function Jr(t,e,i){var n=new yn({style:{image:t,x:e.x,y:e.y,width:e.width,height:e.height},onload:function(t){if("center"===i){var r={width:t.width,height:t.height};n.setStyle(ta(e,r))}}});return n}function ta(t,e){var i,n=e.width/e.height,r=t.height*n;r<=t.width?i=t.height:(r=t.width,i=r/n);var a=t.x+t.width/2,o=t.y+t.height/2;return{x:a-r/2,y:o-i/2,width:r,height:i}}function ea(t,e){if(t.applyTransform){var i=t.getBoundingRect(),n=i.calculateTransform(e);t.applyTransform(n)}}function ia(t){var e=t.shape,i=t.style.lineWidth;return Fy(2*e.x1)===Fy(2*e.x2)&&(e.x1=e.x2=ra(e.x1,i,!0)),Fy(2*e.y1)===Fy(2*e.y2)&&(e.y1=e.y2=ra(e.y1,i,!0)),t}function na(t){var e=t.shape,i=t.style.lineWidth,n=e.x,r=e.y,a=e.width,o=e.height;return e.x=ra(e.x,i,!0),e.y=ra(e.y,i,!0),e.width=Math.max(ra(n+a,i,!1)-e.x,0===a?0:1),e.height=Math.max(ra(r+o,i,!1)-e.y,0===o?0:1),t}function ra(t,e,i){var n=Fy(2*t);return(n+Fy(e))%2===0?n/2:(n+(i?1:-1))/2}function aa(t){return null!=t&&"none"!==t}function oa(t){if("string"!=typeof t)return t;var e=Zy.get(t);return e||(e=Ye(t,-.1),1e4>Xy&&(Zy.set(t,e),Xy++)),e}function sa(t){if(t.__hoverStlDirty){t.__hoverStlDirty=!1;var e=t.__hoverStl;if(!e)return void(t.__normalStl=null);var i=t.__normalStl={},n=t.style;for(var r in e)null!=e[r]&&(i[r]=n[r]);i.fill=n.fill,i.stroke=n.stroke}}function la(t){var e=t.__hoverStl;if(e&&!t.__highlighted){var i=t.useHoverLayer;t.__highlighted=i?"layer":"plain";var n=t.__zr;if(n||!i){var r=t,a=t.style;i&&(r=n.addHover(t),a=r.style),Da(a),i||sa(r),a.extendFrom(e),ha(a,e,"fill"),ha(a,e,"stroke"),Aa(a),i||(t.dirty(!1),t.z2+=1)}}}function ha(t,e,i){!aa(e[i])&&aa(t[i])&&(t[i]=oa(t[i]))}function ua(t){t.__highlighted&&(ca(t),t.__highlighted=!1)}function ca(t){var e=t.__highlighted;if("layer"===e)t.__zr&&t.__zr.removeHover(t);else if(e){var i=t.style,n=t.__normalStl;n&&(Da(i),t.setStyle(n),Aa(i),t.z2-=1)}}function da(t,e){t.isGroup?t.traverse(function(t){!t.isGroup&&e(t)}):e(t)}function fa(t,e){e=t.__hoverStl=e!==!1&&(e||{}),t.__hoverStlDirty=!0,t.__highlighted&&(ua(t),la(t))}function pa(t){return t&&t.__isEmphasisEntered}function ga(t){this.__hoverSilentOnTouch&&t.zrByTouch||!this.__isEmphasisEntered&&da(this,la)}function va(t){this.__hoverSilentOnTouch&&t.zrByTouch||!this.__isEmphasisEntered&&da(this,ua)}function ma(){this.__isEmphasisEntered=!0,da(this,la)}function ya(){this.__isEmphasisEntered=!1,da(this,ua)}function xa(t,e,i){t.isGroup?t.traverse(function(t){!t.isGroup&&fa(t,t.hoverStyle||e)}):fa(t,t.hoverStyle||e),_a(t,i)}function _a(t,e){var i=e===!1;if(t.__hoverSilentOnTouch=null!=e&&e.hoverSilentOnTouch,!i||t.__hoverStyleTrigger){var n=i?"off":"on";t[n]("mouseover",ga)[n]("mouseout",va),t[n]("emphasis",ma)[n]("normal",ya),t.__hoverStyleTrigger=!i}}function wa(t,e,i,n,r,a,o){r=r||Gy;var s,l=r.labelFetcher,h=r.labelDataIndex,u=r.labelDimIndex,c=i.getShallow("show"),d=n.getShallow("show");(c||d)&&(l&&(s=l.getFormattedLabel(h,"normal",null,u)),null==s&&(s=w(r.defaultText)?r.defaultText(h,r):r.defaultText));var f=c?s:null,p=d?D(l?l.getFormattedLabel(h,"emphasis",null,u):null,s):null;(null!=f||null!=p)&&(ba(t,i,a,r),ba(e,n,o,r,!0)),t.text=f,e.text=p}function ba(t,e,i,n,r){return Ma(t,e,n,r),i&&o(t,i),t}function Sa(t,e,i){var n,r={isRectText:!0};i===!1?n=!0:r.autoColor=i,Ma(t,e,r,n)}function Ma(t,e,i,n){if(i=i||Gy,i.isRectText){var r=e.getShallow("position")||(n?null:"inside");"outside"===r&&(r="top"),t.textPosition=r,t.textOffset=e.getShallow("offset");var a=e.getShallow("rotate");null!=a&&(a*=Math.PI/180),t.textRotation=a,t.textDistance=D(e.getShallow("distance"),n?null:5)}var o,s=e.ecModel,l=s&&s.option.textStyle,h=Ia(e);if(h){o={};for(var u in h)if(h.hasOwnProperty(u)){var c=e.getModel(["rich",u]);Ta(o[u]={},c,l,i,n)}}return t.rich=o,Ta(t,e,l,i,n,!0),i.forceRich&&!i.textStyle&&(i.textStyle={}),t}function Ia(t){for(var e;t&&t!==t.ecModel;){var i=(t.option||Gy).rich;if(i){e=e||{};for(var n in i)i.hasOwnProperty(n)&&(e[n]=1)}t=t.parentModel}return e}function Ta(t,e,i,n,r,a){i=!r&&i||Gy,t.textFill=Ca(e.getShallow("color"),n)||i.color,t.textStroke=Ca(e.getShallow("textBorderColor"),n)||i.textBorderColor,t.textStrokeWidth=D(e.getShallow("textBorderWidth"),i.textBorderWidth),t.insideRawTextPosition=t.textPosition,r||(a&&(t.insideRollbackOpt=n,Aa(t)),null==t.textFill&&(t.textFill=n.autoColor)),t.fontStyle=e.getShallow("fontStyle")||i.fontStyle,t.fontWeight=e.getShallow("fontWeight")||i.fontWeight,t.fontSize=e.getShallow("fontSize")||i.fontSize,t.fontFamily=e.getShallow("fontFamily")||i.fontFamily,t.textAlign=e.getShallow("align"),t.textVerticalAlign=e.getShallow("verticalAlign")||e.getShallow("baseline"),t.textLineHeight=e.getShallow("lineHeight"),t.textWidth=e.getShallow("width"),t.textHeight=e.getShallow("height"),t.textTag=e.getShallow("tag"),a&&n.disableBox||(t.textBackgroundColor=Ca(e.getShallow("backgroundColor"),n),t.textPadding=e.getShallow("padding"),t.textBorderColor=Ca(e.getShallow("borderColor"),n),t.textBorderWidth=e.getShallow("borderWidth"),t.textBorderRadius=e.getShallow("borderRadius"),t.textBoxShadowColor=e.getShallow("shadowColor"),t.textBoxShadowBlur=e.getShallow("shadowBlur"),t.textBoxShadowOffsetX=e.getShallow("shadowOffsetX"),t.textBoxShadowOffsetY=e.getShallow("shadowOffsetY")),t.textShadowColor=e.getShallow("textShadowColor")||i.textShadowColor,t.textShadowBlur=e.getShallow("textShadowBlur")||i.textShadowBlur,t.textShadowOffsetX=e.getShallow("textShadowOffsetX")||i.textShadowOffsetX,t.textShadowOffsetY=e.getShallow("textShadowOffsetY")||i.textShadowOffsetY}function Ca(t,e){return"auto"!==t?t:e&&e.autoColor?e.autoColor:null}function Aa(t){var e=t.insideRollbackOpt;if(e&&null==t.textFill){var i,n=e.useInsideStyle,r=t.insideRawTextPosition,a=e.autoColor;n!==!1&&(n===!0||e.isRectText&&r&&"string"==typeof r&&r.indexOf("inside")>=0)?(i={textFill:null,textStroke:t.textStroke,textStrokeWidth:t.textStrokeWidth},t.textFill="#fff",null==t.textStroke&&(t.textStroke=a,null==t.textStrokeWidth&&(t.textStrokeWidth=2))):null!=a&&(i={textFill:null},t.textFill=a),i&&(t.insideRollback=i)}}function Da(t){var e=t.insideRollback;e&&(t.textFill=e.textFill,t.textStroke=e.textStroke,t.textStrokeWidth=e.textStrokeWidth,t.insideRollback=null)}function ka(t,e){var i=e||e.getModel("textStyle");return z([t.fontStyle||i&&i.getShallow("fontStyle")||"",t.fontWeight||i&&i.getShallow("fontWeight")||"",(t.fontSize||i&&i.getShallow("fontSize")||12)+"px",t.fontFamily||i&&i.getShallow("fontFamily")||"sans-serif"].join(" "))}function Pa(t,e,i,n,r,a){"function"==typeof r&&(a=r,r=null);var o=n&&n.isAnimationEnabled();if(o){var s=t?"Update":"",l=n.getShallow("animationDuration"+s),h=n.getShallow("animationEasing"+s),u=n.getShallow("animationDelay"+s);"function"==typeof u&&(u=u(r,n.getAnimationDelayParams?n.getAnimationDelayParams(e,r):null)),"function"==typeof l&&(l=l(r)),l>0?e.animateTo(i,l,u||0,h,a,!!a):(e.stopAnimation(),e.attr(i),a&&a())}else e.stopAnimation(),e.attr(i),a&&a()}function La(t,e,i,n,r){Pa(!0,t,e,i,n,r)}function Oa(t,e,i,n,r){Pa(!1,t,e,i,n,r)}function za(t,e){for(var i=Se([]);t&&t!==e;)Ie(i,t.getLocalTransform(),i),t=t.parent;return i}function Ea(t,e,i){return e&&!d(e)&&(e=Og.getLocalTransform(e)),i&&(e=De([],e)),ae([],t,e)}function Ra(t,e,i){var n=0===e[4]||0===e[5]||0===e[0]?1:Math.abs(2*e[4]/e[0]),r=0===e[4]||0===e[5]||0===e[2]?1:Math.abs(2*e[4]/e[2]),a=["left"===t?-n:"right"===t?n:0,"top"===t?-r:"bottom"===t?r:0];return a=Ea(a,e,i),Math.abs(a[0])>Math.abs(a[1])?a[0]>0?"right":"left":a[1]>0?"bottom":"top"}function Ba(t,e,i){function n(t){var e={};return t.traverse(function(t){!t.isGroup&&t.anid&&(e[t.anid]=t)}),e}function r(t){var e={position:H(t.position),rotation:t.rotation};return t.shape&&(e.shape=o({},t.shape)),e}if(t&&e){var a=n(t);e.traverse(function(t){if(!t.isGroup&&t.anid){var e=a[t.anid];if(e){var n=r(t);t.attr(r(e)),La(t,n,i,t.dataIndex)}}})}}function Na(t,e){return p(t,function(t){var i=t[0];i=Vy(i,e.x),i=Wy(i,e.x+e.width);var n=t[1];return n=Vy(n,e.y),n=Wy(n,e.y+e.height),[i,n]})}function Fa(t,e){var i=Vy(t.x,e.x),n=Wy(t.x+t.width,e.x+e.width),r=Vy(t.y,e.y),a=Wy(t.y+t.height,e.y+e.height);return n>=i&&a>=r?{x:i,y:r,width:n-i,height:a-r}:void 0}function Va(t,e,i){e=o({rectHover:!0},e);var n=e.style={strokeNoScale:!0};return i=i||{x:-1,y:-1,width:2,height:2},t?0===t.indexOf("image://")?(n.image=t.slice(8),s(n,i),new yn(e)):Qr(t.replace("path://",""),e,i,"center"):void 0}function Wa(t,e,i){this.parentModel=e,this.ecModel=i,this.option=t}function Ga(t,e,i){for(var n=0;n=0&&i.push(t)}),i}t.topologicalTravel=function(t,e,n,r){function a(t){l[t].entryCount--,0===l[t].entryCount&&h.push(t)}function o(t){u[t]=!0,a(t)}if(t.length){var s=i(e),l=s.graph,h=s.noEntryList,u={};for(f(t,function(t){u[t]=!0});h.length;){var c=h.pop(),d=l[c],p=!!u[c];p&&(n.call(r,c,d.originalDeps.slice()),delete u[c]),f(d.successor,p?o:a)}f(u,function(){throw new Error("Circle dependency may exists")})}}}function ja(t){return t.replace(/^\s+/,"").replace(/\s+$/,"")}function qa(t,e,i,n){var r=e[1]-e[0],a=i[1]-i[0];if(0===r)return 0===a?i[0]:(i[0]+i[1])/2;if(n)if(r>0){if(t<=e[0])return i[0];if(t>=e[1])return i[1]}else{if(t>=e[0])return i[0];if(t<=e[1])return i[1]}else{if(t===e[0])return i[0];if(t===e[1])return i[1]}return(t-e[0])/r*a+i[0]}function Ua(t,e){switch(t){case"center":case"middle":t="50%";break;case"left":case"top":t="0%";break;case"right":case"bottom":t="100%"}return"string"==typeof t?ja(t).match(/%$/)?parseFloat(t)/100*e:parseFloat(t):null==t?0/0:+t}function $a(t,e,i){return null==e&&(e=10),e=Math.min(Math.max(0,e),20),t=(+t).toFixed(e),i?t:+t}function Ka(t){return t.sort(function(t,e){return t-e}),t}function Qa(t){if(t=+t,isNaN(t))return 0;for(var e=1,i=0;Math.round(t*e)/e!==t;)e*=10,i++;return i}function Ja(t){var e=t.toString(),i=e.indexOf("e");if(i>0){var n=+e.slice(i+1);return 0>n?-n:0}var r=e.indexOf(".");return 0>r?0:e.length-1-r}function to(t,e){var i=Math.log,n=Math.LN10,r=Math.floor(i(t[1]-t[0])/n),a=Math.round(i(Math.abs(e[1]-e[0]))/n),o=Math.min(Math.max(-r+a,0),20);return isFinite(o)?o:20}function eo(t,e,i){if(!t[e])return 0;var n=g(t,function(t,e){return t+(isNaN(e)?0:e)},0);if(0===n)return 0;for(var r=Math.pow(10,i),a=p(t,function(t){return(isNaN(t)?0:t)/n*r*100}),o=100*r,s=p(a,function(t){return Math.floor(t)}),l=g(s,function(t,e){return t+e},0),h=p(a,function(t,e){return t-s[e]});o>l;){for(var u=Number.NEGATIVE_INFINITY,c=null,d=0,f=h.length;f>d;++d)h[d]>u&&(u=h[d],c=d);++s[c],h[c]=0,++l}return s[e]/r}function io(t){var e=2*Math.PI;return(t%e+e)%e}function no(t){return t>-tx&&tx>t}function ro(t){if(t instanceof Date)return t;if("string"==typeof t){var e=ix.exec(t);if(!e)return new Date(0/0);if(e[8]){var i=+e[4]||0;return"Z"!==e[8].toUpperCase()&&(i-=e[8].slice(0,3)),new Date(Date.UTC(+e[1],+(e[2]||1)-1,+e[3]||1,i,+(e[5]||0),+e[6]||0,+e[7]||0))}return new Date(+e[1],+(e[2]||1)-1,+e[3]||1,+e[4]||0,+(e[5]||0),+e[6]||0,+e[7]||0)}return new Date(null==t?0/0:Math.round(t))}function ao(t){return Math.pow(10,oo(t))}function oo(t){return Math.floor(Math.log(t)/Math.LN10)}function so(t,e){var i,n=oo(t),r=Math.pow(10,n),a=t/r;return i=e?1.5>a?1:2.5>a?2:4>a?3:7>a?5:10:1>a?1:2>a?2:3>a?3:5>a?5:10,t=i*r,n>=-20?+t.toFixed(0>n?-n:0):t}function lo(t,e){var i=(t.length-1)*e+1,n=Math.floor(i),r=+t[n-1],a=i-n;return a?r+a*(t[n]-r):r}function ho(t){function e(t,i,n){return t.interval[n]s;s++)a[s]<=i&&(a[s]=i,o[s]=s?1:1-n),i=a[s],n=o[s];a[0]===a[1]&&o[0]*o[1]!==1?t.splice(r,1):r++}return t}function uo(t){return t-parseFloat(t)>=0}function co(t){return isNaN(t)?"-":(t=(t+"").split("."),t[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,"$1,")+(t.length>1?"."+t[1]:""))}function fo(t,e){return t=(t||"").toLowerCase().replace(/-(.)/g,function(t,e){return e.toUpperCase()}),e&&t&&(t=t.charAt(0).toUpperCase()+t.slice(1)),t}function po(t){return null==t?"":(t+"").replace(ax,function(t,e){return ox[e]})}function go(t,e,i){_(e)||(e=[e]);var n=e.length;if(!n)return"";for(var r=e[0].$vars||[],a=0;as;s++)for(var l=0;l':'':{renderMode:r,content:"{marker"+a+"|} ",style:{color:i}}:"" +}function yo(t,e){return t+="","0000".substr(0,e-t.length)+t}function xo(t,e,i){("week"===t||"month"===t||"quarter"===t||"half-year"===t||"year"===t)&&(t="MM-dd\nyyyy");var n=ro(e),r=i?"UTC":"",a=n["get"+r+"FullYear"](),o=n["get"+r+"Month"]()+1,s=n["get"+r+"Date"](),l=n["get"+r+"Hours"](),h=n["get"+r+"Minutes"](),u=n["get"+r+"Seconds"](),c=n["get"+r+"Milliseconds"]();return t=t.replace("MM",yo(o,2)).replace("M",o).replace("yyyy",a).replace("yy",a%100).replace("dd",yo(s,2)).replace("d",s).replace("hh",yo(l,2)).replace("h",l).replace("mm",yo(h,2)).replace("m",h).replace("ss",yo(u,2)).replace("s",u).replace("SSS",yo(c,3))}function _o(t){return t?t.charAt(0).toUpperCase()+t.substr(1):t}function wo(t,e,i,n,r){var a=0,o=0;null==n&&(n=1/0),null==r&&(r=1/0);var s=0;e.eachChild(function(l,h){var u,c,d=l.position,f=l.getBoundingRect(),p=e.childAt(h+1),g=p&&p.getBoundingRect();if("horizontal"===t){var v=f.width+(g?-g.x+f.x:0);u=a+v,u>n||l.newline?(a=0,u=v,o+=s+i,s=f.height):s=Math.max(s,f.height)}else{var m=f.height+(g?-g.y+f.y:0);c=o+m,c>r||l.newline?(a+=s+i,o=0,c=m,s=f.width):s=Math.max(s,f.width)}l.newline||(d[0]=a,d[1]=o,"horizontal"===t?a=u+i:o=c+i)})}function bo(t,e,i){i=rx(i||0);var n=e.width,r=e.height,a=Ua(t.left,n),o=Ua(t.top,r),s=Ua(t.right,n),l=Ua(t.bottom,r),h=Ua(t.width,n),u=Ua(t.height,r),c=i[2]+i[0],d=i[1]+i[3],f=t.aspect;switch(isNaN(h)&&(h=n-s-d-a),isNaN(u)&&(u=r-l-c-o),null!=f&&(isNaN(h)&&isNaN(u)&&(f>n/r?h=.8*n:u=.8*r),isNaN(h)&&(h=f*u),isNaN(u)&&(u=h/f)),isNaN(a)&&(a=n-s-h-d),isNaN(o)&&(o=r-l-u-c),t.left||t.right){case"center":a=n/2-h/2-i[3];break;case"right":a=n-h-d}switch(t.top||t.bottom){case"middle":case"center":o=r/2-u/2-i[0];break;case"bottom":o=r-u-c}a=a||0,o=o||0,isNaN(h)&&(h=n-d-a-(s||0)),isNaN(u)&&(u=r-c-o-(l||0));var p=new gi(a+i[3],o+i[0],h,u);return p.margin=i,p}function So(t,e,i){function n(i,n){var o={},l=0,h={},u=0,c=2;if(dx(i,function(e){h[e]=t[e]}),dx(i,function(t){r(e,t)&&(o[t]=h[t]=e[t]),a(o,t)&&l++,a(h,t)&&u++}),s[n])return a(e,i[1])?h[i[2]]=null:a(e,i[2])&&(h[i[1]]=null),h;if(u!==c&&l){if(l>=c)return o;for(var d=0;dn;n++)if(t[n].length>e)return t[n];return t[i-1]}function Ao(t){var e=t.get("coordinateSystem"),i={coordSysName:e,coordSysDims:[],axisMap:N(),categoryAxisMap:N()},n=Mx[e];return n?(n(t,i,i.axisMap,i.categoryAxisMap),i):void 0}function Do(t){return"category"===t.get("type")}function ko(t){this.fromDataset=t.fromDataset,this.data=t.data||(t.sourceFormat===Ax?{}:[]),this.sourceFormat=t.sourceFormat||Dx,this.seriesLayoutBy=t.seriesLayoutBy||Px,this.dimensionsDefine=t.dimensionsDefine,this.encodeDefine=t.encodeDefine&&N(t.encodeDefine),this.startIndex=t.startIndex||0,this.dimensionsDetectCount=t.dimensionsDetectCount}function Po(t){var e=t.option.source,i=Dx;if(I(e))i=kx;else if(_(e)){0===e.length&&(i=Tx);for(var n=0,r=e.length;r>n;n++){var a=e[n];if(null!=a){if(_(a)){i=Tx;break}if(S(a)){i=Cx;break}}}}else if(S(e)){for(var o in e)if(e.hasOwnProperty(o)&&d(e[o])){i=Ax;break}}else if(null!=e)throw new Error("Invalid data");Ox(t).sourceFormat=i}function Lo(t){return Ox(t).source}function Oo(t){Ox(t).datasetMap=N()}function zo(t){var e=t.option,i=e.data,n=I(i)?kx:Ix,r=!1,a=e.seriesLayoutBy,o=e.sourceHeader,s=e.dimensions,l=Vo(t);if(l){var h=l.option;i=h.source,n=Ox(l).sourceFormat,r=!0,a=a||h.seriesLayoutBy,null==o&&(o=h.sourceHeader),s=s||h.dimensions}var u=Eo(i,n,a,o,s),c=e.encode;!c&&l&&(c=Fo(t,l,i,n,a,u)),Ox(t).source=new ko({data:i,fromDataset:r,seriesLayoutBy:a,sourceFormat:n,dimensionsDefine:u.dimensionsDefine,startIndex:u.startIndex,dimensionsDetectCount:u.dimensionsDetectCount,encodeDefine:c})}function Eo(t,e,i,n,r){if(!t)return{dimensionsDefine:Ro(r)};var a,o,s;if(e===Tx)"auto"===n||null==n?Bo(function(t){null!=t&&"-"!==t&&(b(t)?null==o&&(o=1):o=0)},i,t,10):o=n?1:0,r||1!==o||(r=[],Bo(function(t,e){r[e]=null!=t?t:""},i,t)),a=r?r.length:i===Lx?t.length:t[0]?t[0].length:null;else if(e===Cx)r||(r=No(t),s=!0);else if(e===Ax)r||(r=[],s=!0,f(t,function(t,e){r.push(e)}));else if(e===Ix){var l=Vn(t[0]);a=_(l)&&l.length||1}var h;return s&&f(r,function(t,e){"name"===(S(t)?t.name:t)&&(h=e)}),{startIndex:o,dimensionsDefine:Ro(r),dimensionsDetectCount:a,potentialNameDimIndex:h}}function Ro(t){if(t){var e=N();return p(t,function(t){if(t=o({},S(t)?t:{name:t}),null==t.name)return t;t.name+="",null==t.displayName&&(t.displayName=t.name);var i=e.get(t.name);return i?t.name+="-"+i.count++:e.set(t.name,{count:1}),t})}}function Bo(t,e,i,n){if(null==n&&(n=1/0),e===Lx)for(var r=0;rr;r++)t(i[r]?i[r][0]:null,r);else for(var a=i[0]||[],r=0;rr;r++)t(a[r],r)}function No(t){for(var e,i=0;ix&&null==y;x++)Go(i,n,r,a.dimensionsDefine,a.startIndex,x)||(y=x);if(null!=y){s.value=y;var _=a.potentialNameDimIndex||Math.max(y-1,0);h.push(_),l.push(_)}}return l.length&&(s.itemName=l),h.length&&(s.seriesName=h),s}function Vo(t){var e=t.option,i=e.data;return i?void 0:t.ecModel.getComponent("dataset",e.datasetIndex||0)}function Wo(t,e){return Go(t.data,t.sourceFormat,t.seriesLayoutBy,t.dimensionsDefine,t.startIndex,e)}function Go(t,e,i,n,r,a){function o(t){return null!=t&&isFinite(t)&&""!==t?!1:b(t)&&"-"!==t?!0:void 0}var s,l=5;if(I(t))return!1;var h;if(n&&(h=n[a],h=S(h)?h.name:h),e===Tx)if(i===Lx){for(var u=t[a],c=0;c<(u||[]).length&&l>c;c++)if(null!=(s=o(u[r+c])))return s}else for(var c=0;cc;c++){var d=t[r+c];if(d&&null!=(s=o(d[a])))return s}else if(e===Cx){if(!h)return;for(var c=0;cc;c++){var f=t[c];if(f&&null!=(s=o(f[h])))return s}}else if(e===Ax){if(!h)return;var u=t[h];if(!u||I(u))return!1;for(var c=0;cc;c++)if(null!=(s=o(u[c])))return s}else if(e===Ix)for(var c=0;cc;c++){var f=t[c],p=Vn(f);if(!_(p))return!1;if(null!=(s=o(p[a])))return s}return!1}function Ho(t,e){if(e){var i=e.seiresIndex,n=e.seriesId,r=e.seriesName;return null!=i&&t.componentIndex!==i||null!=n&&t.id!==n||null!=r&&t.name!==r}}function Zo(t,e){var i=t.color&&!t.colorLayer;f(e,function(e,a){"colorLayer"===a&&i||yx.hasClass(a)||("object"==typeof e?t[a]=t[a]?r(t[a],e,!1):n(e):null==t[a]&&(t[a]=e))})}function Xo(t){t=t,this.option={},this.option[zx]=1,this._componentsMap=N({series:[]}),this._seriesIndices,this._seriesIndicesMap,Zo(t,this._theme.option),r(t,_x,!1),this.mergeOption(t)}function Yo(t,e){_(e)||(e=e?[e]:[]);var i={};return f(e,function(e){i[e]=(t.get(e)||[]).slice()}),i}function jo(t,e,i){var n=e.type?e.type:i?i.subType:yx.determineSubType(t,e);return n}function qo(t,e){t._seriesIndicesMap=N(t._seriesIndices=p(e,function(t){return t.componentIndex})||[])}function Uo(t,e){return e.hasOwnProperty("subType")?v(t,function(t){return t.subType===e.subType}):t}function $o(t){f(Rx,function(e){this[e]=y(t[e],t)},this)}function Ko(){this._coordinateSystems=[]}function Qo(t){this._api=t,this._timelineOptions=[],this._mediaList=[],this._mediaDefault,this._currentMediaIndices=[],this._optionBackup,this._newBaseOption}function Jo(t,e,i){var n,r,a=[],o=[],s=t.timeline;if(t.baseOption&&(r=t.baseOption),(s||t.options)&&(r=r||{},a=(t.options||[]).slice()),t.media){r=r||{};var l=t.media;Nx(l,function(t){t&&t.option&&(t.query?o.push(t):n||(n=t))})}return r||(r=t),r.timeline||(r.timeline=s),Nx([r].concat(a).concat(p(o,function(t){return t.option})),function(t){Nx(e,function(e){e(t,i)})}),{baseOption:r,timelineOptions:a,mediaDefault:n,mediaList:o}}function ts(t,e,i){var n={width:e,height:i,aspectratio:e/i},r=!0;return f(t,function(t,e){var i=e.match(Gx);if(i&&i[1]&&i[2]){var a=i[1],o=i[2].toLowerCase();es(n[o],t,a)||(r=!1)}}),r}function es(t,e,i){return"min"===i?t>=e:"max"===i?e>=t:t===e}function is(t,e){return t.join(",")===e.join(",")}function ns(t,e){e=e||{},Nx(e,function(e,i){if(null!=e){var n=t[i];if(yx.hasClass(i)){e=Nn(e),n=Nn(n);var r=Gn(n,e);t[i]=Vx(r,function(t){return t.option&&t.exist?Wx(t.exist,t.option,!0):t.exist||t.option})}else t[i]=Wx(n,e,!0)}})}function rs(t){var e=t&&t.itemStyle;if(e)for(var i=0,n=Xx.length;n>i;i++){var a=Xx[i],o=e.normal,s=e.emphasis;o&&o[a]&&(t[a]=t[a]||{},t[a].normal?r(t[a].normal,o[a]):t[a].normal=o[a],o[a]=null),s&&s[a]&&(t[a]=t[a]||{},t[a].emphasis?r(t[a].emphasis,s[a]):t[a].emphasis=s[a],s[a]=null)}}function as(t,e,i){if(t&&t[e]&&(t[e].normal||t[e].emphasis)){var n=t[e].normal,r=t[e].emphasis;n&&(i?(t[e].normal=t[e].emphasis=null,s(t[e],n)):t[e]=n),r&&(t.emphasis=t.emphasis||{},t.emphasis[e]=r)}}function os(t){as(t,"itemStyle"),as(t,"lineStyle"),as(t,"areaStyle"),as(t,"label"),as(t,"labelLine"),as(t,"upperLabel"),as(t,"edgeLabel")}function ss(t,e){var i=Zx(t)&&t[e],n=Zx(i)&&i.textStyle;if(n)for(var r=0,a=sm.length;a>r;r++){var e=sm[r];n.hasOwnProperty(e)&&(i[e]=n[e])}}function ls(t){t&&(os(t),ss(t,"label"),t.emphasis&&ss(t.emphasis,"label"))}function hs(t){if(Zx(t)){rs(t),os(t),ss(t,"label"),ss(t,"upperLabel"),ss(t,"edgeLabel"),t.emphasis&&(ss(t.emphasis,"label"),ss(t.emphasis,"upperLabel"),ss(t.emphasis,"edgeLabel"));var e=t.markPoint;e&&(rs(e),ls(e));var i=t.markLine;i&&(rs(i),ls(i));var n=t.markArea;n&&ls(n);var r=t.data;if("graph"===t.type){r=r||t.nodes;var a=t.links||t.edges;if(a&&!I(a))for(var o=0;o=0;p--){var g=t[p];if(s||(d=g.data.rawIndexOf(g.stackedByDimension,c)),d>=0){var v=g.data.getByRawIndex(g.stackResultDimension,d);if(u>=0&&v>0||0>=u&&0>v){u+=v,f=v;break}}}return n[0]=u,n[1]=f,n});o.hostModel.setData(l),e.data=l})}function vs(t,e){ko.isInstance(t)||(t=ko.seriesDataToSource(t)),this._source=t;var i=this._data=t.data,n=t.sourceFormat;n===kx&&(this._offset=0,this._dimSize=e,this._data=i);var r=Qx[n===Tx?n+"_"+t.seriesLayoutBy:n];o(this,r)}function ms(){return this._data.length}function ys(t){return this._data[t]}function xs(t){for(var e=0;ee.outputData.count()&&e.model.getRawData().cloneShallow(e.outputData)}function zs(t,e){f(t.CHANGABLE_METHODS,function(i){t.wrapMethod(i,x(Es,e))})}function Es(t){var e=Rs(t);e&&e.setOutputEnd(this.count())}function Rs(t){var e=(t.ecModel||{}).scheduler,i=e&&e.getPipeline(t.uid);if(i){var n=i.currentTask;if(n){var r=n.agentStubMap;r&&(n=r.get(t.uid))}return n}}function Bs(){this.group=new lv,this.uid=Za("viewChart"),this.renderTask=Is({plan:Vs,reset:Ws}),this.renderTask.context={view:this}}function Ns(t,e){if(t&&(t.trigger(e),"group"===t.type))for(var i=0;i=0?n():c=setTimeout(n,-a),h=r};return d.clear=function(){c&&(clearTimeout(c),c=null)},d.debounceNextCall=function(t){l=t},d}function Hs(t,e,i,n){var r=t[e];if(r){var a=r[p_]||r,o=r[v_],s=r[g_];if(s!==i||o!==n){if(null==i||!n)return t[e]=a;r=t[e]=Gs(a,i,"debounce"===n),r[p_]=a,r[v_]=n,r[g_]=i}return r}}function Zs(t,e){var i=t[e];i&&i[p_]&&(t[e]=i[p_])}function Xs(t,e,i,n){this.ecInstance=t,this.api=e,this.unfinished;var i=this._dataProcessorHandlers=i.slice(),n=this._visualHandlers=n.slice();this._allHandlers=i.concat(n),this._stageTaskMap=N()}function Ys(t,e,i,n,r){function a(t,e){return t.setDirty&&(!t.dirtyMap||t.dirtyMap.get(e.__pipeline.id))}r=r||{};var o;f(e,function(e){if(!r.visualType||r.visualType===e.visualType){var s=t._stageTaskMap.get(e.uid),l=s.seriesTaskMap,h=s.overallTask;if(h){var u,c=h.agentStubMap;c.each(function(t){a(r,t)&&(t.dirty(),u=!0)}),u&&h.dirty(),S_(h,n);var d=t.getPerformArgs(h,r.block);c.each(function(t){t.perform(d)}),o|=h.perform(d)}else l&&l.each(function(s){a(r,s)&&s.dirty();var l=t.getPerformArgs(s,r.block);l.skip=!e.performRawSeries&&i.isSeriesFiltered(s.context.model),S_(s,n),o|=s.perform(l)})}}),t.unfinished|=o}function js(t,e,i,n,r){function a(i){var a=i.uid,s=o.get(a)||o.set(a,Is({plan:Js,reset:tl,count:il}));s.context={model:i,ecModel:n,api:r,useClearVisual:e.isVisual&&!e.isLayout,plan:e.plan,reset:e.reset,scheduler:t},nl(t,i,s)}var o=i.seriesTaskMap||(i.seriesTaskMap=N()),s=e.seriesType,l=e.getTargetSeries;e.createOnAllSeries?n.eachRawSeries(a):s?n.eachRawSeriesByType(s,a):l&&l(n,r).each(a);var h=t._pipelineMap;o.each(function(t,e){h.get(e)||(t.dispose(),o.removeKey(e))})}function qs(t,e,i,n,r){function a(e){var i=e.uid,n=s.get(i);n||(n=s.set(i,Is({reset:$s,onDirty:Qs})),o.dirty()),n.context={model:e,overallProgress:u,modifyOutputEnd:c},n.agent=o,n.__block=u,nl(t,e,n)}var o=i.overallTask=i.overallTask||Is({reset:Us});o.context={ecModel:n,api:r,overallReset:e.overallReset,scheduler:t};var s=o.agentStubMap=o.agentStubMap||N(),l=e.seriesType,h=e.getTargetSeries,u=!0,c=e.modifyOutputEnd;l?n.eachRawSeriesByType(l,a):h?h(n,r).each(a):(u=!1,f(n.getSeries(),a));var d=t._pipelineMap;s.each(function(t,e){d.get(e)||(t.dispose(),o.dirty(),s.removeKey(e))})}function Us(t){t.overallReset(t.ecModel,t.api,t.payload)}function $s(t){return t.overallProgress&&Ks}function Ks(){this.agent.dirty(),this.getDownstream().dirty()}function Qs(){this.agent&&this.agent.dirty()}function Js(t){return t.plan&&t.plan(t.model,t.ecModel,t.api,t.payload)}function tl(t){t.useClearVisual&&t.data.clearAllVisual();var e=t.resetDefines=Nn(t.reset(t.model,t.ecModel,t.api,t.payload));return e.length>1?p(e,function(t,e){return el(e)}):M_}function el(t){return function(e,i){var n=i.data,r=i.resetDefines[t];if(r&&r.dataEach)for(var a=e.start;a0?parseInt(n,10)/100:n?parseFloat(n):0;var r=i.getAttribute("stop-color")||"#000000";e.addColorStop(n,r)}i=i.nextSibling}}function hl(t,e){t&&t.__inheritedStyle&&(e.__inheritedStyle||(e.__inheritedStyle={}),s(e.__inheritedStyle,t.__inheritedStyle))}function ul(t){for(var e=z(t).split(E_),i=[],n=0;n0;a-=2){var o=r[a],s=r[a-1];switch(n=n||be(),s){case"translate":o=z(o).split(E_),Te(n,n,[parseFloat(o[0]),parseFloat(o[1]||0)]);break;case"scale":o=z(o).split(E_),Ae(n,n,[parseFloat(o[0]),parseFloat(o[1]||o[0])]);break;case"rotate":o=z(o).split(E_),Ce(n,n,parseFloat(o[0]));break;case"skew":o=z(o).split(E_),console.warn("Skew transform is not supported yet");break;case"matrix":var o=z(o).split(E_);n[0]=parseFloat(o[0]),n[1]=parseFloat(o[1]),n[2]=parseFloat(o[2]),n[3]=parseFloat(o[3]),n[4]=parseFloat(o[4]),n[5]=parseFloat(o[5])}}}e.setLocalTransform(n)}function pl(t){var e=t.getAttribute("style"),i={};if(!e)return i;var n={};W_.lastIndex=0;for(var r;null!=(r=W_.exec(e));)n[r[1]]=r[2];for(var a in N_)N_.hasOwnProperty(a)&&null!=n[a]&&(i[N_[a]]=n[a]);return i}function gl(t,e,i){var n=e/t.width,r=i/t.height,a=Math.min(n,r),o=[a,a],s=[-(t.x+t.width/2)*a+e/2,-(t.y+t.height/2)*a+i/2];return{scale:o,position:s}}function vl(t){return function(e,i,n){e=e&&e.toLowerCase(),bg.prototype[t].call(this,e,i,n)}}function ml(){bg.call(this)}function yl(t,e,i){function r(t,e){return t.__prio-e.__prio}i=i||{},"string"==typeof e&&(e=xw[e]),this.id,this.group,this._dom=t;var a="canvas",o=this._zr=On(t,{renderer:i.renderer||a,devicePixelRatio:i.devicePixelRatio,width:i.width,height:i.height});this._throttledZrFlush=Gs(y(o.flush,o),17);var e=n(e);e&&Ux(e,!0),this._theme=e,this._chartsViews=[],this._chartsMap={},this._componentsViews=[],this._componentsMap={},this._coordSysMgr=new Ko;var s=this._api=Rl(this);Si(yw,r),Si(gw,r),this._scheduler=new Xs(this,s,gw,yw),bg.call(this,this._ecEventProcessor=new Bl),this._messageCenter=new ml,this._initEvents(),this.resize=y(this.resize,this),this._pendingActions=[],o.animation.on("frame",this._onframe,this),Tl(o,this),E(this)}function xl(t,e,i){var n,r=this._model,a=this._coordSysMgr.getCoordinateSystems();e=qn(r,e);for(var o=0;oe.get("hoverLayerThreshold")&&!tg.node&&i.traverse(function(t){t.isGroup||(t.useHoverLayer=!0)})}function zl(t,e){var i=t.get("blendMode")||null;e.group.traverse(function(t){t.isGroup||t.style.blend!==i&&t.setStyle("blend",i),t.eachPendingDisplayable&&t.eachPendingDisplayable(function(t){t.setStyle("blend",i)})})}function El(t,e){var i=t.get("z"),n=t.get("zlevel");e.group.traverse(function(t){"group"!==t.type&&(null!=i&&(t.z=i),null!=n&&(t.zlevel=n))})}function Rl(t){var e=t._coordSysMgr;return o(new $o(t),{getCoordinateSystems:y(e.getCoordinateSystems,e),getComponentByElement:function(e){for(;e;){var i=e.__ecComponentInfo;if(null!=i)return t._model.getComponent(i.mainType,i.index);e=e.parent}}})}function Bl(){this.eventInfo}function Nl(t){function e(t,e){for(var i=0;i65535?Ow:zw}function vh(t){var e=t.constructor;return e===Array?t.slice():new e(t)}function mh(t,e){f(Ew.concat(e.__wrappedMethods||[]),function(i){e.hasOwnProperty(i)&&(t[i]=e[i])}),t.__wrappedMethods=e.__wrappedMethods,f(Rw,function(i){t[i]=n(e[i])}),t._calculationInfo=o(e._calculationInfo)}function yh(t){var e=t._invertedIndicesMap;f(e,function(i,n){var r=t._dimensionInfos[n],a=r.ordinalMeta;if(a){i=e[n]=new Ow(a.categories.length);for(var o=0;o=0?this._indices[t]:-1}function bh(t,e){var i=t._idList[e];return null==i&&(i=xh(t,t._idDimIdx,e)),null==i&&(i=Pw+e),i}function Sh(t){return _(t)||(t=[t]),t}function Mh(t,e){var i=t.dimensions,n=new Bw(p(i,t.getDimensionInfo,t),t.hostModel);mh(n,t);for(var r=n._storage={},a=t._storage,o=0;o=0?(r[s]=Ih(a[s]),n._rawExtent[s]=Th(),n._extent[s]=null):r[s]=a[s])}return n}function Ih(t){for(var e=new Array(t.length),i=0;ip;p++){var g=a[p]=o({},S(a[p])?a[p]:{name:a[p]}),v=g.name,m=c[p]={otherDims:{}};null!=v&&null==h.get(v)&&(m.name=m.displayName=v,h.set(v,p)),null!=g.type&&(m.type=g.type),null!=g.displayName&&(m.displayName=g.displayName)}l.each(function(t,e){if(t=Nn(t).slice(),1===t.length&&t[0]<0)return void l.set(e,!1);var i=l.set(e,[]);f(t,function(t,n){b(t)&&(t=h.get(t)),null!=t&&d>t&&(i[n]=t,r(c[t],e,n))})});var y=0;f(t,function(t){var e,t,i,a;if(b(t))e=t,t={};else{e=t.name;var o=t.ordinalMeta;t.ordinalMeta=null,t=n(t),t.ordinalMeta=o,i=t.dimsDef,a=t.otherDims,t.name=t.coordDim=t.coordDimIndex=t.dimsDef=t.otherDims=null}var h=l.get(e);if(h!==!1){var h=Nn(h);if(!h.length)for(var u=0;u<(i&&i.length||1);u++){for(;yI;I++){var m=c[I]=c[I]||{},T=m.coordDim;null==T&&(m.coordDim=Dh(M,u,w),m.coordDimIndex=0,(!x||0>=_)&&(m.isExtraCoord=!0),_--),null==m.name&&(m.name=Dh(m.coordDim,h)),null==m.type&&Wo(e,I,m.name)&&(m.type="ordinal")}return c}function Ah(t,e,i,n){var r=Math.max(t.dimensionsDetectCount||1,e.length,i.length,n||0);return f(e,function(t){var e=t.dimsDef;e&&(r=Math.max(r,e.length)) +}),r}function Dh(t,e,i){if(i||null!=e.get(t)){for(var n=0;null!=e.get(t+n);)n++;t+=n}return e.set(t,!0),t}function kh(t,e,i){i=i||{};var n,r,a,o,s=i.byIndex,l=i.stackedCoordDimension,h=!(!t||!t.get("stack"));if(f(e,function(t,i){b(t)&&(e[i]=t={name:t}),h&&!t.isExtraCoord&&(s||n||!t.ordinalMeta||(n=t),r||"ordinal"===t.type||"time"===t.type||l&&l!==t.coordDim||(r=t))}),!r||s||n||(s=!0),r){a="__\x00ecstackresult",o="__\x00ecstackedover",n&&(n.createInvertedIndices=!0);var u=r.coordDim,c=r.type,d=0;f(e,function(t){t.coordDim===u&&d++}),e.push({name:a,coordDim:u,coordDimIndex:d,type:c,isExtraCoord:!0,isCalculationCoord:!0}),d++,e.push({name:o,coordDim:o,coordDimIndex:d,type:c,isExtraCoord:!0,isCalculationCoord:!0})}return{stackedDimension:r&&r.name,stackedByDimension:n&&n.name,isStackedByIndex:s,stackedOverDimension:o,stackResultDimension:a}}function Ph(t,e){return!!e&&e===t.getCalculationInfo("stackedDimension")}function Lh(t,e){return Ph(t,e)?t.getCalculationInfo("stackResultDimension"):e}function Oh(t,e,i){i=i||{},ko.isInstance(t)||(t=ko.seriesDataToSource(t));var n,r=e.get("coordinateSystem"),a=Ko.get(r),o=Ao(e);o&&(n=p(o.coordSysDims,function(t){var e={name:t},i=o.axisMap.get(t);if(i){var n=i.get("type");e.type=fh(n)}return e})),n||(n=a&&(a.getDimensionsInfo?a.getDimensionsInfo():a.dimensions.slice())||["x","y"]);var s,l,h=Vw(t,{coordDimensions:n,generateCoord:i.generateCoord});o&&f(h,function(t,e){var i=t.coordDim,n=o.categoryAxisMap.get(i);n&&(null==s&&(s=e),t.ordinalMeta=n.getOrdinalMeta()),null!=t.otherDims.itemName&&(l=!0)}),l||null==s||(h[s].otherDims.itemName=0);var u=kh(e,h),c=new Bw(h,e);c.setCalculationInfo(u);var d=null!=s&&zh(t)?function(t,e,i,n){return n===s?i:this.defaultDimValueGetter(t,e,i,n)}:null;return c.hasItemOption=!1,c.initData(t,null,d),c}function zh(t){if(t.sourceFormat===Ix){var e=Eh(t.data||[]);return null!=e&&!_(Vn(e))}}function Eh(t){for(var e=0;eo&&(o=r.interval=i),null!=n&&o>n&&(o=r.interval=n);var s=r.intervalPrecision=Wh(o),l=r.niceTickExtent=[Zw(Math.ceil(t[0]/o)*o,s),Zw(Math.floor(t[1]/o)*o,s)];return Hh(l,t),r}function Wh(t){return Ja(t)+2}function Gh(t,e,i){t[e]=Math.max(Math.min(t[e],i[1]),i[0])}function Hh(t,e){!isFinite(t[0])&&(t[0]=e[0]),!isFinite(t[1])&&(t[1]=e[1]),Gh(t,0,e),Gh(t,1,e),t[0]>t[1]&&(t[0]=t[1])}function Zh(t,e,i,n){var r=[];if(!t)return r;var a=1e4;e[0]a)return[];return e[1]>(r.length?r[r.length-1]:i[1])&&r.push(e[1]),r}function Xh(t){return t.get("stack")||jw+t.seriesIndex}function Yh(t){return t.dim+t.index}function jh(t){var e=[],i=t.axis,n="axis0";if("category"===i.type){for(var r=i.getBandWidth(),a=0;ae&&(e=Math.min(e,s),t.width&&(e=Math.min(e,t.width)),s-=e,t.width=e,l--)}),h=(s-a)/(l+(l-1)*o),h=Math.max(h,0);var u,c=0;f(n,function(t){t.width||(t.width=h),u=t,c+=t.width*(1+o)}),u&&(c-=u.width*o);var d=-c/2;f(n,function(t,n){i[e][n]=i[e][n]||{offset:d,width:t.width},d+=t.width*(1+o)})}),i}function Kh(t,e,i){if(t&&e){var n=t[Yh(e)];return null!=n&&null!=i&&(n=n[Xh(i)]),n}}function Qh(t,e){var i=qh(t,e),n=Uh(i),r={};f(i,function(t){var e=t.getData(),i=t.coordinateSystem,a=i.getBaseAxis(),o=Xh(t),s=n[Yh(a)][o],l=s.offset,h=s.width,u=i.getOtherAxis(a),c=t.get("barMinHeight")||0;r[o]=r[o]||[],e.setLayout({offset:l,size:h});for(var d=e.mapDimension(u.dim),f=e.mapDimension(a.dim),p=Ph(e,d),g=u.isHorizontal(),v=eu(a,u,p),m=0,y=e.count();y>m;m++){var x=e.get(d,m),_=e.get(f,m);if(!isNaN(x)){var w=x>=0?"p":"n",b=v;p&&(r[o][_]||(r[o][_]={p:v,n:v}),b=r[o][_][w]);var S,M,I,T;if(g){var C=i.dataToPoint([x,_]);S=b,M=C[1]+l,I=C[0]-v,T=h,Math.abs(I)I?-1:1)*c),p&&(r[o][_][w]+=I)}else{var C=i.dataToPoint([_,x]);S=C[0]+l,M=b,I=h,T=C[1]-v,Math.abs(T)=T?-1:1)*c),p&&(r[o][_][w]+=T)}e.setItemLayout(m,{x:S,y:M,width:I,height:T})}}},this)}function Jh(t){return t.coordinateSystem&&"cartesian2d"===t.coordinateSystem.type}function tu(t){return t.pipelineContext&&t.pipelineContext.large}function eu(t,e){var i,n,r=e.getGlobalExtent();r[0]>r[1]?(i=r[1],n=r[0]):(i=r[0],n=r[1]);var a=e.toGlobalCoord(e.dataToCoord(0));return i>a&&(a=i),a>n&&(a=n),a}function iu(t,e){return ub(t,hb(e))}function nu(t,e){var i,n,r,a=t.type,o=e.getMin(),s=e.getMax(),l=null!=o,h=null!=s,u=t.getExtent();"ordinal"===a?i=e.getCategories().length:(n=e.get("boundaryGap"),_(n)||(n=[n||0,n||0]),"boolean"==typeof n[0]&&(n=[0,0]),n[0]=Ua(n[0],1),n[1]=Ua(n[1],1),r=u[1]-u[0]||Math.abs(u[0])),null==o&&(o="ordinal"===a?i?0:0/0:u[0]-n[0]*r),null==s&&(s="ordinal"===a?i?i-1:0/0:u[1]+n[1]*r),"dataMin"===o?o=u[0]:"function"==typeof o&&(o=o({min:u[0],max:u[1]})),"dataMax"===s?s=u[1]:"function"==typeof s&&(s=s({min:u[0],max:u[1]})),(null==o||!isFinite(o))&&(o=0/0),(null==s||!isFinite(s))&&(s=0/0),t.setBlank(C(o)||C(s)||"ordinal"===a&&!t.getOrdinalMeta().categories.length),e.getNeedCrossZero()&&(o>0&&s>0&&!l&&(o=0),0>o&&0>s&&!h&&(s=0));var c=e.ecModel;if(c&&"time"===a){var d,p=qh("bar",c);if(f(p,function(t){d|=t.getBaseAxis()===e.axis}),d){var g=Uh(p),v=ru(o,s,e,g);o=v.min,s=v.max}}return[o,s]}function ru(t,e,i,n){var r=i.axis.getExtent(),a=r[1]-r[0],o=Kh(n,i.axis);if(void 0===o)return{min:t,max:e};var s=1/0;f(o,function(t){s=Math.min(t.offset,s)});var l=-1/0;f(o,function(t){l=Math.max(t.offset+t.width,l)}),s=Math.abs(s),l=Math.abs(l);var h=s+l,u=e-t,c=1-(s+l)/a,d=u/c-u;return e+=d*(l/h),t-=d*(s/h),{min:t,max:e}}function au(t,e){var i=nu(t,e),n=null!=e.getMin(),r=null!=e.getMax(),a=e.get("splitNumber");"log"===t.type&&(t.base=e.get("logBase"));var o=t.type;t.setExtent(i[0],i[1]),t.niceExtent({splitNumber:a,fixMin:n,fixMax:r,minInterval:"interval"===o||"time"===o?e.get("minInterval"):null,maxInterval:"interval"===o||"time"===o?e.get("maxInterval"):null});var s=e.get("interval");null!=s&&t.setInterval&&t.setInterval(s)}function ou(t,e){if(e=e||t.get("type"))switch(e){case"category":return new Hw(t.getOrdinalMeta?t.getOrdinalMeta():t.getCategories(),[1/0,-1/0]);case"value":return new Yw;default:return(Rh.getClass(e)||Yw).create(t)}}function su(t){var e=t.scale.getExtent(),i=e[0],n=e[1];return!(i>0&&n>0||0>i&&0>n)}function lu(t){var e=t.getLabelModel().get("formatter"),i="category"===t.type?t.scale.getExtent()[0]:null;return"string"==typeof e?e=function(e){return function(i){return i=t.scale.getLabel(i),e.replace("{value}",null!=i?i:"")}}(e):"function"==typeof e?function(n,r){return null!=i&&(r=n-i),e(hu(t,n),r)}:function(e){return t.scale.getLabel(e)}}function hu(t,e){return"category"===t.type?t.scale.getLabel(e):e}function uu(t){var e=t.model,i=t.scale;if(e.get("axisLabel.show")&&!i.isBlank()){var n,r,a="category"===t.type,o=i.getExtent();a?r=i.count():(n=i.getTicks(),r=n.length);var s,l=t.getLabelModel(),h=lu(t),u=1;r>40&&(u=Math.ceil(r/40));for(var c=0;r>c;c+=u){var d=n?n[c]:o[0]+c,f=h(d),p=l.getTextRect(f),g=cu(p,l.get("rotate")||0);s?s.union(g):s=g}return s}}function cu(t,e){var i=e*Math.PI/180,n=t.plain(),r=n.width,a=n.height,o=r*Math.cos(i)+a*Math.sin(i),s=r*Math.sin(i)+a*Math.cos(i),l=new gi(n.x,n.y,o,s);return l}function du(t,e){if("image"!==this.type){var i=this.style,n=this.shape;n&&"line"===n.symbolType?i.stroke=t:this.__isEmptyBrush?(i.stroke=t,i.fill=e||"#fff"):(i.fill&&(i.fill=t),i.stroke&&(i.stroke=t)),this.dirty(!1)}}function fu(t,e,i,n,r,a,o){var s=0===t.indexOf("empty");s&&(t=t.substr(5,1).toLowerCase()+t.substr(6));var l;return l=0===t.indexOf("image://")?Jr(t.slice(8),new gi(e,i,n,r),o?"center":"cover"):0===t.indexOf("path://")?Qr(t.slice(7),{},new gi(e,i,n,r),o?"center":"cover"):new Mb({shape:{symbolType:t,x:e,y:i,width:n,height:r}}),l.__isEmptyBrush=s,l.setColor=du,l.setColor(a),l}function pu(t){return Oh(t.getSource(),t)}function gu(t,e){var i=e;Wa.isInstance(e)||(i=new Wa(e),c(i,vb));var n=ou(i);return n.setExtent(t[0],t[1]),au(n,i),n}function vu(t){c(t,vb)}function mu(t,e){return Math.abs(t-e)>1^-(1&s),l=l>>1^-(1&l),s+=r,l+=a,r=s,a=l,n.push([s/i,l/i])}return n}function bu(t){return"category"===t.type?Mu(t):Cu(t)}function Su(t,e){return"category"===t.type?Tu(t,e):{ticks:t.scale.getTicks()}}function Mu(t){var e=t.getLabelModel(),i=Iu(t,e);return!e.get("show")||t.scale.isBlank()?{labels:[],labelCategoryInterval:i.labelCategoryInterval}:i}function Iu(t,e){var i=Au(t,"labels"),n=Ru(e),r=Du(i,n);if(r)return r;var a,o;return w(n)?a=Eu(t,n):(o="auto"===n?Pu(t):n,a=zu(t,o)),ku(i,n,{labels:a,labelCategoryInterval:o})}function Tu(t,e){var i=Au(t,"ticks"),n=Ru(e),r=Du(i,n);if(r)return r;var a,o;if((!e.get("show")||t.scale.isBlank())&&(a=[]),w(n))a=Eu(t,n,!0);else if("auto"===n){var s=Iu(t,t.getLabelModel());o=s.labelCategoryInterval,a=p(s.labels,function(t){return t.tickValue})}else o=n,a=zu(t,o,!0);return ku(i,n,{ticks:a,tickCategoryInterval:o})}function Cu(t){var e=t.scale.getTicks(),i=lu(t);return{labels:p(e,function(e,n){return{formattedLabel:i(e,n),rawLabel:t.scale.getLabel(e),tickValue:e}})}}function Au(t,e){return Db(t)[e]||(Db(t)[e]=[])}function Du(t,e){for(var i=0;i40&&(s=Math.max(1,Math.floor(o/40)));for(var l=a[0],h=t.dataToCoord(l+1)-t.dataToCoord(l),u=Math.abs(h*Math.cos(n)),c=Math.abs(h*Math.sin(n)),d=0,f=0;l<=a[1];l+=s){var p=0,g=0,v=Ei(i(l),e.font,"center","top");p=1.3*v.width,g=1.3*v.height,d=Math.max(d,p,7),f=Math.max(f,g,7)}var m=d/u,y=f/c;isNaN(m)&&(m=1/0),isNaN(y)&&(y=1/0);var x=Math.max(0,Math.floor(Math.min(m,y))),_=Db(t.model),w=_.lastAutoInterval,b=_.lastTickCount;return null!=w&&null!=b&&Math.abs(w-x)<=1&&Math.abs(b-o)<=1&&w>x?x=w:(_.lastTickCount=o,_.lastAutoInterval=x),x}function Ou(t){var e=t.getLabelModel();return{axisRotate:t.getRotate?t.getRotate():t.isHorizontal&&!t.isHorizontal()?90:0,labelRotate:e.get("rotate")||0,font:e.getFont()}}function zu(t,e,i){function n(t){l.push(i?t:{formattedLabel:r(t),rawLabel:a.getLabel(t),tickValue:t})}var r=lu(t),a=t.scale,o=a.getExtent(),s=t.getLabelModel(),l=[],h=Math.max((e||0)+1,1),u=o[0],c=a.count();0!==u&&h>1&&c/h>2&&(u=Math.round(Math.ceil(u/h)*h));var d={min:s.get("showMinLabel"),max:s.get("showMaxLabel")};d.min&&u!==o[0]&&n(o[0]);for(var f=u;f<=o[1];f+=h)n(f);return d.max&&f!==o[1]&&n(o[1]),l}function Eu(t,e,i){var n=t.scale,r=lu(t),a=[];return f(n.getTicks(),function(t){var o=n.getLabel(t);e(t,o)&&a.push(i?t:{formattedLabel:r(t),rawLabel:o,tickValue:t})}),a}function Ru(t){var e=t.get("interval");return null==e?"auto":e}function Bu(t,e){var i=t[1]-t[0],n=e,r=i/n/2;t[0]+=r,t[1]-=r}function Nu(t,e,i,n,r){function a(t,e){return u?t>e:e>t}var o=e.length;if(t.onBand&&!n&&o){var s,l=t.getExtent();if(1===o)e[0].coord=l[0],s=e[1]={coord:l[0]};else{var h=e[1].coord-e[0].coord;f(e,function(t){t.coord-=h/2;var e=e||0;e%2>0&&(t.coord-=h/(2*(e+1)))}),s={coord:e[o-1].coord+h},e.push(s)}var u=l[0]>l[1];a(e[0].coord,l[0])&&(r?e[0].coord=l[0]:e.shift()),r&&a(l[0],e[0].coord)&&e.unshift({coord:l[0]}),a(l[1],s.coord)&&(r?s.coord=l[1]:e.pop()),r&&a(s.coord,l[1])&&e.push({coord:l[1]})}}function Fu(t){return this._axes[t]}function Vu(t){Eb.call(this,t)}function Wu(t,e){return e.type||(e.data?"category":"value")}function Gu(t,e){return t.getCoordSysModel()===e}function Hu(t,e,i){this._coordsMap={},this._coordsList=[],this._axesMap={},this._axesList=[],this._initCartesian(t,e,i),this.model=t}function Zu(t,e,i,n){function r(t){return t.dim+"_"+t.index}i.getAxesOnZeroOf=function(){return a?[a]:[]};var a,o=t[e],s=i.model,l=s.get("axisLine.onZero"),h=s.get("axisLine.onZeroAxisIndex");if(l){if(null!=h)Xu(o[h])&&(a=o[h]);else for(var u in o)if(o.hasOwnProperty(u)&&Xu(o[u])&&!n[r(o[u])]){a=o[u];break}a&&(n[r(a)]=!0)}}function Xu(t){return t&&"category"!==t.type&&"time"!==t.type&&su(t)}function Yu(t,e){var i=t.getExtent(),n=i[0]+i[1];t.toGlobalCoord="x"===t.dim?function(t){return t+e}:function(t){return n-t+e},t.toLocalCoord="x"===t.dim?function(t){return t-e}:function(t){return n-t+e}}function ju(t){return p(Zb,function(e){var i=t.getReferringComponents(e)[0];return i})}function qu(t){return"cartesian2d"===t.get("coordinateSystem")}function Uu(t,e){var i=t.mapDimension("defaultedLabel",!0),n=i.length;if(1===n)return Ss(t,e,i[0]);if(n){for(var r=[],a=0;a0?"bottom":"top":r.width>0?"left":"right";l||$u(t.style,d,n,h,a,i,p),xa(t,d)}function ec(t,e){var i=t.get(qb)||0;return Math.min(i,Math.abs(e.width),Math.abs(e.height))}function ic(t,e,i){var n=t.getData(),r=[],a=n.getLayout("valueAxisHorizontal")?1:0;r[1-a]=n.getLayout("valueAxisStart");var o=new Kb({shape:{points:n.getLayout("largePoints")},incremental:!!i,__startPoint:r,__valueIdx:a});e.add(o),nc(o,t,n)}function nc(t,e,i){var n=i.getVisual("borderColor")||i.getVisual("color"),r=e.getModel("itemStyle").getItemStyle(["color","borderColor"]);t.useStyle(r),t.style.fill=null,t.style.stroke=n,t.style.lineWidth=i.getLayout("barWidth")}function rc(t){var e={componentType:t.mainType,componentIndex:t.componentIndex};return e[t.mainType+"Index"]=t.componentIndex,e}function ac(t,e,i,n){var r,a,o=io(i-t.rotation),s=n[0]>n[1],l="start"===e&&!s||"start"!==e&&s;return no(o-Qb/2)?(a=l?"bottom":"top",r="center"):no(o-1.5*Qb)?(a=l?"top":"bottom",r="center"):(a="middle",r=1.5*Qb>o&&o>Qb/2?l?"left":"right":l?"right":"left"),{rotation:o,textAlign:r,textVerticalAlign:a}}function oc(t){var e=t.get("tooltip");return t.get("silent")||!(t.get("triggerEvent")||e&&e.show)}function sc(t,e,i){var n=t.get("axisLabel.showMinLabel"),r=t.get("axisLabel.showMaxLabel");e=e||[],i=i||[];var a=e[0],o=e[1],s=e[e.length-1],l=e[e.length-2],h=i[0],u=i[1],c=i[i.length-1],d=i[i.length-2];n===!1?(lc(a),lc(h)):hc(a,o)&&(n?(lc(o),lc(u)):(lc(a),lc(h))),r===!1?(lc(s),lc(c)):hc(l,s)&&(r?(lc(l),lc(d)):(lc(s),lc(c)))}function lc(t){t&&(t.ignore=!0)}function hc(t,e){var i=t&&t.getBoundingRect().clone(),n=e&&e.getBoundingRect().clone();if(i&&n){var r=Se([]);return Ce(r,r,-t.rotation),i.applyTransform(Ie([],r,t.getLocalTransform())),n.applyTransform(Ie([],r,e.getLocalTransform())),i.intersect(n)}}function uc(t){return"middle"===t||"center"===t}function cc(t,e,i){var n=e.axis;if(e.get("axisTick.show")&&!n.scale.isBlank()){for(var r=e.getModel("axisTick"),a=r.getModel("lineStyle"),o=r.get("length"),l=n.getTicksCoords(),h=[],u=[],c=t._transform,d=[],f=0;f=0||t===e}function xc(t){var e=_c(t);if(e){var i=e.axisPointerModel,n=e.axis.scale,r=i.option,a=i.get("status"),o=i.get("value");null!=o&&(o=n.parse(o));var s=bc(i);null==a&&(r.status=s?"show":"hide");var l=n.getExtent().slice();l[0]>l[1]&&l.reverse(),(null==o||o>l[1])&&(o=l[1]),o0?i=n[0]:n[1]<0&&(i=n[1]),i}function Vc(t,e,i,n){var r=0/0;t.stacked&&(r=i.get(i.getCalculationInfo("stackedOverDimension"),n)),isNaN(r)&&(r=t.valueStart);var a=t.baseDataOffset,o=[];return o[a]=i.get(t.baseDim,n),o[1-a]=r,e.dataToPoint(o)}function Wc(t,e){var i=[];return e.diff(t).add(function(t){i.push({cmd:"+",idx:t})}).update(function(t,e){i.push({cmd:"=",idx:e,idx1:t})}).remove(function(t){i.push({cmd:"-",idx:t})}).execute(),i}function Gc(t){return isNaN(t[0])||isNaN(t[1])}function Hc(t,e,i,n,r,a,o,s,l,h){return"none"!==h&&h?Zc.apply(this,arguments):Xc.apply(this,arguments)}function Zc(t,e,i,n,r,a,o,s,l,h,u){for(var c=0,d=i,f=0;n>f;f++){var p=e[d];if(d>=r||0>d)break;if(Gc(p)){if(u){d+=a;continue}break}if(d===i)t[a>0?"moveTo":"lineTo"](p[0],p[1]);else if(l>0){var g=e[c],v="y"===h?1:0,m=(p[v]-g[v])*l;_S(bS,g),bS[v]=g[v]+m,_S(SS,p),SS[v]=p[v]-m,t.bezierCurveTo(bS[0],bS[1],SS[0],SS[1],p[0],p[1])}else t.lineTo(p[0],p[1]);c=d,d+=a}return f}function Xc(t,e,i,n,r,a,o,s,l,h,u){for(var c=0,d=i,f=0;n>f;f++){var p=e[d];if(d>=r||0>d)break;if(Gc(p)){if(u){d+=a;continue}break}if(d===i)t[a>0?"moveTo":"lineTo"](p[0],p[1]),_S(bS,p);else if(l>0){var g=d+a,v=e[g];if(u)for(;v&&Gc(e[g]);)g+=a,v=e[g];var m=.5,y=e[c],v=e[g];if(!v||Gc(v))_S(SS,p);else{Gc(v)&&!u&&(v=p),j(wS,v,y);var x,_;if("x"===h||"y"===h){var w="x"===h?0:1;x=Math.abs(p[w]-y[w]),_=Math.abs(p[w]-v[w])}else x=yg(p,y),_=yg(p,v);m=_/(_+x),xS(SS,p,wS,-l*(1-m))}mS(bS,bS,s),yS(bS,bS,o),mS(SS,SS,s),yS(SS,SS,o),t.bezierCurveTo(bS[0],bS[1],SS[0],SS[1],p[0],p[1]),xS(bS,p,wS,l*m)}else t.lineTo(p[0],p[1]);c=d,d+=a}return f}function Yc(t,e){var i=[1/0,1/0],n=[-1/0,-1/0];if(e)for(var r=0;rn[0]&&(n[0]=a[0]),a[1]>n[1]&&(n[1]=a[1])}return{min:e?i:n,max:e?n:i}}function jc(t,e){if(t.length===e.length){for(var i=0;ie[0]?1:-1;e[0]+=n*i,e[1]-=n*i}return e}function $c(t,e,i){if(!i.valueDim)return[];for(var n=[],r=0,a=e.count();a>r;r++)n.push(Vc(i,t,e,r));return n}function Kc(t,e,i,n){var r=Uc(t.getAxis("x")),a=Uc(t.getAxis("y")),o=t.getBaseAxis().isHorizontal(),s=Math.min(r[0],r[1]),l=Math.min(a[0],a[1]),h=Math.max(r[0],r[1])-s,u=Math.max(a[0],a[1])-l;if(i)s-=.5,h+=.5,l-=.5,u+=.5;else{var c=n.get("lineStyle.width")||2,d=n.get("clipOverflow")?c/2:Math.max(h,u);o?(l-=d,u+=2*d):(s-=d,h+=2*d)}var f=new Dy({shape:{x:s,y:l,width:h,height:u}});return e&&(f.shape[o?"width":"height"]=0,Oa(f,{shape:{width:h,height:u}},n)),f}function Qc(t,e,i,n){var r=t.getAngleAxis(),a=t.getRadiusAxis(),o=a.getExtent().slice();o[0]>o[1]&&o.reverse();var s=r.getExtent(),l=Math.PI/180;i&&(o[0]-=.5,o[1]+=.5);var h=new Sy({shape:{cx:$a(t.cx,1),cy:$a(t.cy,1),r0:$a(o[0],1),r:$a(o[1],1),startAngle:-s[0]*l,endAngle:-s[1]*l,clockwise:r.inverse}});return e&&(h.shape.endAngle=-s[0]*l,Oa(h,{shape:{endAngle:-s[1]*l}},n)),h}function Jc(t,e,i,n){return"polar"===t.type?Qc(t,e,i,n):Kc(t,e,i,n)}function td(t,e,i){for(var n=e.getBaseAxis(),r="x"===n.dim||"radius"===n.dim?0:1,a=[],o=0;o=0;a--){var o=i[a].dimension,s=t.dimensions[o],l=t.getDimensionInfo(s);if(n=l&&l.coordDim,"x"===n||"y"===n){r=i[a];break}}if(r){var h=e.getAxis(n),u=p(r.stops,function(t){return{coord:h.toGlobalCoord(h.dataToCoord(t.value)),color:t.color}}),c=u.length,d=r.outerColors.slice();c&&u[0].coord>u[c-1].coord&&(u.reverse(),d.reverse());var g=10,v=u[0].coord-g,m=u[c-1].coord+g,y=m-v;if(.001>y)return"transparent";f(u,function(t){t.offset=(t.coord-v)/y}),u.push({offset:c?u[c-1].offset:.5,color:d[1]||"transparent"}),u.unshift({offset:c?u[0].offset:.5,color:d[0]||"transparent"});var x=new Ry(0,0,0,0,u,!0);return x[n]=v,x[n+"2"]=m,x}}}function id(t,e,i){var n=t.get("showAllSymbol"),r="auto"===n;if(!n||r){var a=i.getAxesByScale("ordinal")[0];if(a&&(!r||!nd(a,e))){var o=e.mapDimension(a.dim),s={};return f(a.getViewLabels(),function(t){s[t.tickValue]=1}),function(t){return!s.hasOwnProperty(e.get(o,t))}}}}function nd(t,e){var i=t.getExtent(),n=Math.abs(i[1]-i[0])/t.scale.count();isNaN(n)&&(n=0);for(var r=e.count(),a=Math.max(1,Math.round(r/5)),o=0;r>o;o+=a)if(1.5*Cc.getSymbolSize(e,o)[t.isHorizontal()?1:0]>n)return!1;return!0}function rd(t,e,i,n){var r=e.getData(),a=this.dataIndex,o=r.getName(a),s=e.get("selectedOffset");n.dispatchAction({type:"pieToggleSelect",from:t,name:o,seriesId:e.id}),r.each(function(t){ad(r.getItemGraphicEl(t),r.getItemLayout(t),e.isSelected(r.getName(t)),s,i)})}function ad(t,e,i,n,r){var a=(e.startAngle+e.endAngle)/2,o=Math.cos(a),s=Math.sin(a),l=i?n:0,h=[o*l,s*l];r?t.animate().when(200,{position:h}).start("bounceOut"):t.attr("position",h)}function od(t,e){function i(){a.ignore=a.hoverIgnore,o.ignore=o.hoverIgnore}function n(){a.ignore=a.normalIgnore,o.ignore=o.normalIgnore}lv.call(this);var r=new Sy({z2:2}),a=new Ay,o=new xy;this.add(r),this.add(a),this.add(o),this.updateData(t,e,!0),this.on("emphasis",i).on("normal",n).on("mouseover",i).on("mouseout",n)}function sd(t,e,i,n,r,a,o){function s(e,i,n){for(var r=e;i>r;r++)if(t[r].y+=n,r>e&&i>r+1&&t[r+1].y>t[r].y+t[r].height)return void l(r,n/2);l(i-1,n/2)}function l(e,i){for(var n=e;n>=0&&(t[n].y-=i,!(n>0&&t[n].y>t[n-1].y+t[n-1].height));n--);}function h(t,e,i,n,r,a){for(var o=a>0?e?Number.MAX_VALUE:0:e?Number.MAX_VALUE:0,s=0,l=t.length;l>s;s++)if("center"!==t[s].position){var h=Math.abs(t[s].y-n),u=t[s].len,c=t[s].len2,d=r+u>h?Math.sqrt((r+u+c)*(r+u+c)-h*h):Math.abs(t[s].x-i);e&&d>=o&&(d=o-10),!e&&o>=d&&(d=o+10),t[s].x=i+d*a,o=d}}t.sort(function(t,e){return t.y-e.y});for(var u,c=0,d=t.length,f=[],p=[],g=0;d>g;g++)u=t[g].y-c,0>u&&s(g,d,-u,r),c=t[g].y+t[g].height;0>o-c&&l(d-1,c-o);for(var g=0;d>g;g++)t[g].y>=i?p.push(t[g]):f.push(t[g]);h(f,!1,e,i,n,r),h(p,!0,e,i,n,r)}function ld(t,e,i,n,r,a){for(var o=[],s=[],l=0;lu;u++)a[u]&&xd(t.childAt(u),e,a[u],n,t,r)}}function wd(t){new uh(t.oldChildren,t.newChildren,bd,bd,t).add(Sd).update(Sd).remove(Md).execute()}function bd(t,e){var i=t&&t.name;return null!=i?i:KS+e}function Sd(t,e){var i=this.context,n=null!=t?i.newChildren[t]:null,r=null!=e?i.oldChildren[e]:null;xd(r,i.dataIndex,n,i.animatableModel,i.group,i.data)}function Md(t){var e=this.context,i=e.oldChildren[t];i&&e.group.remove(i)}function Id(t){return t&&(t.pathData||t.d)}function Td(t){return t&&(t.hasOwnProperty("pathData")||t.hasOwnProperty("d"))}function Cd(t,e){return t&&t.hasOwnProperty(e)}function Ad(t,e,i){var n,r={},a="toggleSelected"===t;return i.eachComponent("legend",function(i){a&&null!=n?i[n?"select":"unSelect"](e.name):(i[t](e.name),n=i.isSelected(e.name));var o=i.getData();f(o,function(t){var e=t.get("name");if("\n"!==e&&""!==e){var n=i.isSelected(e);r[e]=r.hasOwnProperty(e)?r[e]&&n:n}})}),{name:e.name,selected:r}}function Dd(t,e){var i=rx(e.get("padding")),n=e.getItemStyle(["color","opacity"]);n.fill=e.get("backgroundColor");var t=new Dy({shape:{x:t.x-i[3],y:t.y-i[0],width:t.width+i[1]+i[3],height:t.height+i[0]+i[2],r:e.get("borderRadius")},style:n,silent:!0,z2:-1});return t}function kd(t,e){e.dispatchAction({type:"legendToggleSelect",name:t})}function Pd(t,e,i,n){var r=i.getZr().storage.getDisplayList()[0];r&&r.useHoverLayer||i.dispatchAction({type:"highlight",seriesName:t,name:e,excludeSeriesId:n})}function Ld(t,e,i,n){var r=i.getZr().storage.getDisplayList()[0];r&&r.useHoverLayer||i.dispatchAction({type:"downplay",seriesName:t,name:e,excludeSeriesId:n})}function Od(t,e,i){var n=t.getOrient(),r=[1,1];r[n.index]=0,So(e,i,{type:"box",ignoreSize:r})}function zd(t,e,i,n,r){var a=t.axis;if(!a.scale.isBlank()&&a.containData(e)){if(!t.involveSeries)return void i.showPointer(t,e);var s=Ed(e,t),l=s.payloadBatch,h=s.snapToValue;l[0]&&null==r.seriesIndex&&o(r,l[0]),!n&&t.snap&&a.containData(h)&&null!=h&&(e=h),i.showPointer(t,e,l,r),i.showTooltip(t,s,h)}}function Ed(t,e){var i=e.axis,n=i.dim,r=t,a=[],o=Number.MAX_VALUE,s=-1;return cM(e.seriesModels,function(e){var l,h,u=e.getData().mapDimension(n,!0);if(e.getAxisTooltipData){var c=e.getAxisTooltipData(u,t,i);h=c.dataIndices,l=c.nestestValue}else{if(h=e.getData().indicesOfNearest(u[0],t,"category"===i.type?.5:null),!h.length)return;l=e.getData().get(u[0],h[0])}if(null!=l&&isFinite(l)){var d=t-l,f=Math.abs(d);o>=f&&((o>f||d>=0&&0>s)&&(o=f,s=d,r=l,a.length=0),cM(h,function(t){a.push({seriesIndex:e.seriesIndex,dataIndexInside:t,dataIndex:e.getData().getRawIndex(t)})}))}}),{payloadBatch:a,snapToValue:r}}function Rd(t,e,i,n){t[e.key]={value:i,payloadBatch:n}}function Bd(t,e,i,n){var r=i.payloadBatch,a=e.axis,o=a.model,s=e.axisPointerModel;if(e.triggerTooltip&&r.length){var l=e.coordSys.model,h=Sc(l),u=t.map[h];u||(u=t.map[h]={coordSysId:l.id,coordSysIndex:l.componentIndex,coordSysType:l.type,coordSysMainType:l.mainType,dataByAxis:[]},t.list.push(u)),u.dataByAxis.push({axisDim:a.dim,axisIndex:o.componentIndex,axisType:o.type,axisId:o.id,value:n,valueLabelOpt:{precision:s.get("label.precision"),formatter:s.get("label.formatter")},seriesDataIndices:r.slice()})}}function Nd(t,e,i){var n=i.axesInfo=[];cM(e,function(e,i){var r=e.axisPointerModel.option,a=t[i];a?(!e.useHandle&&(r.status="show"),r.value=a.value,r.seriesDataIndices=(a.payloadBatch||[]).slice()):!e.useHandle&&(r.status="hide"),"show"===r.status&&n.push({axisDim:e.axis.dim,axisIndex:e.axis.model.componentIndex,value:r.value})})}function Fd(t,e,i,n){if(Hd(e)||!t.list.length)return void n({type:"hideTip"});var r=((t.list[0].dataByAxis[0]||{}).seriesDataIndices||[])[0]||{};n({type:"showTip",escapeConnect:!0,x:e[0],y:e[1],tooltipOption:i.tooltipOption,position:i.position,dataIndexInside:r.dataIndexInside,dataIndex:r.dataIndex,seriesIndex:r.seriesIndex,dataByCoordSys:t.list})}function Vd(t,e,i){var n=i.getZr(),r="axisPointerLastHighlights",a=fM(n)[r]||{},o=fM(n)[r]={};cM(t,function(t){var e=t.axisPointerModel.option;"show"===e.status&&cM(e.seriesDataIndices,function(t){var e=t.seriesIndex+" | "+t.dataIndex;o[e]=t})});var s=[],l=[];f(a,function(t,e){!o[e]&&l.push(t)}),f(o,function(t,e){!a[e]&&s.push(t)}),l.length&&i.dispatchAction({type:"downplay",escapeConnect:!0,batch:l}),s.length&&i.dispatchAction({type:"highlight",escapeConnect:!0,batch:s})}function Wd(t,e){for(var i=0;i<(t||[]).length;i++){var n=t[i];if(e.axis.dim===n.axisDim&&e.axis.model.componentIndex===n.axisIndex)return n}}function Gd(t){var e=t.axis.model,i={},n=i.axisDim=t.axis.dim;return i.axisIndex=i[n+"AxisIndex"]=e.componentIndex,i.axisName=i[n+"AxisName"]=e.name,i.axisId=i[n+"AxisId"]=e.id,i}function Hd(t){return!t||null==t[0]||isNaN(t[0])||null==t[1]||isNaN(t[1])}function Zd(t,e,i){if(!tg.node){var n=e.getZr();gM(n).records||(gM(n).records={}),Xd(n,e);var r=gM(n).records[t]||(gM(n).records[t]={});r.handler=i}}function Xd(t,e){function i(i,n){t.on(i,function(i){var r=Ud(e);vM(gM(t).records,function(t){t&&n(t,i,r.dispatchAction)}),Yd(r.pendings,e)})}gM(t).initialized||(gM(t).initialized=!0,i("click",x(qd,"click")),i("mousemove",x(qd,"mousemove")),i("globalout",jd))}function Yd(t,e){var i,n=t.showTip.length,r=t.hideTip.length;n?i=t.showTip[n-1]:r&&(i=t.hideTip[r-1]),i&&(i.dispatchAction=null,e.dispatchAction(i))}function jd(t,e,i){t.handler("leave",null,i)}function qd(t,e,i,n){e.handler(t,i,n)}function Ud(t){var e={showTip:[],hideTip:[]},i=function(n){var r=e[n.type];r?r.push(n):(n.dispatchAction=i,t.dispatchAction(n))};return{dispatchAction:i,pendings:e}}function $d(t,e){if(!tg.node){var i=e.getZr(),n=(gM(i).records||{})[t];n&&(gM(i).records[t]=null)}}function Kd(){}function Qd(t,e,i,n){Jd(yM(i).lastProp,n)||(yM(i).lastProp=n,e?La(i,n,t):(i.stopAnimation(),i.attr(n)))}function Jd(t,e){if(S(t)&&S(e)){var i=!0;return f(e,function(e,n){i=i&&Jd(t[n],e)}),!!i}return t===e}function tf(t,e){t[e.get("label.show")?"show":"hide"]()}function ef(t){return{position:t.position.slice(),rotation:t.rotation||0}}function nf(t,e,i){var n=e.get("z"),r=e.get("zlevel");t&&t.traverse(function(t){"group"!==t.type&&(null!=n&&(t.z=n),null!=r&&(t.zlevel=r),t.silent=i)})}function rf(t){var e,i=t.get("type"),n=t.getModel(i+"Style");return"line"===i?(e=n.getLineStyle(),e.fill=null):"shadow"===i&&(e=n.getAreaStyle(),e.stroke=null),e}function af(t,e,i,n,r){var a=i.get("value"),o=sf(a,e.axis,e.ecModel,i.get("seriesDataIndices"),{precision:i.get("label.precision"),formatter:i.get("label.formatter")}),s=i.getModel("label"),l=rx(s.get("padding")||0),h=s.getFont(),u=Ei(o,h),c=r.position,d=u.width+l[1]+l[3],f=u.height+l[0]+l[2],p=r.align;"right"===p&&(c[0]-=d),"center"===p&&(c[0]-=d/2);var g=r.verticalAlign;"bottom"===g&&(c[1]-=f),"middle"===g&&(c[1]-=f/2),of(c,d,f,n);var v=s.get("backgroundColor");v&&"auto"!==v||(v=e.get("axisLine.lineStyle.color")),t.label={shape:{x:0,y:0,width:d,height:f,r:s.get("borderRadius")},position:c.slice(),style:{text:o,textFont:h,textFill:s.getTextColor(),textPosition:"inside",fill:v,stroke:s.get("borderColor")||"transparent",lineWidth:s.get("borderWidth")||0,shadowBlur:s.get("shadowBlur"),shadowColor:s.get("shadowColor"),shadowOffsetX:s.get("shadowOffsetX"),shadowOffsetY:s.get("shadowOffsetY")},z2:10}}function of(t,e,i,n){var r=n.getWidth(),a=n.getHeight();t[0]=Math.min(t[0]+e,r)-e,t[1]=Math.min(t[1]+i,a)-i,t[0]=Math.max(t[0],0),t[1]=Math.max(t[1],0)}function sf(t,e,i,n,r){t=e.scale.parse(t);var a=e.scale.getLabel(t,{precision:r.precision}),o=r.formatter;if(o){var s={value:hu(e,t),seriesData:[]};f(n,function(t){var e=i.getSeriesByIndex(t.seriesIndex),n=t.dataIndexInside,r=e&&e.getDataParams(n);r&&s.seriesData.push(r)}),b(o)?a=o.replace("{value}",a):w(o)&&(a=o(s))}return a}function lf(t,e,i){var n=be();return Ce(n,n,i.rotation),Te(n,n,i.position),Ea([t.dataToCoord(e),(i.labelOffset||0)+(i.labelDirection||1)*(i.labelMargin||0)],n)}function hf(t,e,i,n,r,a){var o=Jb.innerTextLayout(i.rotation,0,i.labelDirection);i.labelMargin=r.get("label.margin"),af(e,n,r,a,{position:lf(n.axis,t,i),align:o.textAlign,verticalAlign:o.textVerticalAlign})}function uf(t,e,i){return i=i||0,{x1:t[i],y1:t[1-i],x2:e[i],y2:e[1-i]}}function cf(t,e,i){return i=i||0,{x:t[i],y:t[1-i],width:e[i],height:e[1-i]}}function df(t,e){var i={};return i[e.dim+"AxisIndex"]=e.index,t.getCartesian(i)}function ff(t){return"x"===t.dim?0:1}function pf(t){var e="cubic-bezier(0.23, 1, 0.32, 1)",i="left "+t+"s "+e+",top "+t+"s "+e;return p(IM,function(t){return t+"transition:"+i}).join(";")}function gf(t){var e=[],i=t.get("fontSize"),n=t.getTextColor();return n&&e.push("color:"+n),e.push("font:"+t.getFont()),i&&e.push("line-height:"+Math.round(3*i/2)+"px"),SM(["decoration","align"],function(i){var n=t.get(i);n&&e.push("text-"+i+":"+n)}),e.join(";")}function vf(t){var e=[],i=t.get("transitionDuration"),n=t.get("backgroundColor"),r=t.getModel("textStyle"),a=t.get("padding");return i&&e.push(pf(i)),n&&(tg.canvasSupported?e.push("background-Color:"+n):(e.push("background-Color:#"+je(n)),e.push("filter:alpha(opacity=70)"))),SM(["width","color","radius"],function(i){var n="border-"+i,r=MM(n),a=t.get(r);null!=a&&e.push(n+":"+a+("color"===i?"":"px"))}),e.push(gf(r)),null!=a&&e.push("padding:"+rx(a).join("px ")+"px"),e.join(";")+";"}function mf(t,e){if(tg.wxa)return null;var i=document.createElement("div"),n=this._zr=e.getZr();this.el=i,this._x=e.getWidth()/2,this._y=e.getHeight()/2,t.appendChild(i),this._container=t,this._show=!1,this._hideTimeout;var r=this;i.onmouseenter=function(){r._enterable&&(clearTimeout(r._hideTimeout),r._show=!0),r._inContent=!0},i.onmousemove=function(e){if(e=e||window.event,!r._enterable){var i=n.handler;pe(t,e,!0),i.dispatch("mousemove",e)}},i.onmouseleave=function(){r._enterable&&r._show&&r.hideLater(r._hideDelay),r._inContent=!1}}function yf(t){this._zr=t.getZr(),this._show=!1,this._hideTimeout}function xf(t){for(var e=t.pop();t.length;){var i=t.pop();i&&(Wa.isInstance(i)&&(i=i.get("tooltip",!0)),"string"==typeof i&&(i={formatter:i}),e=new Wa(i,e,e.ecModel))}return e}function _f(t,e){return t.dispatchAction||y(e.dispatchAction,e)}function wf(t,e,i,n,r,a,o){var s=i.getOuterSize(),l=s.width,h=s.height;return null!=a&&(t+l+a>n?t-=l+a:t+=a),null!=o&&(e+h+o>r?e-=h+o:e+=o),[t,e]}function bf(t,e,i,n,r){var a=i.getOuterSize(),o=a.width,s=a.height;return t=Math.min(t+o,n)-o,e=Math.min(e+s,r)-s,t=Math.max(t,0),e=Math.max(e,0),[t,e]}function Sf(t,e,i){var n=i[0],r=i[1],a=5,o=0,s=0,l=e.width,h=e.height;switch(t){case"inside":o=e.x+l/2-n/2,s=e.y+h/2-r/2;break;case"top":o=e.x+l/2-n/2,s=e.y-r-a;break;case"bottom":o=e.x+l/2-n/2,s=e.y+h+a;break;case"left":o=e.x-n-a,s=e.y+h/2-r/2;break;case"right":o=e.x+l+a,s=e.y+h/2-r/2}return[o,s]}function Mf(t){return"center"===t||"middle"===t}function If(t){Fn(t,"label",["show"])}function Tf(t){return!(isNaN(parseFloat(t.x))&&isNaN(parseFloat(t.y)))}function Cf(t){return!isNaN(parseFloat(t.x))&&!isNaN(parseFloat(t.y))}function Af(t,e,i,n,r,a){var o=[],s=Ph(e,n),l=s?e.getCalculationInfo("stackResultDimension"):n,h=zf(e,l,t),u=e.indicesOfNearest(l,h)[0];o[r]=e.get(i,u),o[a]=e.get(n,u);var c=Qa(e.get(n,u));return c=Math.min(c,20),c>=0&&(o[a]=+o[a].toFixed(c)),o}function Df(t,e){var i=t.getData(),r=t.coordinateSystem;if(e&&!Cf(e)&&!_(e.coord)&&r){var a=r.dimensions,o=kf(e,i,r,t);if(e=n(e),e.type&&RM[e.type]&&o.baseAxis&&o.valueAxis){var s=zM(a,o.baseAxis.dim),l=zM(a,o.valueAxis.dim);e.coord=RM[e.type](i,o.baseDataDim,o.valueDataDim,s,l),e.value=e.coord[l]}else{for(var h=[null!=e.xAxis?e.xAxis:e.radiusAxis,null!=e.yAxis?e.yAxis:e.angleAxis],u=0;2>u;u++)RM[h[u]]&&(h[u]=zf(i,i.mapDimension(a[u]),h[u]));e.coord=h}}return e}function kf(t,e,i,n){var r={};return null!=t.valueIndex||null!=t.valueDim?(r.valueDataDim=null!=t.valueIndex?e.getDimension(t.valueIndex):t.valueDim,r.valueAxis=i.getAxis(Pf(n,r.valueDataDim)),r.baseAxis=i.getOtherAxis(r.valueAxis),r.baseDataDim=e.mapDimension(r.baseAxis.dim)):(r.baseAxis=n.getBaseAxis(),r.valueAxis=i.getOtherAxis(r.baseAxis),r.baseDataDim=e.mapDimension(r.baseAxis.dim),r.valueDataDim=e.mapDimension(r.valueAxis.dim)),r}function Pf(t,e){var i=t.getData(),n=i.dimensions;e=i.getDimension(e);for(var r=0;rn?t.coord&&t.coord[n]:t.value}function zf(t,e,i){if("average"===i){var n=0,r=0;return t.each(e,function(t){isNaN(t)||(n+=t,r++)}),n/r}return"median"===i?t.getMedian(e):t.getDataExtent(e,!0)["max"===i?1:0]}function Ef(t,e,i){var n=e.coordinateSystem;t.each(function(r){var a,o=t.getItemModel(r),s=Ua(o.get("x"),i.getWidth()),l=Ua(o.get("y"),i.getHeight());if(isNaN(s)||isNaN(l)){if(e.getMarkerPosition)a=e.getMarkerPosition(t.getValues(t.dimensions,r));else if(n){var h=t.get(n.dimensions[0],r),u=t.get(n.dimensions[1],r);a=n.dataToPoint([h,u])}}else a=[s,l];isNaN(s)||(a[0]=s),isNaN(l)||(a[1]=l),t.setItemLayout(r,a)})}function Rf(t,e,i){var n;n=t?p(t&&t.dimensions,function(t){var i=e.getData().getDimensionInfo(e.getData().mapDimension(t))||{};return s({name:t},i)}):[{name:"value",type:"float"}];var r=new Bw(n,i),a=p(i.get("data"),x(Df,e));return t&&(a=v(a,x(Lf,t))),r.initData(a,null,t?Of:function(t){return t.value}),r}function Bf(t){return isNaN(+t.cpx1)||isNaN(+t.cpy1)}function Nf(t){return"_"+t+"Type"}function Ff(t,e,i){var n=e.getItemVisual(i,"color"),r=e.getItemVisual(i,t),a=e.getItemVisual(i,t+"Size");if(r&&"none"!==r){_(a)||(a=[a,a]);var o=fu(r,-a[0]/2,-a[1]/2,a[0],a[1],n);return o.name=t,o}}function Vf(t){var e=new VM({name:"line"});return Wf(e.shape,t),e}function Wf(t,e){var i=e[0],n=e[1],r=e[2];t.x1=i[0],t.y1=i[1],t.x2=n[0],t.y2=n[1],t.percent=1,r?(t.cpx1=r[0],t.cpy1=r[1]):(t.cpx1=0/0,t.cpy1=0/0)}function Gf(){var t=this,e=t.childOfName("fromSymbol"),i=t.childOfName("toSymbol"),n=t.childOfName("label");if(e||i||!n.ignore){for(var r=1,a=this.parent;a;)a.scale&&(r/=a.scale[0]),a=a.parent;var o=t.childOfName("line");if(this.__dirty||o.__dirty){var s=o.shape.percent,l=o.pointAt(0),h=o.pointAt(s),u=j([],h,l);if(te(u,u),e){e.attr("position",l);var c=o.tangentAt(0);e.attr("rotation",Math.PI/2-Math.atan2(c[1],c[0])),e.attr("scale",[r*s,r*s])}if(i){i.attr("position",h);var c=o.tangentAt(1);i.attr("rotation",-Math.PI/2-Math.atan2(c[1],c[0])),i.attr("scale",[r*s,r*s])}if(!n.ignore){n.attr("position",h);var d,f,p,g=5*r;if("end"===n.__position)d=[u[0]*g+h[0],u[1]*g+h[1]],f=u[0]>.8?"left":u[0]<-.8?"right":"center",p=u[1]>.8?"top":u[1]<-.8?"bottom":"middle";else if("middle"===n.__position){var v=s/2,c=o.tangentAt(v),m=[c[1],-c[0]],y=o.pointAt(v);m[1]>0&&(m[0]=-m[0],m[1]=-m[1]),d=[y[0]+m[0]*g,y[1]+m[1]*g],f="center",p="bottom";var x=-Math.atan2(c[1],c[0]);h[0].8?"right":u[0]<-.8?"left":"center",p=u[1]>.8?"bottom":u[1]<-.8?"top":"middle";n.attr({style:{textVerticalAlign:n.__verticalAlign||p,textAlign:n.__textAlign||f},position:d,scale:[r,r]})}}}}function Hf(t,e,i){lv.call(this),this._createLine(t,e,i)}function Zf(t){this._ctor=t||Hf,this.group=new lv}function Xf(t,e,i,n){var r=e.getItemLayout(i);if(Uf(r)){var a=new t._ctor(e,i,n);e.setItemGraphicEl(i,a),t.group.add(a)}}function Yf(t,e,i,n,r,a){var o=e.getItemGraphicEl(n);return Uf(i.getItemLayout(r))?(o?o.updateData(i,r,a):o=new t._ctor(i,r,a),i.setItemGraphicEl(r,o),void t.group.add(o)):void t.group.remove(o)}function jf(t){var e=t.hostModel;return{lineStyle:e.getModel("lineStyle").getLineStyle(),hoverLineStyle:e.getModel("emphasis.lineStyle").getLineStyle(),labelModel:e.getModel("label"),hoverLabelModel:e.getModel("emphasis.label")}}function qf(t){return isNaN(t[0])||isNaN(t[1])}function Uf(t){return!qf(t[0])&&!qf(t[1])}function $f(t){return!isNaN(t)&&!isFinite(t)}function Kf(t,e,i,n){var r=1-t,a=n.dimensions[t];return $f(e[r])&&$f(i[r])&&e[t]===i[t]&&n.getAxis(a).containData(e[t])}function Qf(t,e){if("cartesian2d"===t.type){var i=e[0].coord,n=e[1].coord;if(i&&n&&(Kf(1,i,n,t)||Kf(0,i,n,t)))return!0}return Lf(t,e[0])&&Lf(t,e[1])}function Jf(t,e,i,n,r){var a,o=n.coordinateSystem,s=t.getItemModel(e),l=Ua(s.get("x"),r.getWidth()),h=Ua(s.get("y"),r.getHeight());if(isNaN(l)||isNaN(h)){if(n.getMarkerPosition)a=n.getMarkerPosition(t.getValues(t.dimensions,e));else{var u=o.dimensions,c=t.get(u[0],e),d=t.get(u[1],e);a=o.dataToPoint([c,d])}if("cartesian2d"===o.type){var f=o.getAxis("x"),p=o.getAxis("y"),u=o.dimensions;$f(t.get(u[0],e))?a[0]=f.toGlobalCoord(f.getExtent()[i?0:1]):$f(t.get(u[1],e))&&(a[1]=p.toGlobalCoord(p.getExtent()[i?0:1]))}isNaN(l)||(a[0]=l),isNaN(h)||(a[1]=h)}else a=[l,h];t.setItemLayout(e,a)}function tp(t,e,i){var n;n=t?p(t&&t.dimensions,function(t){var i=e.getData().getDimensionInfo(e.getData().mapDimension(t))||{};return s({name:t},i)}):[{name:"value",type:"float"}];var r=new Bw(n,i),a=new Bw(n,i),o=new Bw([],i),l=p(i.get("data"),x(ZM,e,t,i));t&&(l=v(l,x(Qf,t)));var h=t?Of:function(t){return t.value};return r.initData(p(l,function(t){return t[0]}),null,h),a.initData(p(l,function(t){return t[1]}),null,h),o.initData(p(l,function(t){return t[2]})),o.hasItemOption=!0,{from:r,to:a,line:o}}function ep(t){return!isNaN(t)&&!isFinite(t)}function ip(t,e,i){var n=1-t;return ep(e[n])&&ep(i[n])}function np(t,e){var i=e.coord[0],n=e.coord[1];return"cartesian2d"===t.type&&i&&n&&(ip(1,i,n,t)||ip(0,i,n,t))?!0:Lf(t,{coord:i,x:e.x0,y:e.y0})||Lf(t,{coord:n,x:e.x1,y:e.y1})}function rp(t,e,i,n,r){var a,o=n.coordinateSystem,s=t.getItemModel(e),l=Ua(s.get(i[0]),r.getWidth()),h=Ua(s.get(i[1]),r.getHeight());if(isNaN(l)||isNaN(h)){if(n.getMarkerPosition)a=n.getMarkerPosition(t.getValues(i,e));else{var u=t.get(i[0],e),c=t.get(i[1],e),d=[u,c];o.clampData&&o.clampData(d,d),a=o.dataToPoint(d,!0)}if("cartesian2d"===o.type){var f=o.getAxis("x"),p=o.getAxis("y"),u=t.get(i[0],e),c=t.get(i[1],e);ep(u)?a[0]=f.toGlobalCoord(f.getExtent()["x0"===i[0]?0:1]):ep(c)&&(a[1]=p.toGlobalCoord(p.getExtent()["y0"===i[1]?0:1]))}isNaN(l)||(a[0]=l),isNaN(h)||(a[1]=h)}else a=[l,h];return a}function ap(t,e,i){var n,r,a=["x0","y0","x1","y1"];t?(n=p(t&&t.dimensions,function(t){var i=e.getData(),n=i.getDimensionInfo(i.mapDimension(t))||{};return s({name:t},n)}),r=new Bw(p(a,function(t,e){return{name:t,type:n[e%2].type}}),i)):(n=[{name:"value",type:"float"}],r=new Bw(n,i));var o=p(i.get("data"),x(XM,e,t,i));t&&(o=v(o,x(np,t)));var l=t?function(t,e,i,n){return t.coord[Math.floor(n/2)][n%2]}:function(t){return t.value};return r.initData(o,null,l),r.hasItemOption=!0,r}function op(t){var e=t.type,i={number:"value",time:"time"};if(i[e]&&(t.axisType=i[e],delete t.type),sp(t),lp(t,"controlPosition")){var n=t.controlStyle||(t.controlStyle={});lp(n,"position")||(n.position=t.controlPosition),"none"!==n.position||lp(n,"show")||(n.show=!1,delete n.position),delete t.controlPosition}f(t.data||[],function(t){S(t)&&!_(t)&&(!lp(t,"value")&&lp(t,"name")&&(t.value=t.name),sp(t))})}function sp(t){var e=t.itemStyle||(t.itemStyle={}),i=e.emphasis||(e.emphasis={}),n=t.label||t.label||{},r=n.normal||(n.normal={}),a={normal:1,emphasis:1};f(n,function(t,e){a[e]||lp(r,e)||(r[e]=t)}),i.label&&!lp(n,"emphasis")&&(n.emphasis=i.label,delete i.label)}function lp(t,e){return t.hasOwnProperty(e)}function hp(t,e){return bo(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()},t.get("padding"))}function up(t,e,i,r){var a=Qr(t.get(e).replace(/^path:\/\//,""),n(r||{}),new gi(i[0],i[1],i[2],i[3]),"center");return a}function cp(t,e,i,n,a,o){var s=e.get("color");if(a)a.setColor(s),i.add(a),o&&o.onUpdate(a);else{var l=t.get("symbol");a=fu(l,-1,-1,2,2,s),a.setStyle("strokeNoScale",!0),i.add(a),o&&o.onCreate(a)}var h=e.getItemStyle(["color","symbol","symbolSize"]);a.setStyle(h),n=r({rectHover:!0,z2:100},n,!0);var u=t.get("symbolSize");u=u instanceof Array?u.slice():[+u,+u],u[0]/=2,u[1]/=2,n.scale=u;var c=t.get("symbolOffset");if(c){var d=n.position=n.position||[0,0];d[0]+=Ua(c[0],u[0]),d[1]+=Ua(c[1],u[1])}var f=t.get("symbolRotate");return n.rotation=(f||0)*Math.PI/180||0,a.attr(n),a.updateTransform(),a}function dp(t,e,i,n,r){if(!t.dragging){var a=n.getModel("checkpointStyle"),o=i.dataToCoord(n.getData().get(["value"],e));r||!a.get("animation",!0)?t.attr({position:[o,0]}):(t.stopAnimation(!0),t.animateTo({position:[o,0]},a.get("animationDuration",!0),a.get("animationEasing",!0)))}}function fp(t){return h(iI,t)>=0}function pp(t,e){t=t.slice();var i=p(t,_o);e=(e||[]).slice();var n=p(e,_o);return function(r,a){f(t,function(t,o){for(var s={name:t,capital:i[o]},l=0;l=0}function r(t,n){var r=!1;return e(function(e){f(i(t,e)||[],function(t){n.records[e.name][t]&&(r=!0)})}),r}function a(t,n){n.nodes.push(t),e(function(e){f(i(t,e)||[],function(t){n.records[e.name][t]=!0})})}return function(i){function o(t){!n(t,s)&&r(t,s)&&(a(t,s),l=!0)}var s={nodes:[],records:{}};if(e(function(t){s.records[t.name]={}}),!i)return s;a(i,s);var l;do l=!1,t(o);while(l);return s}}function vp(t,e,i){var n=[1/0,-1/0];return rI(i,function(t){var i=t.getData();i&&rI(i.mapDimension(e,!0),function(t){var e=i.getApproximateExtent(t);e[0]n[1]&&(n[1]=e[1])})}),n[1]0?0:0/0);var o=i.getMax(!0);return null!=o&&"dataMax"!==o&&"function"!=typeof o?e[1]=o:r&&(e[1]=a>0?a-1:0/0),i.get("scale",!0)||(e[0]>0&&(e[0]=0),e[1]<0&&(e[1]=0)),e}function yp(t,e){var i=t.getAxisModel(),n=t._percentWindow,r=t._valueWindow;if(n){var a=to(r,[0,500]);a=Math.min(a,20);var o=e||0===n[0]&&100===n[1];i.setRange(o?null:+r[0].toFixed(a),o?null:+r[1].toFixed(a))}}function xp(t){var e=t._minMaxSpan={},i=t._dataZoomModel;rI(["min","max"],function(n){e[n+"Span"]=i.get(n+"Span");var r=i.get(n+"ValueSpan");if(null!=r&&(e[n+"ValueSpan"]=r,r=t.getAxisModel().axis.scale.parse(r),null!=r)){var a=t._dataExtent;e[n+"Span"]=qa(a[0]+r,a,[0,100],!0)}})}function _p(t){var e={};return sI(["start","end","startValue","endValue","throttle"],function(i){t.hasOwnProperty(i)&&(e[i]=t[i])}),e}function wp(t,e){var i=t._rangePropMode,n=t.get("rangeMode");sI([["start","startValue"],["end","endValue"]],function(t,r){var a=null!=e[t[0]],o=null!=e[t[1]];a&&!o?i[r]="percent":!a&&o?i[r]="value":n?i[r]=n[r]:a&&(i[r]="percent")})}function bp(t,e){var i=t[e]-t[1-e];return{span:Math.abs(i),sign:i>0?-1:0>i?1:e?-1:1}}function Sp(t,e){return Math.min(e[1],Math.max(e[0],t))}function Mp(t){var e={x:"y",y:"x",radius:"angle",angle:"radius"};return e[t]}function Ip(t){return"vertical"===t?"ns-resize":"ew-resize"}function Tp(t,e){return!!Cp(t)[e]}function Cp(t){return t[II]||(t[II]={})}function Ap(t){this.pointerChecker,this._zr=t,this._opt={};var e=y,i=e(Dp,this),r=e(kp,this),a=e(Pp,this),o=e(Lp,this),l=e(Op,this);bg.call(this),this.setPointerChecker=function(t){this.pointerChecker=t},this.enable=function(e,h){this.disable(),this._opt=s(n(h)||{},{zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!1,preventDefaultMouseMove:!0}),null==e&&(e=!0),(e===!0||"move"===e||"pan"===e)&&(t.on("mousedown",i),t.on("mousemove",r),t.on("mouseup",a)),(e===!0||"scale"===e||"zoom"===e)&&(t.on("mousewheel",o),t.on("pinch",l))},this.disable=function(){t.off("mousedown",i),t.off("mousemove",r),t.off("mouseup",a),t.off("mousewheel",o),t.off("pinch",l)},this.dispose=this.disable,this.isDragging=function(){return this._dragging},this.isPinching=function(){return this._pinching}}function Dp(t){if(!(me(t)||t.target&&t.target.draggable)){var e=t.offsetX,i=t.offsetY;this.pointerChecker&&this.pointerChecker(t,e,i)&&(this._x=e,this._y=i,this._dragging=!0)}}function kp(t){if(!me(t)&&Rp("moveOnMouseMove",t,this._opt)&&this._dragging&&"pinch"!==t.gestureEvent&&!Tp(this._zr,"globalPan")){var e=t.offsetX,i=t.offsetY,n=this._x,r=this._y,a=e-n,o=i-r;this._x=e,this._y=i,this._opt.preventDefaultMouseMove&&Ig(t.event),Ep(this,"pan","moveOnMouseMove",t,{dx:a,dy:o,oldX:n,oldY:r,newX:e,newY:i})}}function Pp(t){me(t)||(this._dragging=!1)}function Lp(t){var e=Rp("zoomOnMouseWheel",t,this._opt),i=Rp("moveOnMouseWheel",t,this._opt),n=t.wheelDelta,r=Math.abs(n),a=t.offsetX,o=t.offsetY;if(0!==n&&(e||i)){if(e){var s=r>3?1.4:r>1?1.2:1.1,l=n>0?s:1/s;zp(this,"zoom","zoomOnMouseWheel",t,{scale:l,originX:a,originY:o})}if(i){var h=Math.abs(n),u=(n>0?1:-1)*(h>3?.4:h>1?.15:.05);zp(this,"scrollMove","moveOnMouseWheel",t,{scrollDelta:u,originX:a,originY:o})}}}function Op(t){if(!Tp(this._zr,"globalPan")){var e=t.pinchScale>1?1.1:1/1.1;zp(this,"zoom",null,t,{scale:e,originX:t.pinchX,originY:t.pinchY})}}function zp(t,e,i,n,r){t.pointerChecker&&t.pointerChecker(n,r.originX,r.originY)&&(Ig(n.event),Ep(t,e,i,n,r))}function Ep(t,e,i,n,r){r.isAvailableBehavior=y(Rp,null,i,n),t.trigger(e,r)}function Rp(t,e,i){var n=i[t];return!t||n&&(!b(n)||e.event[n+"Key"])}function Bp(t,e){var i=Vp(t),n=e.dataZoomId,r=e.coordId;f(i,function(t){var i=t.dataZoomInfos;i[n]&&h(e.allCoordIds,r)<0&&(delete i[n],t.count--)}),Gp(i);var a=i[r];a||(a=i[r]={coordId:r,dataZoomInfos:{},count:0},a.controller=Wp(t,a),a.dispatchAction=x(Hp,t)),!a.dataZoomInfos[n]&&a.count++,a.dataZoomInfos[n]=e;var o=Zp(a.dataZoomInfos);a.controller.enable(o.controlType,o.opt),a.controller.setPointerChecker(e.containsPoint),Hs(a,"dispatchAction",e.dataZoomModel.get("throttle",!0),"fixRate")}function Np(t,e){var i=Vp(t);f(i,function(t){t.controller.dispose();var i=t.dataZoomInfos;i[e]&&(delete i[e],t.count--)}),Gp(i)}function Fp(t){return t.type+"\x00_"+t.id}function Vp(t){var e=t.getZr();return e[TI]||(e[TI]={})}function Wp(t,e){var i=new Ap(t.getZr());return f(["pan","zoom","scrollMove"],function(t){i.on(t,function(i){var n=[];f(e.dataZoomInfos,function(r){if(i.isAvailableBehavior(r.dataZoomModel.option)){var a=(r.getRange||{})[t],o=a&&a(e.controller,i);!r.dataZoomModel.get("disabled",!0)&&o&&n.push({dataZoomId:r.dataZoomId,start:o[0],end:o[1]})}}),n.length&&e.dispatchAction(n)})}),i}function Gp(t){f(t,function(e,i){e.count||(e.controller.dispose(),delete t[i])})}function Hp(t,e){t.dispatchAction({type:"dataZoom",batch:e})}function Zp(t){var e,i="type_",n={type_true:2,type_move:1,type_false:0,type_undefined:-1},r=!0;return f(t,function(t){var a=t.dataZoomModel,o=a.get("disabled",!0)?!1:a.get("zoomLock",!0)?"move":!0;n[i+o]>n[i+e]&&(e=o),r&=a.get("preventDefaultMouseMove",!0)}),{controlType:e,opt:{zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!0,preventDefaultMouseMove:!!r}}}function Xp(t){return function(e,i,n,r){var a=this._range,o=a.slice(),s=e.axisModels[0];if(s){var l=t(o,s,e,i,n,r);return cI(l,o,[0,100],"all"),this._range=o,a[0]!==o[0]||a[1]!==o[1]?o:void 0}}}function Yp(t){return PI(t)}function jp(){if(!zI&&EI){zI=!0;var t=EI.styleSheets;t.length<31?EI.createStyleSheet().addRule(".zrvml","behavior:url(#default#VML)"):t[0].addRule(".zrvml","behavior:url(#default#VML)")}}function qp(t){return parseInt(t,10)}function Up(t,e){jp(),this.root=t,this.storage=e;var i=document.createElement("div"),n=document.createElement("div");i.style.cssText="display:inline-block;overflow:hidden;position:relative;width:300px;height:150px;",n.style.cssText="position:absolute;left:0;top:0;",t.appendChild(i),this._vmlRoot=n,this._vmlViewport=i,this.resize();var r=e.delFromStorage,a=e.addToStorage;e.delFromStorage=function(t){r.call(e,t),t&&t.onRemove&&t.onRemove(n)},e.addToStorage=function(t){t.onAdd&&t.onAdd(n),a.call(e,t) +},this._firstPaint=!0}function $p(t){return function(){iv('In IE8.0 VML mode painter not support method "'+t+'"')}}var Kp=2311,Qp=function(){return Kp++},Jp={};Jp="object"==typeof wx&&"function"==typeof wx.getSystemInfoSync?{browser:{},os:{},node:!1,wxa:!0,canvasSupported:!0,svgSupported:!1,touchEventsSupported:!0,domSupported:!1}:"undefined"==typeof document&&"undefined"!=typeof self?{browser:{},os:{},node:!1,worker:!0,canvasSupported:!0,domSupported:!1}:"undefined"==typeof navigator?{browser:{},os:{},node:!0,worker:!1,canvasSupported:!0,svgSupported:!0,domSupported:!1}:e(navigator.userAgent);var tg=Jp,eg={"[object Function]":1,"[object RegExp]":1,"[object Date]":1,"[object Error]":1,"[object CanvasGradient]":1,"[object CanvasPattern]":1,"[object Image]":1,"[object Canvas]":1},ig={"[object Int8Array]":1,"[object Uint8Array]":1,"[object Uint8ClampedArray]":1,"[object Int16Array]":1,"[object Uint16Array]":1,"[object Int32Array]":1,"[object Uint32Array]":1,"[object Float32Array]":1,"[object Float64Array]":1},ng=Object.prototype.toString,rg=Array.prototype,ag=rg.forEach,og=rg.filter,sg=rg.slice,lg=rg.map,hg=rg.reduce,ug={},cg=function(){return ug.createCanvas()};ug.createCanvas=function(){return document.createElement("canvas")};var dg,fg="__ec_primitive__";B.prototype={constructor:B,get:function(t){return this.data.hasOwnProperty(t)?this.data[t]:null},set:function(t,e){return this.data[t]=e},each:function(t,e){void 0!==e&&(t=y(t,e));for(var i in this.data)this.data.hasOwnProperty(i)&&t(this.data[i],i)},removeKey:function(t){delete this.data[t]}};var pg=(Object.freeze||Object)({$override:i,clone:n,merge:r,mergeAll:a,extend:o,defaults:s,createCanvas:cg,getContext:l,indexOf:h,inherits:u,mixin:c,isArrayLike:d,each:f,map:p,reduce:g,filter:v,find:m,bind:y,curry:x,isArray:_,isFunction:w,isString:b,isObject:S,isBuiltInObject:M,isTypedArray:I,isDom:T,eqNaN:C,retrieve:A,retrieve2:D,retrieve3:k,slice:P,normalizeCssArray:L,assert:O,trim:z,setAsPrimitive:E,isPrimitive:R,createHashMap:N,concatArray:F,noop:V}),gg="undefined"==typeof Float32Array?Array:Float32Array,vg=q,mg=U,yg=ee,xg=ie,_g=(Object.freeze||Object)({create:W,copy:G,clone:H,set:Z,add:X,scaleAndAdd:Y,sub:j,len:q,length:vg,lenSquare:U,lengthSquare:mg,mul:$,div:K,dot:Q,scale:J,normalize:te,distance:ee,dist:yg,distanceSquare:ie,distSquare:xg,negate:ne,lerp:re,applyTransform:ae,min:oe,max:se});le.prototype={constructor:le,_dragStart:function(t){var e=t.target;e&&e.draggable&&(this._draggingTarget=e,e.dragging=!0,this._x=t.offsetX,this._y=t.offsetY,this.dispatchToElement(he(e,t),"dragstart",t.event))},_drag:function(t){var e=this._draggingTarget;if(e){var i=t.offsetX,n=t.offsetY,r=i-this._x,a=n-this._y;this._x=i,this._y=n,e.drift(r,a,t),this.dispatchToElement(he(e,t),"drag",t.event);var o=this.findHover(i,n,e).target,s=this._dropTarget;this._dropTarget=o,e!==o&&(s&&o!==s&&this.dispatchToElement(he(s,t),"dragleave",t.event),o&&o!==s&&this.dispatchToElement(he(o,t),"dragenter",t.event))}},_dragEnd:function(t){var e=this._draggingTarget;e&&(e.dragging=!1),this.dispatchToElement(he(e,t),"dragend",t.event),this._dropTarget&&this.dispatchToElement(he(this._dropTarget,t),"drop",t.event),this._draggingTarget=null,this._dropTarget=null}};var wg=Array.prototype.slice,bg=function(t){this._$handlers={},this._$eventProcessor=t};bg.prototype={constructor:bg,one:function(t,e,i,n){var r=this._$handlers;if("function"==typeof e&&(n=i,i=e,e=null),!i||!t)return this;e=ue(this,e),r[t]||(r[t]=[]);for(var a=0;ar;r++)i[t][r].h!==e&&n.push(i[t][r]);i[t]=n}i[t]&&0===i[t].length&&delete i[t]}else delete i[t];return this},trigger:function(t){var e=this._$handlers[t],i=this._$eventProcessor;if(e){var n=arguments,r=n.length;r>3&&(n=wg.call(n,1));for(var a=e.length,o=0;a>o;){var s=e[o];if(i&&i.filter&&null!=s.query&&!i.filter(t,s.query))o++;else{switch(r){case 1:s.h.call(s.ctx);break;case 2:s.h.call(s.ctx,n[1]);break;case 3:s.h.call(s.ctx,n[1],n[2]);break;default:s.h.apply(s.ctx,n)}s.one?(e.splice(o,1),a--):o++}}}return i&&i.afterTrigger&&i.afterTrigger(t),this},triggerWithContext:function(t){var e=this._$handlers[t],i=this._$eventProcessor;if(e){var n=arguments,r=n.length;r>4&&(n=wg.call(n,1,n.length-1));for(var a=n[n.length-1],o=e.length,s=0;o>s;){var l=e[s];if(i&&i.filter&&null!=l.query&&!i.filter(t,l.query))s++;else{switch(r){case 1:l.h.call(a);break;case 2:l.h.call(a,n[1]);break;case 3:l.h.call(a,n[1],n[2]);break;default:l.h.apply(a,n)}l.one?(e.splice(s,1),o--):s++}}}return i&&i.afterTrigger&&i.afterTrigger(t),this}};var Sg="undefined"!=typeof window&&!!window.addEventListener,Mg=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ig=Sg?function(t){t.preventDefault(),t.stopPropagation(),t.cancelBubble=!0}:function(t){t.returnValue=!1,t.cancelBubble=!0},Tg="silent";_e.prototype.dispose=function(){};var Cg=["click","dblclick","mousewheel","mouseout","mouseup","mousedown","mousemove","contextmenu"],Ag=function(t,e,i,n){bg.call(this),this.storage=t,this.painter=e,this.painterRoot=n,i=i||new _e,this.proxy=null,this._hovered={},this._lastTouchMoment,this._lastX,this._lastY,le.call(this),this.setHandlerProxy(i)};Ag.prototype={constructor:Ag,setHandlerProxy:function(t){this.proxy&&this.proxy.dispose(),t&&(f(Cg,function(e){t.on&&t.on(e,this[e],this)},this),t.handler=this),this.proxy=t},mousemove:function(t){var e=t.zrX,i=t.zrY,n=this._hovered,r=n.target;r&&!r.__zr&&(n=this.findHover(n.x,n.y),r=n.target);var a=this._hovered=this.findHover(e,i),o=a.target,s=this.proxy;s.setCursor&&s.setCursor(o?o.cursor:"default"),r&&o!==r&&this.dispatchToElement(n,"mouseout",t),this.dispatchToElement(a,"mousemove",t),o&&o!==r&&this.dispatchToElement(a,"mouseover",t)},mouseout:function(t){this.dispatchToElement(this._hovered,"mouseout",t);var e,i=t.toElement||t.relatedTarget;do i=i&&i.parentNode;while(i&&9!=i.nodeType&&!(e=i===this.painterRoot));!e&&this.trigger("globalout",{event:t})},resize:function(){this._hovered={}},dispatch:function(t,e){var i=this[t];i&&i.call(this,e)},dispose:function(){this.proxy.dispose(),this.storage=this.proxy=this.painter=null},setCursorStyle:function(t){var e=this.proxy;e.setCursor&&e.setCursor(t)},dispatchToElement:function(t,e,i){t=t||{};var n=t.target;if(!n||!n.silent){for(var r="on"+e,a=ye(e,t,i);n&&(n[r]&&(a.cancelBubble=n[r].call(n,a)),n.trigger(e,a),n=n.parent,!a.cancelBubble););a.cancelBubble||(this.trigger(e,a),this.painter&&this.painter.eachOtherLayer(function(t){"function"==typeof t[r]&&t[r].call(t,a),t.trigger&&t.trigger(e,a)}))}},findHover:function(t,e,i){for(var n=this.storage.getDisplayList(),r={x:t,y:e},a=n.length-1;a>=0;a--){var o;if(n[a]!==i&&!n[a].ignore&&(o=we(n[a],t,e))&&(!r.topTarget&&(r.topTarget=n[a]),o!==Tg)){r.target=n[a];break}}return r}},f(["click","mousedown","mouseup","mousewheel","dblclick","contextmenu"],function(t){Ag.prototype[t]=function(e){var i=this.findHover(e.zrX,e.zrY),n=i.target;if("mousedown"===t)this._downEl=n,this._downPoint=[e.zrX,e.zrY],this._upEl=n;else if("mouseup"===t)this._upEl=n;else if("click"===t){if(this._downEl!==this._upEl||!this._downPoint||yg(this._downPoint,[e.zrX,e.zrY])>4)return;this._downPoint=null}this.dispatchToElement(i,t,e)}}),c(Ag,bg),c(Ag,le);var Dg="undefined"==typeof Float32Array?Array:Float32Array,kg=(Object.freeze||Object)({create:be,identity:Se,copy:Me,mul:Ie,translate:Te,rotate:Ce,scale:Ae,invert:De,clone:ke}),Pg=Se,Lg=5e-5,Og=function(t){t=t||{},t.position||(this.position=[0,0]),null==t.rotation&&(this.rotation=0),t.scale||(this.scale=[1,1]),this.origin=this.origin||null},zg=Og.prototype;zg.transform=null,zg.needLocalTransform=function(){return Pe(this.rotation)||Pe(this.position[0])||Pe(this.position[1])||Pe(this.scale[0]-1)||Pe(this.scale[1]-1)};var Eg=[];zg.updateTransform=function(){var t=this.parent,e=t&&t.transform,i=this.needLocalTransform(),n=this.transform;if(!i&&!e)return void(n&&Pg(n));n=n||be(),i?this.getLocalTransform(n):Pg(n),e&&(i?Ie(n,t.transform,n):Me(n,t.transform)),this.transform=n;var r=this.globalScaleRatio;if(null!=r&&1!==r){this.getGlobalScale(Eg);var a=Eg[0]<0?-1:1,o=Eg[1]<0?-1:1,s=((Eg[0]-a)*r+a)/Eg[0]||0,l=((Eg[1]-o)*r+o)/Eg[1]||0;n[0]*=s,n[1]*=s,n[2]*=l,n[3]*=l}this.invTransform=this.invTransform||be(),De(this.invTransform,n)},zg.getLocalTransform=function(t){return Og.getLocalTransform(this,t)},zg.setTransform=function(t){var e=this.transform,i=t.dpr||1;e?t.setTransform(i*e[0],i*e[1],i*e[2],i*e[3],i*e[4],i*e[5]):t.setTransform(i,0,0,i,0,0)},zg.restoreTransform=function(t){var e=t.dpr||1;t.setTransform(e,0,0,e,0,0)};var Rg=[],Bg=be();zg.setLocalTransform=function(t){if(t){var e=t[0]*t[0]+t[1]*t[1],i=t[2]*t[2]+t[3]*t[3],n=this.position,r=this.scale;Pe(e-1)&&(e=Math.sqrt(e)),Pe(i-1)&&(i=Math.sqrt(i)),t[0]<0&&(e=-e),t[3]<0&&(i=-i),n[0]=t[4],n[1]=t[5],r[0]=e,r[1]=i,this.rotation=Math.atan2(-t[1]/i,t[0]/e)}},zg.decomposeTransform=function(){if(this.transform){var t=this.parent,e=this.transform;t&&t.transform&&(Ie(Rg,t.invTransform,e),e=Rg);var i=this.origin;i&&(i[0]||i[1])&&(Bg[4]=i[0],Bg[5]=i[1],Ie(Rg,e,Bg),Rg[4]-=i[0],Rg[5]-=i[1],e=Rg),this.setLocalTransform(e)}},zg.getGlobalScale=function(t){var e=this.transform;return t=t||[],e?(t[0]=Math.sqrt(e[0]*e[0]+e[1]*e[1]),t[1]=Math.sqrt(e[2]*e[2]+e[3]*e[3]),e[0]<0&&(t[0]=-t[0]),e[3]<0&&(t[1]=-t[1]),t):(t[0]=1,t[1]=1,t)},zg.transformCoordToLocal=function(t,e){var i=[t,e],n=this.invTransform;return n&&ae(i,i,n),i},zg.transformCoordToGlobal=function(t,e){var i=[t,e],n=this.transform;return n&&ae(i,i,n),i},Og.getLocalTransform=function(t,e){e=e||[],Pg(e);var i=t.origin,n=t.scale||[1,1],r=t.rotation||0,a=t.position||[0,0];return i&&(e[4]-=i[0],e[5]-=i[1]),Ae(e,e,n),r&&Ce(e,e,r),i&&(e[4]+=i[0],e[5]+=i[1]),e[4]+=a[0],e[5]+=a[1],e};var Ng={linear:function(t){return t},quadraticIn:function(t){return t*t},quadraticOut:function(t){return t*(2-t)},quadraticInOut:function(t){return(t*=2)<1?.5*t*t:-.5*(--t*(t-2)-1)},cubicIn:function(t){return t*t*t},cubicOut:function(t){return--t*t*t+1},cubicInOut:function(t){return(t*=2)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},quarticIn:function(t){return t*t*t*t},quarticOut:function(t){return 1- --t*t*t*t},quarticInOut:function(t){return(t*=2)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},quinticIn:function(t){return t*t*t*t*t},quinticOut:function(t){return--t*t*t*t*t+1},quinticInOut:function(t){return(t*=2)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},sinusoidalIn:function(t){return 1-Math.cos(t*Math.PI/2)},sinusoidalOut:function(t){return Math.sin(t*Math.PI/2)},sinusoidalInOut:function(t){return.5*(1-Math.cos(Math.PI*t))},exponentialIn:function(t){return 0===t?0:Math.pow(1024,t-1)},exponentialOut:function(t){return 1===t?1:1-Math.pow(2,-10*t)},exponentialInOut:function(t){return 0===t?0:1===t?1:(t*=2)<1?.5*Math.pow(1024,t-1):.5*(-Math.pow(2,-10*(t-1))+2)},circularIn:function(t){return 1-Math.sqrt(1-t*t)},circularOut:function(t){return Math.sqrt(1- --t*t)},circularInOut:function(t){return(t*=2)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},elasticIn:function(t){var e,i=.1,n=.4;return 0===t?0:1===t?1:(!i||1>i?(i=1,e=n/4):e=n*Math.asin(1/i)/(2*Math.PI),-(i*Math.pow(2,10*(t-=1))*Math.sin(2*(t-e)*Math.PI/n)))},elasticOut:function(t){var e,i=.1,n=.4;return 0===t?0:1===t?1:(!i||1>i?(i=1,e=n/4):e=n*Math.asin(1/i)/(2*Math.PI),i*Math.pow(2,-10*t)*Math.sin(2*(t-e)*Math.PI/n)+1)},elasticInOut:function(t){var e,i=.1,n=.4;return 0===t?0:1===t?1:(!i||1>i?(i=1,e=n/4):e=n*Math.asin(1/i)/(2*Math.PI),(t*=2)<1?-.5*i*Math.pow(2,10*(t-=1))*Math.sin(2*(t-e)*Math.PI/n):i*Math.pow(2,-10*(t-=1))*Math.sin(2*(t-e)*Math.PI/n)*.5+1)},backIn:function(t){var e=1.70158;return t*t*((e+1)*t-e)},backOut:function(t){var e=1.70158;return--t*t*((e+1)*t+e)+1},backInOut:function(t){var e=2.5949095;return(t*=2)<1?.5*t*t*((e+1)*t-e):.5*((t-=2)*t*((e+1)*t+e)+2)},bounceIn:function(t){return 1-Ng.bounceOut(1-t)},bounceOut:function(t){return 1/2.75>t?7.5625*t*t:2/2.75>t?7.5625*(t-=1.5/2.75)*t+.75:2.5/2.75>t?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},bounceInOut:function(t){return.5>t?.5*Ng.bounceIn(2*t):.5*Ng.bounceOut(2*t-1)+.5}};Le.prototype={constructor:Le,step:function(t,e){if(this._initialized||(this._startTime=t+this._delay,this._initialized=!0),this._paused)return void(this._pausedTime+=e);var i=(t-this._startTime-this._pausedTime)/this._life;if(!(0>i)){i=Math.min(i,1);var n=this.easing,r="string"==typeof n?Ng[n]:n,a="function"==typeof r?r(i):i;return this.fire("frame",a),1==i?this.loop?(this.restart(t),"restart"):(this._needsRemove=!0,"destroy"):null}},restart:function(t){var e=(t-this._startTime-this._pausedTime)%this._life;this._startTime=t-e+this.gap,this._pausedTime=0,this._needsRemove=!1},fire:function(t,e){t="on"+t,this[t]&&this[t](this._target,e)},pause:function(){this._paused=!0},resume:function(){this._paused=!1}};var Fg=function(){this.head=null,this.tail=null,this._len=0},Vg=Fg.prototype;Vg.insert=function(t){var e=new Wg(t);return this.insertEntry(e),e},Vg.insertEntry=function(t){this.head?(this.tail.next=t,t.prev=this.tail,t.next=null,this.tail=t):this.head=this.tail=t,this._len++},Vg.remove=function(t){var e=t.prev,i=t.next;e?e.next=i:this.head=i,i?i.prev=e:this.tail=e,t.next=t.prev=null,this._len--},Vg.len=function(){return this._len},Vg.clear=function(){this.head=this.tail=null,this._len=0};var Wg=function(t){this.value=t,this.next,this.prev},Gg=function(t){this._list=new Fg,this._map={},this._maxSize=t||10,this._lastRemovedEntry=null},Hg=Gg.prototype;Hg.put=function(t,e){var i=this._list,n=this._map,r=null;if(null==n[t]){var a=i.len(),o=this._lastRemovedEntry;if(a>=this._maxSize&&a>0){var s=i.head;i.remove(s),delete n[s.key],r=s.value,this._lastRemovedEntry=s}o?o.value=e:o=new Wg(e),o.key=t,i.insertEntry(o),n[t]=o}return r},Hg.get=function(t){var e=this._map[t],i=this._list;return null!=e?(e!==i.tail&&(i.remove(e),i.insertEntry(e)),e.value):void 0},Hg.clear=function(){this._list.clear(),this._map={}};var Zg={transparent:[0,0,0,0],aliceblue:[240,248,255,1],antiquewhite:[250,235,215,1],aqua:[0,255,255,1],aquamarine:[127,255,212,1],azure:[240,255,255,1],beige:[245,245,220,1],bisque:[255,228,196,1],black:[0,0,0,1],blanchedalmond:[255,235,205,1],blue:[0,0,255,1],blueviolet:[138,43,226,1],brown:[165,42,42,1],burlywood:[222,184,135,1],cadetblue:[95,158,160,1],chartreuse:[127,255,0,1],chocolate:[210,105,30,1],coral:[255,127,80,1],cornflowerblue:[100,149,237,1],cornsilk:[255,248,220,1],crimson:[220,20,60,1],cyan:[0,255,255,1],darkblue:[0,0,139,1],darkcyan:[0,139,139,1],darkgoldenrod:[184,134,11,1],darkgray:[169,169,169,1],darkgreen:[0,100,0,1],darkgrey:[169,169,169,1],darkkhaki:[189,183,107,1],darkmagenta:[139,0,139,1],darkolivegreen:[85,107,47,1],darkorange:[255,140,0,1],darkorchid:[153,50,204,1],darkred:[139,0,0,1],darksalmon:[233,150,122,1],darkseagreen:[143,188,143,1],darkslateblue:[72,61,139,1],darkslategray:[47,79,79,1],darkslategrey:[47,79,79,1],darkturquoise:[0,206,209,1],darkviolet:[148,0,211,1],deeppink:[255,20,147,1],deepskyblue:[0,191,255,1],dimgray:[105,105,105,1],dimgrey:[105,105,105,1],dodgerblue:[30,144,255,1],firebrick:[178,34,34,1],floralwhite:[255,250,240,1],forestgreen:[34,139,34,1],fuchsia:[255,0,255,1],gainsboro:[220,220,220,1],ghostwhite:[248,248,255,1],gold:[255,215,0,1],goldenrod:[218,165,32,1],gray:[128,128,128,1],green:[0,128,0,1],greenyellow:[173,255,47,1],grey:[128,128,128,1],honeydew:[240,255,240,1],hotpink:[255,105,180,1],indianred:[205,92,92,1],indigo:[75,0,130,1],ivory:[255,255,240,1],khaki:[240,230,140,1],lavender:[230,230,250,1],lavenderblush:[255,240,245,1],lawngreen:[124,252,0,1],lemonchiffon:[255,250,205,1],lightblue:[173,216,230,1],lightcoral:[240,128,128,1],lightcyan:[224,255,255,1],lightgoldenrodyellow:[250,250,210,1],lightgray:[211,211,211,1],lightgreen:[144,238,144,1],lightgrey:[211,211,211,1],lightpink:[255,182,193,1],lightsalmon:[255,160,122,1],lightseagreen:[32,178,170,1],lightskyblue:[135,206,250,1],lightslategray:[119,136,153,1],lightslategrey:[119,136,153,1],lightsteelblue:[176,196,222,1],lightyellow:[255,255,224,1],lime:[0,255,0,1],limegreen:[50,205,50,1],linen:[250,240,230,1],magenta:[255,0,255,1],maroon:[128,0,0,1],mediumaquamarine:[102,205,170,1],mediumblue:[0,0,205,1],mediumorchid:[186,85,211,1],mediumpurple:[147,112,219,1],mediumseagreen:[60,179,113,1],mediumslateblue:[123,104,238,1],mediumspringgreen:[0,250,154,1],mediumturquoise:[72,209,204,1],mediumvioletred:[199,21,133,1],midnightblue:[25,25,112,1],mintcream:[245,255,250,1],mistyrose:[255,228,225,1],moccasin:[255,228,181,1],navajowhite:[255,222,173,1],navy:[0,0,128,1],oldlace:[253,245,230,1],olive:[128,128,0,1],olivedrab:[107,142,35,1],orange:[255,165,0,1],orangered:[255,69,0,1],orchid:[218,112,214,1],palegoldenrod:[238,232,170,1],palegreen:[152,251,152,1],paleturquoise:[175,238,238,1],palevioletred:[219,112,147,1],papayawhip:[255,239,213,1],peachpuff:[255,218,185,1],peru:[205,133,63,1],pink:[255,192,203,1],plum:[221,160,221,1],powderblue:[176,224,230,1],purple:[128,0,128,1],red:[255,0,0,1],rosybrown:[188,143,143,1],royalblue:[65,105,225,1],saddlebrown:[139,69,19,1],salmon:[250,128,114,1],sandybrown:[244,164,96,1],seagreen:[46,139,87,1],seashell:[255,245,238,1],sienna:[160,82,45,1],silver:[192,192,192,1],skyblue:[135,206,235,1],slateblue:[106,90,205,1],slategray:[112,128,144,1],slategrey:[112,128,144,1],snow:[255,250,250,1],springgreen:[0,255,127,1],steelblue:[70,130,180,1],tan:[210,180,140,1],teal:[0,128,128,1],thistle:[216,191,216,1],tomato:[255,99,71,1],turquoise:[64,224,208,1],violet:[238,130,238,1],wheat:[245,222,179,1],white:[255,255,255,1],whitesmoke:[245,245,245,1],yellow:[255,255,0,1],yellowgreen:[154,205,50,1]},Xg=new Gg(20),Yg=null,jg=qe,qg=Ue,Ug=(Object.freeze||Object)({parse:He,lift:Ye,toHex:je,fastLerp:qe,fastMapToColor:jg,lerp:Ue,mapToColor:qg,modifyHSL:$e,modifyAlpha:Ke,stringify:Qe}),$g=Array.prototype.slice,Kg=function(t,e,i,n){this._tracks={},this._target=t,this._loop=e||!1,this._getter=i||Je,this._setter=n||ti,this._clipCount=0,this._delay=0,this._doneList=[],this._onframeList=[],this._clipList=[]};Kg.prototype={when:function(t,e){var i=this._tracks;for(var n in e)if(e.hasOwnProperty(n)){if(!i[n]){i[n]=[];var r=this._getter(this._target,n);if(null==r)continue;0!==t&&i[n].push({time:0,value:li(r)})}i[n].push({time:t,value:e[n]})}return this},during:function(t){return this._onframeList.push(t),this},pause:function(){for(var t=0;ti;i++)t[i].call(this)},start:function(t,e){var i,n=this,r=0,a=function(){r--,r||n._doneCallback()};for(var o in this._tracks)if(this._tracks.hasOwnProperty(o)){var s=ci(this,t,a,this._tracks[o],o,e);s&&(this._clipList.push(s),r++,this.animation&&this.animation.addClip(s),i=s)}if(i){var l=i.onframe;i.onframe=function(t,e){l(t,e);for(var i=0;i1&&(ev=function(){for(var t in arguments)console.log(arguments[t])});var iv=ev,nv=function(){this.animators=[]};nv.prototype={constructor:nv,animate:function(t,e){var i,n=!1,r=this,a=this.__zr;if(t){var o=t.split("."),s=r;n="shape"===o[0];for(var l=0,u=o.length;u>l;l++)s&&(s=s[o[l]]);s&&(i=s)}else i=r;if(!i)return void iv('Property "'+t+'" is not existed in element '+r.id);var c=r.animators,d=new Kg(i,e);return d.during(function(){r.dirty(n)}).done(function(){c.splice(h(c,d),1)}),c.push(d),a&&a.animation.addAnimator(d),d},stopAnimation:function(t){for(var e=this.animators,i=e.length,n=0;i>n;n++)e[n].stop(t);return e.length=0,this},animateTo:function(t,e,i,n,r,a){di(this,t,e,i,n,r,a)},animateFrom:function(t,e,i,n,r,a){di(this,t,e,i,n,r,a,!0)}};var rv=function(t){Og.call(this,t),bg.call(this,t),nv.call(this,t),this.id=t.id||Qp()};rv.prototype={type:"element",name:"",__zr:null,ignore:!1,clipPath:null,isGroup:!1,drift:function(t,e){switch(this.draggable){case"horizontal":e=0;break;case"vertical":t=0}var i=this.transform;i||(i=this.transform=[1,0,0,1,0,0]),i[4]+=t,i[5]+=e,this.decomposeTransform(),this.dirty(!1)},beforeUpdate:function(){},afterUpdate:function(){},update:function(){this.updateTransform()},traverse:function(){},attrKV:function(t,e){if("position"===t||"scale"===t||"origin"===t){if(e){var i=this[t];i||(i=this[t]=[]),i[0]=e[0],i[1]=e[1]}}else this[t]=e},hide:function(){this.ignore=!0,this.__zr&&this.__zr.refresh()},show:function(){this.ignore=!1,this.__zr&&this.__zr.refresh()},attr:function(t,e){if("string"==typeof t)this.attrKV(t,e);else if(S(t))for(var i in t)t.hasOwnProperty(i)&&this.attrKV(i,t[i]);return this.dirty(!1),this},setClipPath:function(t){var e=this.__zr;e&&t.addSelfToZr(e),this.clipPath&&this.clipPath!==t&&this.removeClipPath(),this.clipPath=t,t.__zr=e,t.__clipTarget=this,this.dirty(!1)},removeClipPath:function(){var t=this.clipPath;t&&(t.__zr&&t.removeSelfFromZr(t.__zr),t.__zr=null,t.__clipTarget=null,this.clipPath=null,this.dirty(!1))},addSelfToZr:function(t){this.__zr=t;var e=this.animators;if(e)for(var i=0;in||i>s||l>a||r>h)},contain:function(t,e){var i=this;return t>=i.x&&t<=i.x+i.width&&e>=i.y&&e<=i.y+i.height},clone:function(){return new gi(this.x,this.y,this.width,this.height)},copy:function(t){this.x=t.x,this.y=t.y,this.width=t.width,this.height=t.height},plain:function(){return{x:this.x,y:this.y,width:this.width,height:this.height}}},gi.create=function(t){return new gi(t.x,t.y,t.width,t.height)};var lv=function(t){t=t||{},rv.call(this,t);for(var e in t)t.hasOwnProperty(e)&&(this[e]=t[e]);this._children=[],this.__storage=null,this.__dirty=!0};lv.prototype={constructor:lv,isGroup:!0,type:"group",silent:!1,children:function(){return this._children.slice()},childAt:function(t){return this._children[t]},childOfName:function(t){for(var e=this._children,i=0;i=0&&(i.splice(n,0,t),this._doAdd(t))}return this},_doAdd:function(t){t.parent&&t.parent.remove(t),t.parent=this;var e=this.__storage,i=this.__zr;e&&e!==t.__storage&&(e.addToStorage(t),t instanceof lv&&t.addChildrenToStorage(e)),i&&i.refresh()},remove:function(t){var e=this.__zr,i=this.__storage,n=this._children,r=h(n,t);return 0>r?this:(n.splice(r,1),t.parent=null,i&&(i.delFromStorage(t),t instanceof lv&&t.delChildrenFromStorage(i)),e&&e.refresh(),this)},removeAll:function(){var t,e,i=this._children,n=this.__storage;for(e=0;en;n++)this._updateAndAddDisplayable(e[n],null,t);i.length=this._displayListLen,tg.canvasSupported&&Si(i,Mi)},_updateAndAddDisplayable:function(t,e,i){if(!t.ignore||i){t.beforeUpdate(),t.__dirty&&t.update(),t.afterUpdate();var n=t.clipPath;if(n){e=e?e.slice():[];for(var r=n,a=t;r;)r.parent=a,r.updateTransform(),e.push(r),a=r,r=r.clipPath}if(t.isGroup){for(var o=t._children,s=0;se;e++)this.delRoot(t[e]);else{var r=h(this._roots,t);r>=0&&(this.delFromStorage(t),this._roots.splice(r,1),t instanceof lv&&t.delChildrenFromStorage(this))}},addToStorage:function(t){return t&&(t.__storage=this,t.dirty(!1)),this},delFromStorage:function(t){return t&&(t.__storage=null),this},dispose:function(){this._renderList=this._roots=null},displayableSortFunc:Mi};var dv={shadowBlur:1,shadowOffsetX:1,shadowOffsetY:1,textShadowBlur:1,textShadowOffsetX:1,textShadowOffsetY:1,textBoxShadowBlur:1,textBoxShadowOffsetX:1,textBoxShadowOffsetY:1},fv=function(t,e,i){return dv.hasOwnProperty(e)?i*=t.dpr:i},pv=[["shadowBlur",0],["shadowOffsetX",0],["shadowOffsetY",0],["shadowColor","#000"],["lineCap","butt"],["lineJoin","miter"],["miterLimit",10]],gv=function(t){this.extendFrom(t,!1)};gv.prototype={constructor:gv,fill:"#000",stroke:null,opacity:1,fillOpacity:null,strokeOpacity:null,lineDash:null,lineDashOffset:0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,lineWidth:1,strokeNoScale:!1,text:null,font:null,textFont:null,fontStyle:null,fontWeight:null,fontSize:null,fontFamily:null,textTag:null,textFill:"#000",textStroke:null,textWidth:null,textHeight:null,textStrokeWidth:0,textLineHeight:null,textPosition:"inside",textRect:null,textOffset:null,textAlign:null,textVerticalAlign:null,textDistance:5,textShadowColor:"transparent",textShadowBlur:0,textShadowOffsetX:0,textShadowOffsetY:0,textBoxShadowColor:"transparent",textBoxShadowBlur:0,textBoxShadowOffsetX:0,textBoxShadowOffsetY:0,transformText:!1,textRotation:0,textOrigin:null,textBackgroundColor:null,textBorderColor:null,textBorderWidth:0,textBorderRadius:0,textPadding:null,rich:null,truncate:null,blend:null,bind:function(t,e,i){for(var n=this,r=i&&i.style,a=!r,o=0;o0},extendFrom:function(t,e){if(t)for(var i in t)!t.hasOwnProperty(i)||e!==!0&&(e===!1?this.hasOwnProperty(i):null==t[i])||(this[i]=t[i])},set:function(t,e){"string"==typeof t?this[t]=e:this.extendFrom(t,!0)},clone:function(){var t=new this.constructor;return t.extendFrom(this,!0),t},getGradient:function(t,e,i){for(var n="radial"===e.type?Ti:Ii,r=n(t,e,i),a=e.colorStops,o=0;o=0&&i.splice(n,1),t.__hoverMir=null},clearHover:function(){for(var t=this._hoverElements,e=0;er;){var a=t[r],o=a.__from;o&&o.__zr?(r++,o.invisible||(a.transform=o.transform,a.invTransform=o.invTransform,a.__clipPaths=o.__clipPaths,this._doPaintEl(a,i,!0,n))):(t.splice(r,1),o.__hoverMir=null,e--)}i.ctx.restore()}},getHoverLayer:function(){return this.getLayer(zv)},_paintList:function(t,e,i){if(this._redrawId===i){e=e||!1,this._updateLayerStatus(t);var n=this._doPaintList(t,e);if(this._needsManuallyCompositing&&this._compositeManually(),!n){var r=this;wv(function(){r._paintList(t,e,i)})}}},_compositeManually:function(){var t=this.getLayer(Ev).ctx,e=this._domRoot.width,i=this._domRoot.height;t.clearRect(0,0,e,i),this.eachBuiltinLayer(function(n){n.virtual&&t.drawImage(n.dom,0,0,e,i)})},_doPaintList:function(t,e){for(var i=[],n=0;n15)break}}a.__drawIndex=v,a.__drawIndex0&&t>n[0]){for(o=0;r-1>o&&!(n[o]t);o++);a=i[n[o]]}if(n.splice(o+1,0,t),i[t]=e,!e.virtual)if(a){var l=a.dom;l.nextSibling?s.insertBefore(e.dom,l.nextSibling):s.appendChild(e.dom)}else s.firstChild?s.insertBefore(e.dom,s.firstChild):s.appendChild(e.dom)},eachLayer:function(t,e){var i,n,r=this._zlevelList;for(n=0;n0?Rv:0),this._needsManuallyCompositing),o.__builtin__||iv("ZLevel "+s+" has been used by unkown layer "+o.id),o!==r&&(o.__used=!0,o.__startIndex!==i&&(o.__dirty=!0),o.__startIndex=i,o.__drawIndex=o.incremental?-1:i,e(i),r=o),n.__dirty&&(o.__dirty=!0,o.incremental&&o.__drawIndex<0&&(o.__drawIndex=i))}e(i),this.eachBuiltinLayer(function(t){!t.__used&&t.getElementCount()>0&&(t.__dirty=!0,t.__startIndex=t.__endIndex=t.__drawIndex=0),t.__dirty&&t.__drawIndex<0&&(t.__drawIndex=t.__startIndex)})},clear:function(){return this.eachBuiltinLayer(this._clearLayer),this},_clearLayer:function(t){t.clear()},setBackgroundColor:function(t){this._backgroundColor=t},configLayer:function(t,e){if(e){var i=this._layerConfig;i[t]?r(i[t],e,!0):i[t]=e;for(var n=0;n=0&&this._clips.splice(e,1)},removeAnimator:function(t){for(var e=t.getClips(),i=0;io;o++){var s=i[o],l=s.step(t,e);l&&(r.push(l),a.push(s))}for(var o=0;n>o;)i[o]._needsRemove?(i[o]=i[n-1],i.pop(),n--):o++;n=r.length;for(var o=0;n>o;o++)a[o].fire(r[o]);this._time=t,this.onframe(e),this.trigger("frame",e),this.stage.update&&this.stage.update()},_startLoop:function(){function t(){e._running&&(wv(t),!e._paused&&e._update())}var e=this;this._running=!0,wv(t)},start:function(){this._time=(new Date).getTime(),this._pausedTime=0,this._startLoop()},stop:function(){this._running=!1},pause:function(){this._paused||(this._pauseStart=(new Date).getTime(),this._paused=!0)},resume:function(){this._paused&&(this._pausedTime+=(new Date).getTime()-this._pauseStart,this._paused=!1)},clear:function(){this._clips=[]},isFinished:function(){return!this._clips.length},animate:function(t,e){e=e||{};var i=new Kg(t,e.loop,e.getter,e.setter);return this.addAnimator(i),i}},c(Wv,bg);var Gv=function(){this._track=[]};Gv.prototype={constructor:Gv,recognize:function(t,e,i){return this._doTrack(t,e,i),this._recognize(t)},clear:function(){return this._track.length=0,this},_doTrack:function(t,e,i){var n=t.touches;if(n){for(var r={points:[],touches:[],target:e,event:t},a=0,o=n.length;o>a;a++){var s=n[a],l=de(i,s,{});r.points.push([l.zrX,l.zrY]),r.touches.push(s)}this._track.push(r)}},_recognize:function(t){for(var e in Hv)if(Hv.hasOwnProperty(e)){var i=Hv[e](this._track,t);if(i)return i}}};var Hv={pinch:function(t,e){var i=t.length;if(i){var n=(t[i-1]||{}).points,r=(t[i-2]||{}).points||n;if(r&&r.length>1&&n&&n.length>1){var a=In(n)/In(r);!isFinite(a)&&(a=1),e.pinchScale=a;var o=Tn(n);return e.pinchX=o[0],e.pinchY=o[1],{type:"pinch",target:t[0].target,event:e}}}}},Zv=300,Xv=["click","dblclick","mousewheel","mouseout","mouseup","mousedown","mousemove","contextmenu"],Yv=["touchstart","touchend","touchmove"],jv={pointerdown:1,pointerup:1,pointermove:1,pointerout:1},qv=p(Xv,function(t){var e=t.replace("mouse","pointer");return jv[e]?e:t}),Uv={mousemove:function(t){t=pe(this.dom,t),this.trigger("mousemove",t)},mouseout:function(t){t=pe(this.dom,t);var e=t.toElement||t.relatedTarget;if(e!=this.dom)for(;e&&9!=e.nodeType;){if(e===this.dom)return;e=e.parentNode}this.trigger("mouseout",t)},touchstart:function(t){t=pe(this.dom,t),t.zrByTouch=!0,this._lastTouchMoment=new Date,An(this,t,"start"),Uv.mousemove.call(this,t),Uv.mousedown.call(this,t),Dn(this)},touchmove:function(t){t=pe(this.dom,t),t.zrByTouch=!0,An(this,t,"change"),Uv.mousemove.call(this,t),Dn(this)},touchend:function(t){t=pe(this.dom,t),t.zrByTouch=!0,An(this,t,"end"),Uv.mouseup.call(this,t),+new Date-this._lastTouchMoment=0||n&&h(n,o)<0)){var s=e.getShallow(o);null!=s&&(r[t[a][0]]=s)}}return r}},fm=dm([["lineWidth","width"],["stroke","color"],["opacity"],["shadowBlur"],["shadowOffsetX"],["shadowOffsetY"],["shadowColor"]]),pm={getLineStyle:function(t){var e=fm(this,t),i=this.getLineDash(e.lineWidth);return i&&(e.lineDash=i),e},getLineDash:function(t){null==t&&(t=1);var e=this.get("type"),i=Math.max(t,2),n=4*t;return"solid"===e||null==e?null:"dashed"===e?[n,n]:[i,i]}},gm=dm([["fill","color"],["shadowBlur"],["shadowOffsetX"],["shadowOffsetY"],["opacity"],["shadowColor"]]),vm={getAreaStyle:function(t,e){return gm(this,t,e)}},mm=Math.pow,ym=Math.sqrt,xm=1e-8,_m=1e-4,wm=ym(3),bm=1/3,Sm=W(),Mm=W(),Im=W(),Tm=Math.min,Cm=Math.max,Am=Math.sin,Dm=Math.cos,km=2*Math.PI,Pm=W(),Lm=W(),Om=W(),zm=[],Em=[],Rm={M:1,L:2,C:3,Q:4,A:5,Z:6,R:7},Bm=[],Nm=[],Fm=[],Vm=[],Wm=Math.min,Gm=Math.max,Hm=Math.cos,Zm=Math.sin,Xm=Math.sqrt,Ym=Math.abs,jm="undefined"!=typeof Float32Array,qm=function(t){this._saveData=!t,this._saveData&&(this.data=[]),this._ctx=null};qm.prototype={constructor:qm,_xi:0,_yi:0,_x0:0,_y0:0,_ux:0,_uy:0,_len:0,_lineDash:null,_dashOffset:0,_dashIdx:0,_dashSum:0,setScale:function(t,e){this._ux=Ym(1/tv/t)||0,this._uy=Ym(1/tv/e)||0},getContext:function(){return this._ctx},beginPath:function(t){return this._ctx=t,t&&t.beginPath(),t&&(this.dpr=t.dpr),this._saveData&&(this._len=0),this._lineDash&&(this._lineDash=null,this._dashOffset=0),this},moveTo:function(t,e){return this.addData(Rm.M,t,e),this._ctx&&this._ctx.moveTo(t,e),this._x0=t,this._y0=e,this._xi=t,this._yi=e,this},lineTo:function(t,e){var i=Ym(t-this._xi)>this._ux||Ym(e-this._yi)>this._uy||this._len<5;return this.addData(Rm.L,t,e),this._ctx&&i&&(this._needsDash()?this._dashedLineTo(t,e):this._ctx.lineTo(t,e)),i&&(this._xi=t,this._yi=e),this},bezierCurveTo:function(t,e,i,n,r,a){return this.addData(Rm.C,t,e,i,n,r,a),this._ctx&&(this._needsDash()?this._dashedBezierTo(t,e,i,n,r,a):this._ctx.bezierCurveTo(t,e,i,n,r,a)),this._xi=r,this._yi=a,this},quadraticCurveTo:function(t,e,i,n){return this.addData(Rm.Q,t,e,i,n),this._ctx&&(this._needsDash()?this._dashedQuadraticTo(t,e,i,n):this._ctx.quadraticCurveTo(t,e,i,n)),this._xi=i,this._yi=n,this},arc:function(t,e,i,n,r,a){return this.addData(Rm.A,t,e,i,i,n,r-n,0,a?0:1),this._ctx&&this._ctx.arc(t,e,i,n,r,a),this._xi=Hm(r)*i+t,this._yi=Zm(r)*i+e,this},arcTo:function(t,e,i,n,r){return this._ctx&&this._ctx.arcTo(t,e,i,n,r),this},rect:function(t,e,i,n){return this._ctx&&this._ctx.rect(t,e,i,n),this.addData(Rm.R,t,e,i,n),this},closePath:function(){this.addData(Rm.Z);var t=this._ctx,e=this._x0,i=this._y0;return t&&(this._needsDash()&&this._dashedLineTo(e,i),t.closePath()),this._xi=e,this._yi=i,this},fill:function(t){t&&t.fill(),this.toStatic()},stroke:function(t){t&&t.stroke(),this.toStatic()},setLineDash:function(t){if(t instanceof Array){this._lineDash=t,this._dashIdx=0;for(var e=0,i=0;ii;i++)this.data[i]=t[i];this._len=e},appendPath:function(t){t instanceof Array||(t=[t]);for(var e=t.length,i=0,n=this._len,r=0;e>r;r++)i+=t[r].len();jm&&this.data instanceof Float32Array&&(this.data=new Float32Array(n+i));for(var r=0;e>r;r++)for(var a=t[r].data,o=0;oe.length&&(this._expandData(),e=this.data);for(var i=0;ia&&(a=r+a),a%=r,f-=a*u,p-=a*c;u>0&&t>=f||0>u&&f>=t||0==u&&(c>0&&e>=p||0>c&&p>=e);)n=this._dashIdx,i=o[n],f+=u*i,p+=c*i,this._dashIdx=(n+1)%g,u>0&&l>f||0>u&&f>l||c>0&&h>p||0>c&&p>h||s[n%2?"moveTo":"lineTo"](u>=0?Wm(f,t):Gm(f,t),c>=0?Wm(p,e):Gm(p,e));u=f-t,c=p-e,this._dashOffset=-Xm(u*u+c*c)},_dashedBezierTo:function(t,e,i,n,r,a){var o,s,l,h,u,c=this._dashSum,d=this._dashOffset,f=this._lineDash,p=this._ctx,g=this._xi,v=this._yi,m=lr,y=0,x=this._dashIdx,_=f.length,w=0;for(0>d&&(d=c+d),d%=c,o=0;1>o;o+=.1)s=m(g,t,i,r,o+.1)-m(g,t,i,r,o),l=m(v,e,n,a,o+.1)-m(v,e,n,a,o),y+=Xm(s*s+l*l);for(;_>x&&(w+=f[x],!(w>d));x++);for(o=(w-d)/y;1>=o;)h=m(g,t,i,r,o),u=m(v,e,n,a,o),x%2?p.moveTo(h,u):p.lineTo(h,u),o+=f[x]/y,x=(x+1)%_;x%2!==0&&p.lineTo(r,a),s=r-h,l=a-u,this._dashOffset=-Xm(s*s+l*l)},_dashedQuadraticTo:function(t,e,i,n){var r=i,a=n;i=(i+2*t)/3,n=(n+2*e)/3,t=(this._xi+2*t)/3,e=(this._yi+2*e)/3,this._dashedBezierTo(t,e,i,n,r,a)},toStatic:function(){var t=this.data;t instanceof Array&&(t.length=this._len,jm&&(this.data=new Float32Array(t)))},getBoundingRect:function(){Bm[0]=Bm[1]=Fm[0]=Fm[1]=Number.MAX_VALUE,Nm[0]=Nm[1]=Vm[0]=Vm[1]=-Number.MAX_VALUE;for(var t=this.data,e=0,i=0,n=0,r=0,a=0;ac;){var d=s[c++];switch(1==c&&(n=s[c],r=s[c+1],e=n,i=r),d){case Rm.M:e=n=s[c++],i=r=s[c++],t.moveTo(n,r);break;case Rm.L:a=s[c++],o=s[c++],(Ym(a-n)>l||Ym(o-r)>h||c===u-1)&&(t.lineTo(a,o),n=a,r=o);break;case Rm.C:t.bezierCurveTo(s[c++],s[c++],s[c++],s[c++],s[c++],s[c++]),n=s[c-2],r=s[c-1];break;case Rm.Q:t.quadraticCurveTo(s[c++],s[c++],s[c++],s[c++]),n=s[c-2],r=s[c-1];break;case Rm.A:var f=s[c++],p=s[c++],g=s[c++],v=s[c++],m=s[c++],y=s[c++],x=s[c++],_=s[c++],w=g>v?g:v,b=g>v?1:g/v,S=g>v?v/g:1,M=Math.abs(g-v)>.001,I=m+y;M?(t.translate(f,p),t.rotate(x),t.scale(b,S),t.arc(0,0,w,m,I,1-_),t.scale(1/b,1/S),t.rotate(-x),t.translate(-f,-p)):t.arc(f,p,w,m,I,1-_),1==c&&(e=Hm(m)*g+f,i=Zm(m)*v+p),n=Hm(I)*g+f,r=Zm(I)*v+p;break;case Rm.R:e=n=s[c],i=r=s[c+1],t.rect(s[c++],s[c++],s[c++],s[c++]);break;case Rm.Z:t.closePath(),n=e,r=i}}}},qm.CMD=Rm;var Um=2*Math.PI,$m=2*Math.PI,Km=qm.CMD,Qm=2*Math.PI,Jm=1e-4,ty=[-1,-1,-1],ey=[-1,-1],iy=xv.prototype.getCanvasPattern,ny=Math.abs,ry=new qm(!0);Fr.prototype={constructor:Fr,type:"path",__dirtyPath:!0,strokeContainThreshold:5,brush:function(t,e){var i=this.style,n=this.path||ry,r=i.hasStroke(),a=i.hasFill(),o=i.fill,s=i.stroke,l=a&&!!o.colorStops,h=r&&!!s.colorStops,u=a&&!!o.image,c=r&&!!s.image;if(i.bind(t,this,e),this.setTransform(t),this.__dirty){var d;l&&(d=d||this.getBoundingRect(),this._fillGradient=i.getGradient(t,o,d)),h&&(d=d||this.getBoundingRect(),this._strokeGradient=i.getGradient(t,s,d))}l?t.fillStyle=this._fillGradient:u&&(t.fillStyle=iy.call(o,t)),h?t.strokeStyle=this._strokeGradient:c&&(t.strokeStyle=iy.call(s,t));var f=i.lineDash,p=i.lineDashOffset,g=!!t.setLineDash,v=this.getGlobalScale();if(n.setScale(v[0],v[1]),this.__dirtyPath||f&&!g&&r?(n.beginPath(t),f&&!g&&(n.setLineDash(f),n.setLineDashOffset(p)),this.buildPath(n,this.shape,!1),this.path&&(this.__dirtyPath=!1)):(t.beginPath(),this.path.rebuildPath(t)),a)if(null!=i.fillOpacity){var m=t.globalAlpha;t.globalAlpha=i.fillOpacity*i.opacity,n.fill(t),t.globalAlpha=m}else n.fill(t);if(f&&g&&(t.setLineDash(f),t.lineDashOffset=p),r)if(null!=i.strokeOpacity){var m=t.globalAlpha;t.globalAlpha=i.strokeOpacity*i.opacity,n.stroke(t),t.globalAlpha=m}else n.stroke(t);f&&g&&t.setLineDash([]),null!=i.text&&(this.restoreTransform(t),this.drawRectText(t,this.getBoundingRect()))},buildPath:function(){},createPathProxy:function(){this.path=new qm},getBoundingRect:function(){var t=this._rect,e=this.style,i=!t;if(i){var n=this.path;n||(n=this.path=new qm),this.__dirtyPath&&(n.beginPath(),this.buildPath(n,this.shape,!1)),t=n.getBoundingRect()}if(this._rect=t,e.hasStroke()){var r=this._rectWithStroke||(this._rectWithStroke=t.clone());if(this.__dirty||i){r.copy(t);var a=e.lineWidth,o=e.strokeNoScale?this.getLineScale():1;e.hasFill()||(a=Math.max(a,this.strokeContainThreshold||4)),o>1e-10&&(r.width+=a/o,r.height+=a/o,r.x-=a/o/2,r.y-=a/o/2)}return r}return t},contain:function(t,e){var i=this.transformCoordToLocal(t,e),n=this.getBoundingRect(),r=this.style;if(t=i[0],e=i[1],n.contain(t,e)){var a=this.path.data;if(r.hasStroke()){var o=r.lineWidth,s=r.strokeNoScale?this.getLineScale():1;if(s>1e-10&&(r.hasFill()||(o=Math.max(o,this.strokeContainThreshold)),Nr(a,o/s,t,e)))return!0}if(r.hasFill())return Br(a,t,e)}return!1},dirty:function(t){null==t&&(t=!0),t&&(this.__dirtyPath=t,this._rect=null),this.__dirty=this.__dirtyText=!0,this.__zr&&this.__zr.refresh(),this.__clipTarget&&this.__clipTarget.dirty()},animateShape:function(t){return this.animate("shape",t)},attrKV:function(t,e){"shape"===t?(this.setShape(e),this.__dirtyPath=!0,this._rect=null):mn.prototype.attrKV.call(this,t,e)},setShape:function(t,e){var i=this.shape;if(i){if(S(t))for(var n in t)t.hasOwnProperty(n)&&(i[n]=t[n]);else i[t]=e;this.dirty(!0)}return this},getLineScale:function(){var t=this.transform;return t&&ny(t[0]-1)>1e-10&&ny(t[3]-1)>1e-10?Math.sqrt(ny(t[0]*t[3]-t[2]*t[1])):1}},Fr.extend=function(t){var e=function(e){Fr.call(this,e),t.style&&this.style.extendFrom(t.style,!1);var i=t.shape;if(i){this.shape=this.shape||{};var n=this.shape;for(var r in i)!n.hasOwnProperty(r)&&i.hasOwnProperty(r)&&(n[r]=i[r])}t.init&&t.init.call(this,e)};u(e,Fr);for(var i in t)"style"!==i&&"shape"!==i&&(e.prototype[i]=t[i]);return e},u(Fr,mn);var ay=qm.CMD,oy=[[],[],[]],sy=Math.sqrt,ly=Math.atan2,hy=function(t,e){var i,n,r,a,o,s,l=t.data,h=ay.M,u=ay.C,c=ay.L,d=ay.R,f=ay.A,p=ay.Q;for(r=0,a=0;ro;o++){var s=oy[o];s[0]=l[r++],s[1]=l[r++],ae(s,s,e),l[a++]=s[0],l[a++]=s[1]}}},uy=Math.sqrt,cy=Math.sin,dy=Math.cos,fy=Math.PI,py=function(t){return Math.sqrt(t[0]*t[0]+t[1]*t[1])},gy=function(t,e){return(t[0]*e[0]+t[1]*e[1])/(py(t)*py(e))},vy=function(t,e){return(t[0]*e[1]=11?function(){var e,i=this.__clipPaths,n=this.style;if(i)for(var r=0;ra;a++)r+=ee(t[a-1],t[a]);var o=r/2;o=i>o?i:o;for(var a=0;o>a;a++){var s,l,h,u=a/(o-1)*(e?i:i-1),c=Math.floor(u),d=u-c,f=t[c%i];e?(s=t[(c-1+i)%i],l=t[(c+1)%i],h=t[(c+2)%i]):(s=t[0===c?c:c-1],l=t[c>i-2?i-1:c+1],h=t[c>i-3?i-1:c+2]);var p=d*d,g=d*p;n.push([Yr(s[0],f[0],l[0],h[0],d,p,g),Yr(s[1],f[1],l[1],h[1],d,p,g)])}return n},Ty=function(t,e,i,n){var r,a,o,s,l=[],h=[],u=[],c=[];if(n){o=[1/0,1/0],s=[-1/0,-1/0];for(var d=0,f=t.length;f>d;d++)oe(o,o,t[d]),se(s,s,t[d]);oe(o,o,n[0]),se(s,s,n[1])}for(var d=0,f=t.length;f>d;d++){var p=t[d];if(i)r=t[d?d-1:f-1],a=t[(d+1)%f];else{if(0===d||d===f-1){l.push(H(t[d]));continue}r=t[d-1],a=t[d+1]}j(h,a,r),J(h,h,e);var g=ee(p,r),v=ee(p,a),m=g+v;0!==m&&(g/=m,v/=m),J(u,h,-g),J(c,h,v);var y=X([],p,u),x=X([],p,c);n&&(se(y,y,o),oe(y,y,s),se(x,x,o),oe(x,x,s)),l.push(y),l.push(x)}return i&&l.push(l.shift()),l},Cy=Fr.extend({type:"polygon",shape:{points:null,smooth:!1,smoothConstraint:null},buildPath:function(t,e){jr(t,e,!0)}}),Ay=Fr.extend({type:"polyline",shape:{points:null,smooth:!1,smoothConstraint:null},style:{stroke:"#000",fill:null},buildPath:function(t,e){jr(t,e,!1)}}),Dy=Fr.extend({type:"rect",shape:{r:0,x:0,y:0,width:0,height:0},buildPath:function(t,e){var i=e.x,n=e.y,r=e.width,a=e.height;e.r?Ki(t,e):t.rect(i,n,r,a),t.closePath()}}),ky=Fr.extend({type:"line",shape:{x1:0,y1:0,x2:0,y2:0,percent:1},style:{stroke:"#000",fill:null},buildPath:function(t,e){var i=e.x1,n=e.y1,r=e.x2,a=e.y2,o=e.percent;0!==o&&(t.moveTo(i,n),1>o&&(r=i*(1-o)+r*o,a=n*(1-o)+a*o),t.lineTo(r,a))},pointAt:function(t){var e=this.shape;return[e.x1*(1-t)+e.x2*t,e.y1*(1-t)+e.y2*t]}}),Py=[],Ly=Fr.extend({type:"bezier-curve",shape:{x1:0,y1:0,x2:0,y2:0,cpx1:0,cpy1:0,percent:1},style:{stroke:"#000",fill:null},buildPath:function(t,e){var i=e.x1,n=e.y1,r=e.x2,a=e.y2,o=e.cpx1,s=e.cpy1,l=e.cpx2,h=e.cpy2,u=e.percent;0!==u&&(t.moveTo(i,n),null==l||null==h?(1>u&&(yr(i,o,r,u,Py),o=Py[1],r=Py[2],yr(n,s,a,u,Py),s=Py[1],a=Py[2]),t.quadraticCurveTo(o,s,r,a)):(1>u&&(dr(i,o,l,r,u,Py),o=Py[1],l=Py[2],r=Py[3],dr(n,s,h,a,u,Py),s=Py[1],h=Py[2],a=Py[3]),t.bezierCurveTo(o,s,l,h,r,a)))},pointAt:function(t){return qr(this.shape,t,!1)},tangentAt:function(t){var e=qr(this.shape,t,!0);return te(e,e)}}),Oy=Fr.extend({type:"arc",shape:{cx:0,cy:0,r:0,startAngle:0,endAngle:2*Math.PI,clockwise:!0},style:{stroke:"#000",fill:null},buildPath:function(t,e){var i=e.cx,n=e.cy,r=Math.max(e.r,0),a=e.startAngle,o=e.endAngle,s=e.clockwise,l=Math.cos(a),h=Math.sin(a);t.moveTo(l*r+i,h*r+n),t.arc(i,n,r,a,o,!s)}}),zy=Fr.extend({type:"compound",shape:{paths:null},_updatePathDirty:function(){for(var t=this.__dirtyPath,e=this.shape.paths,i=0;i"'])/g,ox={"&":"&","<":"<",">":">",'"':""","'":"'"},sx=["a","b","c","d","e","f","g"],lx=function(t,e){return"{"+t+(null==e?"":e)+"}"},hx=Wi,ux=Ei,cx=(Object.freeze||Object)({addCommas:co,toCamelCase:fo,normalizeCssArray:rx,encodeHTML:po,formatTpl:go,formatTplSimple:vo,getTooltipMarker:mo,formatTime:xo,capitalFirst:_o,truncateText:hx,getTextRect:ux}),dx=f,fx=["left","right","top","bottom","width","height"],px=[["width","left","right"],["height","top","bottom"]],gx=wo,vx=(x(wo,"vertical"),x(wo,"horizontal"),{getBoxLayoutParams:function(){return{left:this.get("left"),top:this.get("top"),right:this.get("right"),bottom:this.get("bottom"),width:this.get("width"),height:this.get("height")}}}),mx=jn(),yx=Wa.extend({type:"component",id:"",name:"",mainType:"",subType:"",componentIndex:0,defaultOption:null,ecModel:null,dependentModels:[],uid:null,layoutMode:null,$constructor:function(t,e,i,n){Wa.call(this,t,e,i,n),this.uid=Za("ec_cpt_model")},init:function(t,e,i){this.mergeDefaultAndTheme(t,i)},mergeDefaultAndTheme:function(t,e){var i=this.layoutMode,n=i?Mo(t):{},a=e.getTheme();r(t,a.get(this.mainType)),r(t,this.getDefaultOption()),i&&So(t,n,i)},mergeOption:function(t){r(this.option,t,!0);var e=this.layoutMode;e&&So(this.option,t,e)},optionUpdated:function(){},getDefaultOption:function(){var t=mx(this);if(!t.defaultOption){for(var e=[],i=this.constructor;i;){var n=i.prototype.defaultOption;n&&e.push(n),i=i.superClass}for(var a={},o=e.length-1;o>=0;o--)a=r(a,e[o],!0);t.defaultOption=a}return t.defaultOption},getReferringComponents:function(t){return this.ecModel.queryComponents({mainType:t,index:this.get(t+"Index",!0),id:this.get(t+"Id",!0)})}});ar(yx,{registerWhenExtend:!0}),Xa(yx),Ya(yx,To),c(yx,vx);var xx="";"undefined"!=typeof navigator&&(xx=navigator.platform||"");var _x={color:["#c23531","#2f4554","#61a0a8","#d48265","#91c7ae","#749f83","#ca8622","#bda29a","#6e7074","#546570","#c4ccd3"],gradientColor:["#f6efa6","#d88273","#bf444c"],textStyle:{fontFamily:xx.match(/^Win/)?"Microsoft YaHei":"sans-serif",fontSize:12,fontStyle:"normal",fontWeight:"normal"},blendMode:null,animation:"auto",animationDuration:1e3,animationDurationUpdate:300,animationEasing:"exponentialOut",animationEasingUpdate:"cubicOut",animationThreshold:2e3,progressiveThreshold:3e3,progressive:400,hoverLayerThreshold:3e3,useUTC:!1},bx=jn(),Sx={clearColorPalette:function(){bx(this).colorIdx=0,bx(this).colorNameMap={}},getColorFromPalette:function(t,e,i){e=e||this;var n=bx(e),r=n.colorIdx||0,a=n.colorNameMap=n.colorNameMap||{};if(a.hasOwnProperty(t))return a[t];var o=Nn(this.get("color",!0)),s=this.get("colorLayer",!0),l=null!=i&&s?Co(s,i):o;if(l=l||o,l&&l.length){var h=l[r];return t&&(a[t]=h),n.colorIdx=(r+1)%l.length,h}}},Mx={cartesian2d:function(t,e,i,n){var r=t.getReferringComponents("xAxis")[0],a=t.getReferringComponents("yAxis")[0];e.coordSysDims=["x","y"],i.set("x",r),i.set("y",a),Do(r)&&(n.set("x",r),e.firstCategoryDimIndex=0),Do(a)&&(n.set("y",a),e.firstCategoryDimIndex=1)},singleAxis:function(t,e,i,n){var r=t.getReferringComponents("singleAxis")[0];e.coordSysDims=["single"],i.set("single",r),Do(r)&&(n.set("single",r),e.firstCategoryDimIndex=0)},polar:function(t,e,i,n){var r=t.getReferringComponents("polar")[0],a=r.findAxisModel("radiusAxis"),o=r.findAxisModel("angleAxis");e.coordSysDims=["radius","angle"],i.set("radius",a),i.set("angle",o),Do(a)&&(n.set("radius",a),e.firstCategoryDimIndex=0),Do(o)&&(n.set("angle",o),e.firstCategoryDimIndex=1)},geo:function(t,e){e.coordSysDims=["lng","lat"]},parallel:function(t,e,i,n){var r=t.ecModel,a=r.getComponent("parallel",t.get("parallelIndex")),o=e.coordSysDims=a.dimensions.slice();f(a.parallelAxisIndex,function(t,a){var s=r.getComponent("parallelAxis",t),l=o[a];i.set(l,s),Do(s)&&null==e.firstCategoryDimIndex&&(n.set(l,s),e.firstCategoryDimIndex=a)})}},Ix="original",Tx="arrayRows",Cx="objectRows",Ax="keyedColumns",Dx="unknown",kx="typedArray",Px="column",Lx="row";ko.seriesDataToSource=function(t){return new ko({data:t,sourceFormat:I(t)?kx:Ix,fromDataset:!1})},ir(ko);var Ox=jn(),zx="\x00_ec_inner",Ex=Wa.extend({init:function(t,e,i,n){i=i||{},this.option=null,this._theme=new Wa(i),this._optionManager=n},setOption:function(t,e){O(!(zx in t),"please use chart.getOption()"),this._optionManager.setOption(t,e),this.resetOption(null)},resetOption:function(t){var e=!1,i=this._optionManager;if(!t||"recreate"===t){var n=i.mountOption("recreate"===t);this.option&&"recreate"!==t?(this.restoreData(),this.mergeOption(n)):Xo.call(this,n),e=!0}if(("timeline"===t||"media"===t)&&this.restoreData(),!t||"recreate"===t||"timeline"===t){var r=i.getTimelineOption(this);r&&(this.mergeOption(r),e=!0)}if(!t||"recreate"===t||"media"===t){var a=i.getMediaOption(this,this._api);a.length&&f(a,function(t){this.mergeOption(t,e=!0)},this)}return e},mergeOption:function(t){function e(e,n){var r=Nn(t[e]),s=Gn(a.get(e),r);Hn(s),f(s,function(t){var i=t.option;S(i)&&(t.keyInfo.mainType=e,t.keyInfo.subType=jo(e,i,t.exist))});var l=Yo(a,n);i[e]=[],a.set(e,[]),f(s,function(t,n){var r=t.exist,s=t.option;if(O(S(s)||r,"Empty component definition"),s){var h=yx.getClass(e,t.keyInfo.subType,!0);if(r&&r instanceof h)r.name=t.keyInfo.name,r.mergeOption(s,this),r.optionUpdated(s,!1);else{var u=o({dependentModels:l,componentIndex:n},t.keyInfo);r=new h(s,this,this,u),o(r,u),r.init(s,this,this,u),r.optionUpdated(null,!0)}}else r.mergeOption({},this),r.optionUpdated({},!1);a.get(e)[n]=r,i[e][n]=r.option},this),"series"===e&&qo(this,a.get("series"))}var i=this.option,a=this._componentsMap,s=[];Oo(this),f(t,function(t,e){null!=t&&(yx.hasClass(e)?e&&s.push(e):i[e]=null==i[e]?n(t):r(i[e],t,!0))}),yx.topologicalTravel(s,yx.getAllClassMainTypes(),e,this),this._seriesIndicesMap=N(this._seriesIndices=this._seriesIndices||[])},getOption:function(){var t=n(this.option);return f(t,function(e,i){if(yx.hasClass(i)){for(var e=Nn(e),n=e.length-1;n>=0;n--)Xn(e[n])&&e.splice(n,1);t[i]=e}}),delete t[zx],t},getTheme:function(){return this._theme},getComponent:function(t,e){var i=this._componentsMap.get(t);return i?i[e||0]:void 0},queryComponents:function(t){var e=t.mainType;if(!e)return[];var i=t.index,n=t.id,r=t.name,a=this._componentsMap.get(e);if(!a||!a.length)return[];var o;if(null!=i)_(i)||(i=[i]),o=v(p(i,function(t){return a[t]}),function(t){return!!t});else if(null!=n){var s=_(n);o=v(a,function(t){return s&&h(n,t.id)>=0||!s&&t.id===n})}else if(null!=r){var l=_(r);o=v(a,function(t){return l&&h(r,t.name)>=0||!l&&t.name===r})}else o=a.slice();return Uo(o,t)},findComponents:function(t){function e(t){var e=r+"Index",i=r+"Id",n=r+"Name";return!t||null==t[e]&&null==t[i]&&null==t[n]?null:{mainType:r,index:t[e],id:t[i],name:t[n]}}function i(e){return t.filter?v(e,t.filter):e}var n=t.query,r=t.mainType,a=e(n),o=a?this.queryComponents(a):this._componentsMap.get(r);return i(Uo(o,t))},eachComponent:function(t,e,i){var n=this._componentsMap;if("function"==typeof t)i=e,e=t,n.each(function(t,n){f(t,function(t,r){e.call(i,n,t,r)})});else if(b(t))f(n.get(t),e,i);else if(S(t)){var r=this.findComponents(t);f(r,e,i)}},getSeriesByName:function(t){var e=this._componentsMap.get("series");return v(e,function(e){return e.name===t})},getSeriesByIndex:function(t){return this._componentsMap.get("series")[t]},getSeriesByType:function(t){var e=this._componentsMap.get("series");return v(e,function(e){return e.subType===t})},getSeries:function(){return this._componentsMap.get("series").slice()},getSeriesCount:function(){return this._componentsMap.get("series").length},eachSeries:function(t,e){f(this._seriesIndices,function(i){var n=this._componentsMap.get("series")[i];t.call(e,n,i)},this)},eachRawSeries:function(t,e){f(this._componentsMap.get("series"),t,e)},eachSeriesByType:function(t,e,i){f(this._seriesIndices,function(n){var r=this._componentsMap.get("series")[n];r.subType===t&&e.call(i,r,n)},this)},eachRawSeriesByType:function(t,e,i){return f(this.getSeriesByType(t),e,i)},isSeriesFiltered:function(t){return null==this._seriesIndicesMap.get(t.componentIndex)},getCurrentSeriesIndices:function(){return(this._seriesIndices||[]).slice()},filterSeries:function(t,e){var i=v(this._componentsMap.get("series"),t,e);qo(this,i)},restoreData:function(t){var e=this._componentsMap;qo(this,e.get("series"));var i=[];e.each(function(t,e){i.push(e)}),yx.topologicalTravel(i,yx.getAllClassMainTypes(),function(i){f(e.get(i),function(e){("series"!==i||!Ho(e,t))&&e.restoreData()})})}});c(Ex,Sx);var Rx=["getDom","getZr","getWidth","getHeight","getDevicePixelRatio","dispatchAction","isDisposed","on","off","getDataURL","getConnectedDataURL","getModel","getOption","getViewOfComponentModel","getViewOfSeriesModel"],Bx={};Ko.prototype={constructor:Ko,create:function(t,e){var i=[];f(Bx,function(n){var r=n.create(t,e);i=i.concat(r||[])}),this._coordinateSystems=i},update:function(t,e){f(this._coordinateSystems,function(i){i.update&&i.update(t,e)})},getCoordinateSystems:function(){return this._coordinateSystems.slice()}},Ko.register=function(t,e){Bx[t]=e},Ko.get=function(t){return Bx[t]};var Nx=f,Fx=n,Vx=p,Wx=r,Gx=/^(min|max)?(.+)$/;Qo.prototype={constructor:Qo,setOption:function(t,e){t&&f(Nn(t.series),function(t){t&&t.data&&I(t.data)&&E(t.data)}),t=Fx(t,!0);var i=this._optionBackup,n=Jo.call(this,t,e,!i);this._newBaseOption=n.baseOption,i?(ns(i.baseOption,n.baseOption),n.timelineOptions.length&&(i.timelineOptions=n.timelineOptions),n.mediaList.length&&(i.mediaList=n.mediaList),n.mediaDefault&&(i.mediaDefault=n.mediaDefault)):this._optionBackup=n},mountOption:function(t){var e=this._optionBackup;return this._timelineOptions=Vx(e.timelineOptions,Fx),this._mediaList=Vx(e.mediaList,Fx),this._mediaDefault=Fx(e.mediaDefault),this._currentMediaIndices=[],Fx(t?e.baseOption:this._newBaseOption)},getTimelineOption:function(t){var e,i=this._timelineOptions;if(i.length){var n=t.getComponent("timeline");n&&(e=Fx(i[n.getCurrentIndex()],!0))}return e},getMediaOption:function(){var t=this._api.getWidth(),e=this._api.getHeight(),i=this._mediaList,n=this._mediaDefault,r=[],a=[];if(!i.length&&!n)return a;for(var o=0,s=i.length;s>o;o++)ts(i[o].query,t,e)&&r.push(o);return!r.length&&n&&(r=[-1]),r.length&&!is(r,this._currentMediaIndices)&&(a=Vx(r,function(t){return Fx(-1===t?n.option:i[t].option)})),this._currentMediaIndices=r,a}};var Hx=f,Zx=S,Xx=["areaStyle","lineStyle","nodeStyle","linkStyle","chordStyle","label","labelLine"],Yx=function(t,e){Hx(us(t.series),function(t){Zx(t)&&hs(t)});var i=["xAxis","yAxis","radiusAxis","angleAxis","singleAxis","parallelAxis","radar"];e&&i.push("valueAxis","categoryAxis","logAxis","timeAxis"),Hx(i,function(e){Hx(us(t[e]),function(t){t&&(ss(t,"axisLabel"),ss(t.axisPointer,"label"))})}),Hx(us(t.parallel),function(t){var e=t&&t.parallelAxisDefault;ss(e,"axisLabel"),ss(e&&e.axisPointer,"label")}),Hx(us(t.calendar),function(t){as(t,"itemStyle"),ss(t,"dayLabel"),ss(t,"monthLabel"),ss(t,"yearLabel")}),Hx(us(t.radar),function(t){ss(t,"name")}),Hx(us(t.geo),function(t){Zx(t)&&(ls(t),Hx(us(t.regions),function(t){ls(t)}))}),Hx(us(t.timeline),function(t){ls(t),as(t,"label"),as(t,"itemStyle"),as(t,"controlStyle",!0);var e=t.data;_(e)&&f(e,function(t){S(t)&&(as(t,"label"),as(t,"itemStyle"))})}),Hx(us(t.toolbox),function(t){as(t,"iconStyle"),Hx(t.feature,function(t){as(t,"iconStyle")})}),ss(cs(t.axisPointer),"label"),ss(cs(t.tooltip).axisPointer,"label")},jx=[["x","left"],["y","top"],["x2","right"],["y2","bottom"]],qx=["grid","geo","parallel","legend","toolbox","title","visualMap","dataZoom","timeline"],Ux=function(t,e){Yx(t,e),t.series=Nn(t.series),f(t.series,function(t){if(S(t)){var e=t.type;if(("pie"===e||"gauge"===e)&&null!=t.clockWise&&(t.clockwise=t.clockWise),"gauge"===e){var i=ds(t,"pointer.color");null!=i&&fs(t,"itemStyle.normal.color",i)}ps(t)}}),t.dataRange&&(t.visualMap=t.dataRange),f(qx,function(e){var i=t[e];i&&(_(i)||(i=[i]),f(i,function(t){ps(t)}))})},$x=function(t){var e=N();t.eachSeries(function(t){var i=t.get("stack");if(i){var n=e.get(i)||e.set(i,[]),r=t.getData(),a={stackResultDimension:r.getCalculationInfo("stackResultDimension"),stackedOverDimension:r.getCalculationInfo("stackedOverDimension"),stackedDimension:r.getCalculationInfo("stackedDimension"),stackedByDimension:r.getCalculationInfo("stackedByDimension"),isStackedByIndex:r.getCalculationInfo("isStackedByIndex"),data:r,seriesModel:t};if(!a.stackedDimension||!a.isStackedByIndex&&!a.stackedByDimension)return;n.length&&r.setCalculationInfo("stackedOnSeries",n[n.length-1].seriesModel),n.push(a)}}),e.each(gs)},Kx=vs.prototype;Kx.pure=!1,Kx.persistent=!0,Kx.getSource=function(){return this._source};var Qx={arrayRows_column:{pure:!0,count:function(){return Math.max(0,this._data.length-this._source.startIndex)},getItem:function(t){return this._data[t+this._source.startIndex]},appendData:xs},arrayRows_row:{pure:!0,count:function(){var t=this._data[0];return t?Math.max(0,t.length-this._source.startIndex):0},getItem:function(t){t+=this._source.startIndex;for(var e=[],i=this._data,n=0;n=1)&&(t=1),t}var i=this._upstream,n=t&&t.skip;if(this._dirty&&i){var r=this.context;r.data=r.outputData=i.context.outputData}this.__pipeline&&(this.__pipeline.currentTask=this);var a;this._plan&&!n&&(a=this._plan(this.context));var o=e(this._modBy),s=this._modDataCount||0,l=e(t&&t.modBy),h=t&&t.modDataCount||0;(o!==l||s!==h)&&(a="reset");var u;(this._dirty||"reset"===a)&&(this._dirty=!1,u=As(this,n)),this._modBy=l,this._modDataCount=h;var c=t&&t.step;if(this._dueEnd=i?i._outputDueEnd:this._count?this._count(this.context):1/0,this._progress){var d=this._dueIndex,f=Math.min(null!=c?this._dueIndex+c:1/0,this._dueEnd);if(!n&&(u||f>d)){var p=this._progress;if(_(p))for(var g=0;gn?n++:null}function e(){var t=n%o*r+Math.ceil(n/o),e=n>=i?null:a>t?t:n;return n++,e}var i,n,r,a,o,s={reset:function(l,h,u,c){n=l,i=h,r=u,a=c,o=Math.ceil(a/r),s.next=r>1&&a>0?e:t}};return s}();n_.dirty=function(){this._dirty=!0,this._onDirty&&this._onDirty(this.context)},n_.unfinished=function(){return this._progress&&this._dueIndex":"",v=p+s.join(p||", ");return{renderMode:n,content:v,style:h}}function a(t){return{renderMode:n,content:po(co(t)),style:h}}var o=this;n=n||"html";var s="html"===n?"
    ":"\n",l="richText"===n,h={},u=0,c=this.getData(),d=c.mapDimension("defaultedTooltip",!0),p=d.length,v=this.getRawValue(t),m=_(v),y=c.getItemVisual(t,"color");S(y)&&y.colorStops&&(y=(y.colorStops[0]||{}).color),y=y||"transparent";var x=p>1||m&&!p?r(v):a(p?Ss(c,t,d[0]):m?v[0]:v),w=x.content,b=o.seriesIndex+"at"+u,M=mo({color:y,type:"item",renderMode:n,markerId:b});h[b]=y,++u;var I=c.getName(t),T=this.name;Zn(this)||(T=""),T=T?po(T)+(e?": ":s):"";var C="string"==typeof M?M:M.content,A=e?C+T+w:T+C+(I?po(I)+": "+w:w);return{html:A,markers:h}},isAnimationEnabled:function(){if(tg.node)return!1;var t=this.getShallow("animation");return t&&this.getData().count()>this.getShallow("animationThreshold")&&(t=!1),t},restoreData:function(){this.dataTask.dirty()},getColorFromPalette:function(t,e,i){var n=this.ecModel,r=Sx.getColorFromPalette.call(this,t,e,i);return r||(r=n.getColorFromPalette(t,e,i)),r},coordDimToDataDim:function(t){return this.getRawData().mapDimension(t,!0)},getProgressive:function(){return this.get("progressive")},getProgressiveThreshold:function(){return this.get("progressiveThreshold")},getAxisTooltipData:null,getTooltipPosition:null,pipeTask:null,preventIncremental:null,pipelineContext:null});c(o_,i_),c(o_,Sx);var s_=function(){this.group=new lv,this.uid=Za("viewComponent")};s_.prototype={constructor:s_,init:function(){},render:function(){},dispose:function(){},filterForExposedEvent:null};var l_=s_.prototype;l_.updateView=l_.updateLayout=l_.updateVisual=function(){},er(s_),ar(s_,{registerWhenExtend:!0});var h_=function(){var t=jn();return function(e){var i=t(e),n=e.pipelineContext,r=i.large,a=i.progressiveRender,o=i.large=n.large,s=i.progressiveRender=n.progressiveRender;return!!(r^o||a^s)&&"reset"}},u_=jn(),c_=h_();Bs.prototype={type:"chart",init:function(){},render:function(){},highlight:function(t,e,i,n){Fs(t.getData(),n,"emphasis")},downplay:function(t,e,i,n){Fs(t.getData(),n,"normal")},remove:function(){this.group.removeAll()},dispose:function(){},incrementalPrepareRender:null,incrementalRender:null,updateTransform:null,filterForExposedEvent:null};var d_=Bs.prototype;d_.updateView=d_.updateLayout=d_.updateVisual=function(t,e,i,n){this.render(t,e,i,n)},er(Bs,["dispose"]),ar(Bs,{registerWhenExtend:!0}),Bs.markUpdateMethod=function(t,e){u_(t).updateMethod=e +};var f_={incrementalPrepareRender:{progress:function(t,e){e.view.incrementalRender(t,e.model,e.ecModel,e.api,e.payload)}},render:{forceFirstProgress:!0,progress:function(t,e){e.view.render(e.model,e.ecModel,e.api,e.payload)}}},p_="\x00__throttleOriginMethod",g_="\x00__throttleRate",v_="\x00__throttleType",m_={createOnAllSeries:!0,performRawSeries:!0,reset:function(t,e){var i=t.getData(),n=(t.visualColorAccessPath||"itemStyle.color").split("."),r=t.get(n)||t.getColorFromPalette(t.name,null,e.getSeriesCount());if(i.setVisual("color",r),!e.isSeriesFiltered(t)){"function"!=typeof r||r instanceof Ey||i.each(function(e){i.setItemVisual(e,"color",r(t.getDataParams(e)))});var a=function(t,e){var i=t.getItemModel(e),r=i.get(n,!0);null!=r&&t.setItemVisual(e,"color",r)};return{dataEach:i.hasItemOption?a:null}}}},y_={toolbox:{brush:{title:{rect:"矩形选择",polygon:"圈选",lineX:"横向选择",lineY:"纵向选择",keep:"保持选择",clear:"清除选择"}},dataView:{title:"数据视图",lang:["数据视图","关闭","刷新"]},dataZoom:{title:{zoom:"区域缩放",back:"区域缩放还原"}},magicType:{title:{line:"切换为折线图",bar:"切换为柱状图",stack:"切换为堆叠",tiled:"切换为平铺"}},restore:{title:"还原"},saveAsImage:{title:"保存为图片",lang:["右键另存为图片"]}},series:{typeNames:{pie:"饼图",bar:"柱状图",line:"折线图",scatter:"散点图",effectScatter:"涟漪散点图",radar:"雷达图",tree:"树图",treemap:"矩形树图",boxplot:"箱型图",candlestick:"K线图",k:"K线图",heatmap:"热力图",map:"地图",parallel:"平行坐标图",lines:"线图",graph:"关系图",sankey:"桑基图",funnel:"漏斗图",gauge:"仪表盘图",pictorialBar:"象形柱图",themeRiver:"主题河流图",sunburst:"旭日图"}},aria:{general:{withTitle:"这是一个关于“{title}”的图表。",withoutTitle:"这是一个图表,"},series:{single:{prefix:"",withName:"图表类型是{seriesType},表示{seriesName}。",withoutName:"图表类型是{seriesType}。"},multiple:{prefix:"它由{seriesCount}个图表系列组成。",withName:"第{seriesId}个系列是一个表示{seriesName}的{seriesType},",withoutName:"第{seriesId}个系列是一个{seriesType},",separator:{middle:";",end:"。"}}},data:{allData:"其数据是——",partialData:"其中,前{displayCnt}项是——",withName:"{name}的数据是{value}",withoutName:"{value}",separator:{middle:",",end:""}}}},x_=function(t,e){function i(t,e){if("string"!=typeof t)return t;var i=t;return f(e,function(t,e){i=i.replace(new RegExp("\\{\\s*"+e+"\\s*\\}","g"),t)}),i}function n(t){var e=o.get(t);if(null==e){for(var i=t.split("."),n=y_.aria,r=0;rs)){var d=r();l=d?i(n("general.withTitle"),{title:d}):n("general.withoutTitle");var p=[],g=s>1?"series.multiple.prefix":"series.single.prefix";l+=i(n(g),{seriesCount:s}),e.eachSeries(function(t,e){if(c>e){var r,o=t.get("name"),l="series."+(s>1?"multiple":"single")+".";r=n(o?l+"withName":l+"withoutName"),r=i(r,{seriesId:t.seriesIndex,seriesName:t.get("name"),seriesType:a(t.subType)});var u=t.getData();window.data=u,r+=u.count()>h?i(n("data.partialData"),{displayCnt:h}):n("data.allData");for(var d=[],f=0;ff){var g=u.getName(f),v=Ss(u,f);d.push(i(n(g?"data.withName":"data.withoutName"),{name:g,value:v}))}r+=d.join(n("data.separator.middle"))+n("data.separator.end"),p.push(r)}}),l+=p.join(n("series.multiple.separator.middle"))+n("series.multiple.separator.end"),t.setAttribute("aria-label",l)}}},__=Math.PI,w_=function(t,e){e=e||{},s(e,{text:"loading",color:"#c23531",textColor:"#000",maskColor:"rgba(255, 255, 255, 0.8)",zlevel:0});var i=new Dy({style:{fill:e.maskColor},zlevel:e.zlevel,z:1e4}),n=new Oy({shape:{startAngle:-__/2,endAngle:-__/2+.1,r:10},style:{stroke:e.color,lineCap:"round",lineWidth:5},zlevel:e.zlevel,z:10001}),r=new Dy({style:{fill:"none",text:e.text,textPosition:"right",textDistance:10,textFill:e.textColor},zlevel:e.zlevel,z:10001});n.animateShape(!0).when(1e3,{endAngle:3*__/2}).start("circularInOut"),n.animateShape(!0).when(1e3,{startAngle:3*__/2}).delay(300).start("circularInOut");var a=new lv;return a.add(n),a.add(r),a.add(i),a.resize=function(){var e=t.getWidth()/2,a=t.getHeight()/2;n.setShape({cx:e,cy:a});var o=n.shape.r;r.setShape({x:e-o,y:a-o,width:2*o,height:2*o}),i.setShape({x:0,y:0,width:t.getWidth(),height:t.getHeight()})},a.resize(),a},b_=Xs.prototype;b_.restoreData=function(t,e){t.restoreData(e),this._stageTaskMap.each(function(t){var e=t.overallTask;e&&e.dirty()})},b_.getPerformArgs=function(t,e){if(t.__pipeline){var i=this._pipelineMap.get(t.__pipeline.id),n=i.context,r=!e&&i.progressiveEnabled&&(!n||n.progressiveRender)&&t.__idxInPipeline>i.blockIndex,a=r?i.step:null,o=n&&n.modDataCount,s=null!=o?Math.ceil(o/a):null;return{step:a,modBy:s,modDataCount:o}}},b_.getPipeline=function(t){return this._pipelineMap.get(t)},b_.updateStreamModes=function(t,e){var i=this._pipelineMap.get(t.uid),n=t.getData(),r=n.count(),a=i.progressiveEnabled&&e.incrementalPrepareRender&&r>=i.threshold,o=t.get("large")&&r>=t.get("largeThreshold"),s="mod"===t.get("progressiveChunkMode")?r:null;t.pipelineContext=i.context={progressiveRender:a,modDataCount:s,large:o}},b_.restorePipelines=function(t){var e=this,i=e._pipelineMap=N();t.eachSeries(function(t){var n=t.getProgressive(),r=t.uid;i.set(r,{id:r,head:null,tail:null,threshold:t.getProgressiveThreshold(),progressiveEnabled:n&&!(t.preventIncremental&&t.preventIncremental()),blockIndex:-1,step:Math.round(n||700),count:0}),nl(e,t,t.dataTask)})},b_.prepareStageTasks=function(){var t=this._stageTaskMap,e=this.ecInstance.getModel(),i=this.api;f(this._allHandlers,function(n){var r=t.get(n.uid)||t.set(n.uid,[]);n.reset&&js(this,n,r,e,i),n.overallReset&&qs(this,n,r,e,i)},this)},b_.prepareView=function(t,e,i,n){var r=t.renderTask,a=r.context;a.model=e,a.ecModel=i,a.api=n,r.__block=!t.incrementalPrepareRender,nl(this,e,r)},b_.performDataProcessorTasks=function(t,e){Ys(this,this._dataProcessorHandlers,t,e,{block:!0})},b_.performVisualTasks=function(t,e,i){Ys(this,this._visualHandlers,t,e,i)},b_.performSeriesTasks=function(t){var e;t.eachSeries(function(t){e|=t.dataTask.perform()}),this.unfinished|=e},b_.plan=function(){this._pipelineMap.each(function(t){var e=t.tail;do{if(e.__block){t.blockIndex=e.__idxInPipeline;break}e=e.getUpstream()}while(e)})};var S_=b_.updatePayload=function(t,e){"remain"!==e&&(t.context.payload=e)},M_=el(0);Xs.wrapStageHandler=function(t,e){return w(t)&&(t={overallReset:t,seriesType:rl(t)}),t.uid=Za("stageHandler"),e&&(t.visualType=e),t};var I_,T_={},C_={};al(T_,Ex),al(C_,$o),T_.eachSeriesByType=T_.eachRawSeriesByType=function(t){I_=t},T_.eachComponent=function(t){"series"===t.mainType&&t.subType&&(I_=t.subType)};var A_=["#37A2DA","#32C5E9","#67E0E3","#9FE6B8","#FFDB5C","#ff9f7f","#fb7293","#E062AE","#E690D1","#e7bcf3","#9d96f5","#8378EA","#96BFFF"],D_={color:A_,colorLayer:[["#37A2DA","#ffd85c","#fd7b5f"],["#37A2DA","#67E0E3","#FFDB5C","#ff9f7f","#E062AE","#9d96f5"],["#37A2DA","#32C5E9","#9FE6B8","#FFDB5C","#ff9f7f","#fb7293","#e7bcf3","#8378EA","#96BFFF"],A_]},k_="#eee",P_=function(){return{axisLine:{lineStyle:{color:k_}},axisTick:{lineStyle:{color:k_}},axisLabel:{textStyle:{color:k_}},splitLine:{lineStyle:{type:"dashed",color:"#aaa"}},splitArea:{areaStyle:{color:k_}}}},L_=["#dd6b66","#759aa0","#e69d87","#8dc1a9","#ea7e53","#eedd78","#73a373","#73b9bc","#7289ab","#91ca8c","#f49f42"],O_={color:L_,backgroundColor:"#333",tooltip:{axisPointer:{lineStyle:{color:k_},crossStyle:{color:k_}}},legend:{textStyle:{color:k_}},textStyle:{color:k_},title:{textStyle:{color:k_}},toolbox:{iconStyle:{normal:{borderColor:k_}}},dataZoom:{textStyle:{color:k_}},visualMap:{textStyle:{color:k_}},timeline:{lineStyle:{color:k_},itemStyle:{normal:{color:L_[1]}},label:{normal:{textStyle:{color:k_}}},controlStyle:{normal:{color:k_,borderColor:k_}}},timeAxis:P_(),logAxis:P_(),valueAxis:P_(),categoryAxis:P_(),line:{symbol:"circle"},graph:{color:L_},gauge:{title:{textStyle:{color:k_}}},candlestick:{itemStyle:{normal:{color:"#FD1050",color0:"#0CF49B",borderColor:"#FD1050",borderColor0:"#0CF49B"}}}};O_.categoryAxis.splitLine.show=!1,yx.extend({type:"dataset",defaultOption:{seriesLayoutBy:Px,sourceHeader:null,dimensions:null,source:null},optionUpdated:function(){Po(this)}}),s_.extend({type:"dataset"});var z_=Fr.extend({type:"ellipse",shape:{cx:0,cy:0,rx:0,ry:0},buildPath:function(t,e){var i=.5522848,n=e.cx,r=e.cy,a=e.rx,o=e.ry,s=a*i,l=o*i;t.moveTo(n-a,r),t.bezierCurveTo(n-a,r-l,n-s,r-o,n,r-o),t.bezierCurveTo(n+s,r-o,n+a,r-l,n+a,r),t.bezierCurveTo(n+a,r+l,n+s,r+o,n,r+o),t.bezierCurveTo(n-s,r+o,n-a,r+l,n-a,r),t.closePath()}}),E_=/[\s,]+/;sl.prototype.parse=function(t,e){e=e||{};var i=ol(t);if(!i)throw new Error("Illegal svg");var n=new lv;this._root=n;var r=i.getAttribute("viewBox")||"",a=parseFloat(i.getAttribute("width")||e.width),o=parseFloat(i.getAttribute("height")||e.height);isNaN(a)&&(a=null),isNaN(o)&&(o=null),cl(i,n,null,!0);for(var s=i.firstChild;s;)this._parseNode(s,n),s=s.nextSibling;var l,h;if(r){var u=z(r).split(E_);u.length>=4&&(l={x:parseFloat(u[0]||0),y:parseFloat(u[1]||0),width:parseFloat(u[2]),height:parseFloat(u[3])})}if(l&&null!=a&&null!=o&&(h=gl(l,a,o),!e.ignoreViewBox)){var c=n;n=new lv,n.add(c),c.scale=h.scale.slice(),c.position=h.position.slice()}return e.ignoreRootClip||null==a||null==o||n.setClipPath(new Dy({shape:{x:0,y:0,width:a,height:o}})),{root:n,width:a,height:o,viewBoxRect:l,viewBoxTransform:h}},sl.prototype._parseNode=function(t,e){var i=t.nodeName.toLowerCase();"defs"===i?this._isDefine=!0:"text"===i&&(this._isText=!0);var n;if(this._isDefine){var r=B_[i];if(r){var a=r.call(this,t),o=t.getAttribute("id");o&&(this._defs[o]=a)}}else{var r=R_[i];r&&(n=r.call(this,t,e),e.add(n))}for(var s=t.firstChild;s;)1===s.nodeType&&this._parseNode(s,n),3===s.nodeType&&this._isText&&this._parseText(s,n),s=s.nextSibling;"defs"===i?this._isDefine=!1:"text"===i&&(this._isText=!1)},sl.prototype._parseText=function(t,e){if(1===t.nodeType){var i=t.getAttribute("dx")||0,n=t.getAttribute("dy")||0;this._textX+=parseFloat(i),this._textY+=parseFloat(n)}var r=new xy({style:{text:t.textContent,transformText:!0},position:[this._textX||0,this._textY||0]});hl(e,r),cl(t,r,this._defs);var a=r.style.fontSize;a&&9>a&&(r.style.fontSize=9,r.scale=r.scale||[1,1],r.scale[0]*=a/9,r.scale[1]*=a/9);var o=r.getBoundingRect();return this._textX+=o.width,e.add(r),r};var R_={g:function(t,e){var i=new lv;return hl(e,i),cl(t,i,this._defs),i},rect:function(t,e){var i=new Dy;return hl(e,i),cl(t,i,this._defs),i.setShape({x:parseFloat(t.getAttribute("x")||0),y:parseFloat(t.getAttribute("y")||0),width:parseFloat(t.getAttribute("width")||0),height:parseFloat(t.getAttribute("height")||0)}),i},circle:function(t,e){var i=new _y;return hl(e,i),cl(t,i,this._defs),i.setShape({cx:parseFloat(t.getAttribute("cx")||0),cy:parseFloat(t.getAttribute("cy")||0),r:parseFloat(t.getAttribute("r")||0)}),i},line:function(t,e){var i=new ky;return hl(e,i),cl(t,i,this._defs),i.setShape({x1:parseFloat(t.getAttribute("x1")||0),y1:parseFloat(t.getAttribute("y1")||0),x2:parseFloat(t.getAttribute("x2")||0),y2:parseFloat(t.getAttribute("y2")||0)}),i},ellipse:function(t,e){var i=new z_;return hl(e,i),cl(t,i,this._defs),i.setShape({cx:parseFloat(t.getAttribute("cx")||0),cy:parseFloat(t.getAttribute("cy")||0),rx:parseFloat(t.getAttribute("rx")||0),ry:parseFloat(t.getAttribute("ry")||0)}),i},polygon:function(t,e){var i=t.getAttribute("points");i&&(i=ul(i));var n=new Cy({shape:{points:i||[]}});return hl(e,n),cl(t,n,this._defs),n},polyline:function(t,e){var i=new Fr;hl(e,i),cl(t,i,this._defs);var n=t.getAttribute("points");n&&(n=ul(n));var r=new Ay({shape:{points:n||[]}});return r},image:function(t,e){var i=new yn;return hl(e,i),cl(t,i,this._defs),i.setStyle({image:t.getAttribute("xlink:href"),x:t.getAttribute("x"),y:t.getAttribute("y"),width:t.getAttribute("width"),height:t.getAttribute("height")}),i},text:function(t,e){var i=t.getAttribute("x")||0,n=t.getAttribute("y")||0,r=t.getAttribute("dx")||0,a=t.getAttribute("dy")||0;this._textX=parseFloat(i)+parseFloat(r),this._textY=parseFloat(n)+parseFloat(a);var o=new lv;return hl(e,o),cl(t,o,this._defs),o},tspan:function(t,e){var i=t.getAttribute("x"),n=t.getAttribute("y");null!=i&&(this._textX=parseFloat(i)),null!=n&&(this._textY=parseFloat(n));var r=t.getAttribute("dx")||0,a=t.getAttribute("dy")||0,o=new lv;return hl(e,o),cl(t,o,this._defs),this._textX+=r,this._textY+=a,o},path:function(t,e){var i=t.getAttribute("d")||"",n=Hr(i);return hl(e,n),cl(t,n,this._defs),n}},B_={lineargradient:function(t){var e=parseInt(t.getAttribute("x1")||0,10),i=parseInt(t.getAttribute("y1")||0,10),n=parseInt(t.getAttribute("x2")||10,10),r=parseInt(t.getAttribute("y2")||0,10),a=new Ry(e,i,n,r);return ll(t,a),a},radialgradient:function(){}},N_={fill:"fill",stroke:"stroke","stroke-width":"lineWidth",opacity:"opacity","fill-opacity":"fillOpacity","stroke-opacity":"strokeOpacity","stroke-dasharray":"lineDash","stroke-dashoffset":"lineDashOffset","stroke-linecap":"lineCap","stroke-linejoin":"lineJoin","stroke-miterlimit":"miterLimit","font-family":"fontFamily","font-size":"fontSize","font-style":"fontStyle","font-weight":"fontWeight","text-align":"textAlign","alignment-baseline":"textBaseline"},F_=/url\(\s*#(.*?)\)/,V_=/(translate|scale|rotate|skewX|skewY|matrix)\(([\-\s0-9\.e,]*)\)/g,W_=/([^\s:;]+)\s*:\s*([^:;]+)/g,G_=N(),H_={registerMap:function(t,e,i){var n;return _(e)?n=e:e.svg?n=[{type:"svg",source:e.svg,specialAreas:e.specialAreas}]:(e.geoJson&&!e.features&&(i=e.specialAreas,e=e.geoJson),n=[{type:"geoJSON",source:e,specialAreas:i}]),f(n,function(t){var e=t.type;"geoJson"===e&&(e=t.type="geoJSON");var i=Z_[e];i(t)}),G_.set(t,n)},retrieveMap:function(t){return G_.get(t)}},Z_={geoJSON:function(t){var e=t.source;t.geoJSON=b(e)?"undefined"!=typeof JSON&&JSON.parse?JSON.parse(e):new Function("return ("+e+");")():e},svg:function(t){t.svgXML=ol(t.source)}},X_=O,Y_=f,j_=w,q_=S,U_=yx.parseClassType,$_="4.2.0",K_={zrender:"4.0.5"},Q_=1,J_=1e3,tw=5e3,ew=1e3,iw=2e3,nw=3e3,rw=4e3,aw=5e3,ow={PROCESSOR:{FILTER:J_,STATISTIC:tw},VISUAL:{LAYOUT:ew,GLOBAL:iw,CHART:nw,COMPONENT:rw,BRUSH:aw}},sw="__flagInMainProcess",lw="__optionUpdated",hw=/^[a-zA-Z0-9_]+$/;ml.prototype.on=vl("on"),ml.prototype.off=vl("off"),ml.prototype.one=vl("one"),c(ml,bg);var uw=yl.prototype;uw._onframe=function(){if(!this._disposed){var t=this._scheduler;if(this[lw]){var e=this[lw].silent;this[sw]=!0,_l(this),cw.update.call(this),this[sw]=!1,this[lw]=!1,Ml.call(this,e),Il.call(this,e)}else if(t.unfinished){var i=Q_,n=this._model,r=this._api;t.unfinished=!1;do{var a=+new Date;t.performSeriesTasks(n),t.performDataProcessorTasks(n),bl(this,n),t.performVisualTasks(n),Pl(this,this._model,r,"remain"),i-=+new Date-a}while(i>0&&t.unfinished);t.unfinished||this._zr.flush()}}},uw.getDom=function(){return this._dom},uw.getZr=function(){return this._zr},uw.setOption=function(t,e,i){var n;if(q_(e)&&(i=e.lazyUpdate,n=e.silent,e=e.notMerge),this[sw]=!0,!this._model||e){var r=new Qo(this._api),a=this._theme,o=this._model=new Ex(null,null,a,r);o.scheduler=this._scheduler,o.init(null,null,a,r)}this._model.setOption(t,vw),i?(this[lw]={silent:n},this[sw]=!1):(_l(this),cw.update.call(this),this._zr.flush(),this[lw]=!1,this[sw]=!1,Ml.call(this,n),Il.call(this,n))},uw.setTheme=function(){console.error("ECharts#setTheme() is DEPRECATED in ECharts 3.0")},uw.getModel=function(){return this._model},uw.getOption=function(){return this._model&&this._model.getOption()},uw.getWidth=function(){return this._zr.getWidth()},uw.getHeight=function(){return this._zr.getHeight()},uw.getDevicePixelRatio=function(){return this._zr.painter.dpr||window.devicePixelRatio||1},uw.getRenderedCanvas=function(t){if(tg.canvasSupported){t=t||{},t.pixelRatio=t.pixelRatio||1,t.backgroundColor=t.backgroundColor||this._model.get("backgroundColor");var e=this._zr;return e.painter.getRenderedCanvas(t)}},uw.getSvgDataUrl=function(){if(tg.svgSupported){var t=this._zr,e=t.storage.getDisplayList();return f(e,function(t){t.stopAnimation(!0)}),t.painter.pathToDataUrl()}},uw.getDataURL=function(t){t=t||{};var e=t.excludeComponents,i=this._model,n=[],r=this;Y_(e,function(t){i.eachComponent({mainType:t},function(t){var e=r._componentsMap[t.__viewId];e.group.ignore||(n.push(e),e.group.ignore=!0)})});var a="svg"===this._zr.painter.getType()?this.getSvgDataUrl():this.getRenderedCanvas(t).toDataURL("image/"+(t&&t.type||"png"));return Y_(n,function(t){t.group.ignore=!1}),a},uw.getConnectedDataURL=function(t){if(tg.canvasSupported){var e=this.group,i=Math.min,r=Math.max,a=1/0;if(bw[e]){var o=a,s=a,l=-a,h=-a,u=[],c=t&&t.pixelRatio||1;f(ww,function(a){if(a.group===e){var c=a.getRenderedCanvas(n(t)),d=a.getDom().getBoundingClientRect();o=i(d.left,o),s=i(d.top,s),l=r(d.right,l),h=r(d.bottom,h),u.push({dom:c,left:d.left,top:d.top})}}),o*=c,s*=c,l*=c,h*=c;var d=l-o,p=h-s,g=cg();g.width=d,g.height=p;var v=On(g);return Y_(u,function(t){var e=new yn({style:{x:t.left*c-o,y:t.top*c-s,image:t.dom}});v.add(e)}),v.refreshImmediately(),g.toDataURL("image/"+(t&&t.type||"png"))}return this.getDataURL(t)}},uw.convertToPixel=x(xl,"convertToPixel"),uw.convertFromPixel=x(xl,"convertFromPixel"),uw.containPixel=function(t,e){var i,n=this._model;return t=qn(n,t),f(t,function(t,n){n.indexOf("Models")>=0&&f(t,function(t){var r=t.coordinateSystem;if(r&&r.containPoint)i|=!!r.containPoint(e);else if("seriesModels"===n){var a=this._chartsMap[t.__viewId];a&&a.containPoint&&(i|=a.containPoint(e,t))}},this)},this),!!i},uw.getVisual=function(t,e){var i=this._model;t=qn(i,t,{defaultMainType:"series"});var n=t.seriesModel,r=n.getData(),a=t.hasOwnProperty("dataIndexInside")?t.dataIndexInside:t.hasOwnProperty("dataIndex")?r.indexOfRawIndex(t.dataIndex):null;return null!=a?r.getItemVisual(a,e):r.getVisual(e)},uw.getViewOfComponentModel=function(t){return this._componentsMap[t.__viewId]},uw.getViewOfSeriesModel=function(t){return this._chartsMap[t.__viewId]};var cw={prepareAndUpdate:function(t){_l(this),cw.update.call(this,t)},update:function(t){var e=this._model,i=this._api,n=this._zr,r=this._coordSysMgr,a=this._scheduler;if(e){a.restoreData(e,t),a.performSeriesTasks(e),r.create(e,i),a.performDataProcessorTasks(e,t),bl(this,e),r.update(e,i),Al(e),a.performVisualTasks(e,t),Dl(this,e,i,t);var o=e.get("backgroundColor")||"transparent";if(tg.canvasSupported)n.setBackgroundColor(o);else{var s=He(o);o=Qe(s,"rgb"),0===s[3]&&(o="transparent")}Ll(e,i)}},updateTransform:function(t){var e=this._model,i=this,n=this._api;if(e){var r=[];e.eachComponent(function(a,o){var s=i.getViewOfComponentModel(o);if(s&&s.__alive)if(s.updateTransform){var l=s.updateTransform(o,e,n,t);l&&l.update&&r.push(s)}else r.push(s)});var a=N();e.eachSeries(function(r){var o=i._chartsMap[r.__viewId];if(o.updateTransform){var s=o.updateTransform(r,e,n,t);s&&s.update&&a.set(r.uid,1)}else a.set(r.uid,1)}),Al(e),this._scheduler.performVisualTasks(e,t,{setDirty:!0,dirtyMap:a}),Pl(i,e,n,t,a),Ll(e,this._api)}},updateView:function(t){var e=this._model;e&&(Bs.markUpdateMethod(t,"updateView"),Al(e),this._scheduler.performVisualTasks(e,t,{setDirty:!0}),Dl(this,this._model,this._api,t),Ll(e,this._api))},updateVisual:function(t){cw.update.call(this,t)},updateLayout:function(t){cw.update.call(this,t)}};uw.resize=function(t){this._zr.resize(t);var e=this._model;if(this._loadingFX&&this._loadingFX.resize(),e){var i=e.resetOption("media"),n=t&&t.silent;this[sw]=!0,i&&_l(this),cw.update.call(this),this[sw]=!1,Ml.call(this,n),Il.call(this,n)}},uw.showLoading=function(t,e){if(q_(t)&&(e=t,t=""),t=t||"default",this.hideLoading(),_w[t]){var i=_w[t](this._api,e),n=this._zr;this._loadingFX=i,n.add(i)}},uw.hideLoading=function(){this._loadingFX&&this._zr.remove(this._loadingFX),this._loadingFX=null},uw.makeActionFromEvent=function(t){var e=o({},t);return e.type=pw[t.type],e},uw.dispatchAction=function(t,e){if(q_(e)||(e={silent:!!e}),fw[t.type]&&this._model){if(this[sw])return void this._pendingActions.push(t);Sl.call(this,t,e.silent),e.flush?this._zr.flush(!0):e.flush!==!1&&tg.browser.weChat&&this._throttledZrFlush(),Ml.call(this,e.silent),Il.call(this,e.silent)}},uw.appendData=function(t){var e=t.seriesIndex,i=this.getModel(),n=i.getSeriesByIndex(e);n.appendData(t),this._scheduler.unfinished=!0},uw.on=vl("on"),uw.off=vl("off"),uw.one=vl("one");var dw=["click","dblclick","mouseover","mouseout","mousemove","mousedown","mouseup","globalout","contextmenu"];uw._initEvents=function(){Y_(dw,function(t){this._zr.on(t,function(e){var i,n=this.getModel(),r=e.target,a="globalout"===t;if(a)i={};else if(r&&null!=r.dataIndex){var s=r.dataModel||n.getSeriesByIndex(r.seriesIndex);i=s&&s.getDataParams(r.dataIndex,r.dataType,r)||{}}else r&&r.eventData&&(i=o({},r.eventData));if(i){var l=i.componentType,h=i.componentIndex;("markLine"===l||"markPoint"===l||"markArea"===l)&&(l="series",h=i.seriesIndex);var u=l&&null!=h&&n.getComponent(l,h),c=u&&this["series"===u.mainType?"_chartsMap":"_componentsMap"][u.__viewId];i.event=e,i.type=t,this._ecEventProcessor.eventInfo={targetEl:r,packedEvent:i,model:u,view:c},this.trigger(t,i)}},this)},this),Y_(pw,function(t,e){this._messageCenter.on(e,function(t){this.trigger(e,t)},this)},this)},uw.isDisposed=function(){return this._disposed},uw.clear=function(){this.setOption({series:[]},!0)},uw.dispose=function(){if(!this._disposed){this._disposed=!0,$n(this.getDom(),Iw,"");var t=this._api,e=this._model;Y_(this._componentsViews,function(i){i.dispose(e,t)}),Y_(this._chartsViews,function(i){i.dispose(e,t)}),this._zr.dispose(),delete ww[this.id]}},c(yl,bg),Bl.prototype={constructor:Bl,normalizeQuery:function(t){var e={},i={},n={};if(b(t)){var r=U_(t);e.mainType=r.main||null,e.subType=r.sub||null}else{var a=["Index","Name","Id"],o={name:1,dataIndex:1,dataType:1};f(t,function(t,r){for(var s=!1,l=0;l0&&u===r.length-h.length){var c=r.slice(0,u);"data"!==c&&(e.mainType=c,e[h.toLowerCase()]=t,s=!0)}}o.hasOwnProperty(r)&&(i[r]=t,s=!0),s||(n[r]=t)})}return{cptQuery:e,dataQuery:i,otherQuery:n}},filter:function(t,e){function i(t,e,i,n){return null==t[i]||e[n||i]===t[i]}var n=this.eventInfo;if(!n)return!0;var r=n.targetEl,a=n.packedEvent,o=n.model,s=n.view;if(!o||!s)return!0;var l=e.cptQuery,h=e.dataQuery;return i(l,o,"mainType")&&i(l,o,"subType")&&i(l,o,"index","componentIndex")&&i(l,o,"name")&&i(l,o,"id")&&i(h,a,"name")&&i(h,a,"dataIndex")&&i(h,a,"dataType")&&(!s.filterForExposedEvent||s.filterForExposedEvent(t,e.otherQuery,r,a))},afterTrigger:function(){this.eventInfo=null}};var fw={},pw={},gw=[],vw=[],mw=[],yw=[],xw={},_w={},ww={},bw={},Sw=new Date-0,Mw=new Date-0,Iw="_echarts_instance_",Tw=Wl;Jl(iw,m_),Yl(Ux),jl(tw,$x),eh("default",w_),Ul({type:"highlight",event:"highlight",update:"highlight"},V),Ul({type:"downplay",event:"downplay",update:"downplay"},V),Xl("light",D_),Xl("dark",O_);var Cw={};uh.prototype={constructor:uh,add:function(t){return this._add=t,this},update:function(t){return this._update=t,this},remove:function(t){return this._remove=t,this},execute:function(){var t,e=this._old,i=this._new,n={},r={},a=[],o=[];for(ch(e,n,a,"_oldKeyGetter",this),ch(i,r,o,"_newKeyGetter",this),t=0;tu;u++)this._add&&this._add(l[u]);else this._add&&this._add(l)}}}};var Aw=N(["tooltip","label","itemName","itemId","seriesName"]),Dw=S,kw="undefined",Pw="e\x00\x00",Lw={"float":typeof Float64Array===kw?Array:Float64Array,"int":typeof Int32Array===kw?Array:Int32Array,ordinal:Array,number:Array,time:Array},Ow=typeof Uint32Array===kw?Array:Uint32Array,zw=typeof Uint16Array===kw?Array:Uint16Array,Ew=["hasItemOption","_nameList","_idList","_invertedIndicesMap","_rawData","_chunkSize","_chunkCount","_dimValueGetter","_count","_rawCount","_nameDimIdx","_idDimIdx"],Rw=["_extent","_approximateExtent","_rawExtent"],Bw=function(t,e){t=t||["x","y"];for(var i={},n=[],r={},a=0;a=e)){for(var i,n=this._chunkSize,r=this._rawData,a=this._storage,o=this.dimensions,s=o.length,l=this._dimensionInfos,h=this._nameList,u=this._idList,c=this._rawExtent,d=this._nameRepeatCount={},f=this._chunkCount,p=f-1,g=0;s>g;g++){var v=o[g];c[v]||(c[v]=Th());var m=l[v];0===m.otherDims.itemName&&(i=this._nameDimIdx=g),0===m.otherDims.itemId&&(this._idDimIdx=g);var y=Lw[m.type];a[v]||(a[v]=[]);var x=a[v][p];if(x&&x.lengthb;b+=n)a[v].push(new y(Math.min(e-b,n)));this._chunkCount=a[v].length}for(var S=new Array(s),M=t;e>M;M++){S=r.getItem(M,S);for(var I=Math.floor(M/n),T=M%n,b=0;s>b;b++){var v=o[b],C=a[v][I],A=this._dimValueGetter(S,v,M,b);C[T]=A;var D=c[v];AD[1]&&(D[1]=A)}if(!r.pure){var k=h[M];if(S&&null==k)if(null!=S.name)h[M]=k=S.name;else if(null!=i){var P=o[i],L=a[P][I];if(L){k=L[T];var O=l[P].ordinalMeta;O&&O.categories.length&&(k=O.categories[k])}}var z=null==S?null:S.id;null==z&&null!=k&&(d[k]=d[k]||0,z=k,d[k]>0&&(z+="__ec__"+d[k]),d[k]++),null!=z&&(u[M]=z)}}!r.persistent&&r.clean&&r.clean(),this._rawCount=this._count=e,this._extent={},yh(this)}},Nw.count=function(){return this._count},Nw.getIndices=function(){var t,e=this._indices;if(e){var i=e.constructor,n=this._count;if(i===Array){t=new i(n);for(var r=0;n>r;r++)t[r]=e[r]}else t=new i(e.buffer,0,n)}else for(var i=gh(this),t=new i(this.count()),r=0;r=0&&e=0&&en;n++)i.push(this.get(t[n],e));return i},Nw.hasValue=function(t){for(var e=this._dimensionsSummary.dataDimsOnCoord,i=this._dimensionInfos,n=0,r=e.length;r>n;n++)if("ordinal"!==i[e[n]].type&&isNaN(this.get(e[n],t)))return!1;return!0},Nw.getDataExtent=function(t){t=this.getDimension(t);var e=this._storage[t],i=Th();if(!e)return i;var n,r=this.count(),a=!this._indices;if(a)return this._rawExtent[t].slice();if(n=this._extent[t])return n.slice();n=i;for(var o=n[0],s=n[1],l=0;r>l;l++){var h=this._getFast(t,this.getRawIndex(l));o>h&&(o=h),h>s&&(s=h)}return n=[o,s],this._extent[t]=n,n},Nw.getApproximateExtent=function(t){return t=this.getDimension(t),this._approximateExtent[t]||this.getDataExtent(t)},Nw.setApproximateExtent=function(t,e){e=this.getDimension(e),this._approximateExtent[e]=t.slice()},Nw.getCalculationInfo=function(t){return this._calculationInfo[t]},Nw.setCalculationInfo=function(t,e){Dw(t)?o(this._calculationInfo,t):this._calculationInfo[t]=e},Nw.getSum=function(t){var e=this._storage[t],i=0;if(e)for(var n=0,r=this.count();r>n;n++){var a=this.get(t,n);isNaN(a)||(i+=a)}return i},Nw.getMedian=function(t){var e=[];this.each(t,function(t){isNaN(t)||e.push(t)});var i=[].concat(e).sort(function(t,e){return t-e}),n=this.count();return 0===n?0:n%2===1?i[(n-1)/2]:(i[n/2]+i[n/2-1])/2},Nw.rawIndexOf=function(t,e){var i=t&&this._invertedIndicesMap[t],n=i[e];return null==n||isNaN(n)?-1:n},Nw.indexOfName=function(t){for(var e=0,i=this.count();i>e;e++)if(this.getName(e)===t)return e;return-1},Nw.indexOfRawIndex=function(t){if(!this._indices)return t;if(t>=this._rawCount||0>t)return-1;var e=this._indices,i=e[t];if(null!=i&&i=n;){var a=(n+r)/2|0;if(e[a]t))return a;r=a-1}}return-1},Nw.indicesOfNearest=function(t,e,i){var n=this._storage,r=n[t],a=[];if(!r)return a;null==i&&(i=1/0);for(var o=Number.MAX_VALUE,s=-1,l=0,h=this.count();h>l;l++){var u=e-this.get(t,l),c=Math.abs(u);i>=u&&o>=c&&((o>c||u>=0&&0>s)&&(o=c,s=u,a.length=0),a.push(l))}return a},Nw.getRawIndex=_h,Nw.getRawDataItem=function(t){if(this._rawData.persistent)return this._rawData.getItem(this.getRawIndex(t));for(var e=[],i=0;io;o++)s[o]=this.get(t[o],a);s[o]=a,e.apply(i,s)}}},Nw.filterSelf=function(t,e,i,n){if(this._count){"function"==typeof t&&(n=i,i=e,e=t,t=[]),i=i||n||this,t=p(Sh(t),this.getDimension,this);for(var r=this.count(),a=gh(this),o=new a(r),s=[],l=t.length,h=0,u=t[0],c=0;r>c;c++){var d,f=this.getRawIndex(c);if(0===l)d=e.call(i,c);else if(1===l){var g=this._getFast(u,f);d=e.call(i,g,c)}else{for(var v=0;l>v;v++)s[v]=this._getFast(u,f);s[v]=c,d=e.apply(i,s)}d&&(o[h++]=f)}return r>h&&(this._indices=o),this._count=h,this._extent={},this.getRawIndex=this._indices?wh:_h,this}},Nw.selectRange=function(t){if(this._count){var e=[];for(var i in t)t.hasOwnProperty(i)&&e.push(i);var n=e.length;if(n){var r=this.count(),a=gh(this),o=new a(r),s=0,l=e[0],h=t[l][0],u=t[l][1],c=!1;if(!this._indices){var d=0;if(1===n){for(var f=this._storage[e[0]],p=0;pm;m++){var y=g[m];(y>=h&&u>=y||isNaN(y))&&(o[s++]=d),d++}c=!0}else if(2===n){for(var f=this._storage[l],x=this._storage[e[1]],_=t[e[1]][0],w=t[e[1]][1],p=0;pm;m++){var y=g[m],S=b[m]; + (y>=h&&u>=y||isNaN(y))&&(S>=_&&w>=S||isNaN(S))&&(o[s++]=d),d++}c=!0}}if(!c)if(1===n)for(var m=0;r>m;m++){var M=this.getRawIndex(m),y=this._getFast(l,M);(y>=h&&u>=y||isNaN(y))&&(o[s++]=M)}else for(var m=0;r>m;m++){for(var I=!0,M=this.getRawIndex(m),p=0;n>p;p++){var T=e[p],y=this._getFast(i,M);(yt[T][1])&&(I=!1)}I&&(o[s++]=this.getRawIndex(m))}return r>s&&(this._indices=o),this._count=s,this._extent={},this.getRawIndex=this._indices?wh:_h,this}}},Nw.mapArray=function(t,e,i,n){"function"==typeof t&&(n=i,i=e,e=t,t=[]),i=i||n||this;var r=[];return this.each(t,function(){r.push(e&&e.apply(this,arguments))},i),r},Nw.map=function(t,e,i,n){i=i||n||this,t=p(Sh(t),this.getDimension,this);var r=Mh(this,t);r._indices=this._indices,r.getRawIndex=r._indices?wh:_h;for(var a=r._storage,o=[],s=this._chunkSize,l=t.length,h=this.count(),u=[],c=r._rawExtent,d=0;h>d;d++){for(var f=0;l>f;f++)u[f]=this.get(t[f],d);u[l]=d;var g=e&&e.apply(i,u);if(null!=g){"object"!=typeof g&&(o[0]=g,g=o);for(var v=this.getRawIndex(d),m=Math.floor(v/s),y=v%s,x=0;xb[1]&&(b[1]=w)}}}return r},Nw.downSample=function(t,e,i,n){for(var r=Mh(this,[t]),a=r._storage,o=[],s=Math.floor(1/e),l=a[t],h=this.count(),u=this._chunkSize,c=r._rawExtent[t],d=new(gh(this))(h),f=0,p=0;h>p;p+=s){s>h-p&&(s=h-p,o.length=s);for(var g=0;s>g;g++){var v=this.getRawIndex(p+g),m=Math.floor(v/u),y=v%u;o[g]=l[m][y]}var x=i(o),_=this.getRawIndex(Math.min(p+n(o,x)||0,h-1)),w=Math.floor(_/u),b=_%u;l[w][b]=x,xc[1]&&(c[1]=x),d[f++]=_}return r._count=f,r._indices=d,r.getRawIndex=wh,r},Nw.getItemModel=function(t){var e=this.hostModel;return new Wa(this.getRawDataItem(t),e,e&&e.ecModel)},Nw.diff=function(t){var e=this;return new uh(t?t.getIndices():[],this.getIndices(),function(e){return bh(t,e)},function(t){return bh(e,t)})},Nw.getVisual=function(t){var e=this._visual;return e&&e[t]},Nw.setVisual=function(t,e){if(Dw(t))for(var i in t)t.hasOwnProperty(i)&&this.setVisual(i,t[i]);else this._visual=this._visual||{},this._visual[t]=e},Nw.setLayout=function(t,e){if(Dw(t))for(var i in t)t.hasOwnProperty(i)&&this.setLayout(i,t[i]);else this._layout[t]=e},Nw.getLayout=function(t){return this._layout[t]},Nw.getItemLayout=function(t){return this._itemLayouts[t]},Nw.setItemLayout=function(t,e,i){this._itemLayouts[t]=i?o(this._itemLayouts[t]||{},e):e},Nw.clearItemLayouts=function(){this._itemLayouts.length=0},Nw.getItemVisual=function(t,e,i){var n=this._itemVisuals[t],r=n&&n[e];return null!=r||i?r:this.getVisual(e)},Nw.setItemVisual=function(t,e,i){var n=this._itemVisuals[t]||{},r=this.hasItemVisual;if(this._itemVisuals[t]=n,Dw(e))for(var a in e)e.hasOwnProperty(a)&&(n[a]=e[a],r[a]=!0);else n[e]=i,r[e]=!0},Nw.clearAllVisual=function(){this._visual={},this._itemVisuals=[],this.hasItemVisual={}};var Fw=function(t){t.seriesIndex=this.seriesIndex,t.dataIndex=this.dataIndex,t.dataType=this.dataType};Nw.setItemGraphicEl=function(t,e){var i=this.hostModel;e&&(e.dataIndex=t,e.dataType=this.dataType,e.seriesIndex=i&&i.seriesIndex,"group"===e.type&&e.traverse(Fw,e)),this._graphicEls[t]=e},Nw.getItemGraphicEl=function(t){return this._graphicEls[t]},Nw.eachItemGraphicEl=function(t,e){f(this._graphicEls,function(i,n){i&&t&&t.call(e,i,n)})},Nw.cloneShallow=function(t){if(!t){var e=p(this.dimensions,this.getDimensionInfo,this);t=new Bw(e,this.hostModel)}if(t._storage=this._storage,mh(t,this),this._indices){var i=this._indices.constructor;t._indices=new i(this._indices)}else t._indices=null;return t.getRawIndex=t._indices?wh:_h,t},Nw.wrapMethod=function(t,e){var i=this[t];"function"==typeof i&&(this.__wrappedMethods=this.__wrappedMethods||[],this.__wrappedMethods.push(t),this[t]=function(){var t=i.apply(this,arguments);return e.apply(this,[t].concat(P(arguments)))})},Nw.TRANSFERABLE_METHODS=["cloneShallow","downSample","map"],Nw.CHANGABLE_METHODS=["filterSelf","selectRange"];var Vw=function(t,e){return e=e||{},Ch(e.coordDimensions||[],t,{dimsDef:e.dimensionsDefine||t.dimensionsDefine,encodeDef:e.encodeDefine||t.encodeDefine,dimCount:e.dimensionsCount,generateCoord:e.generateCoord,generateCoordCount:e.generateCoordCount})};Rh.prototype.parse=function(t){return t},Rh.prototype.getSetting=function(t){return this._setting[t]},Rh.prototype.contain=function(t){var e=this._extent;return t>=e[0]&&t<=e[1]},Rh.prototype.normalize=function(t){var e=this._extent;return e[1]===e[0]?.5:(t-e[0])/(e[1]-e[0])},Rh.prototype.scale=function(t){var e=this._extent;return t*(e[1]-e[0])+e[0]},Rh.prototype.unionExtent=function(t){var e=this._extent;t[0]e[1]&&(e[1]=t[1])},Rh.prototype.unionExtentFromData=function(t,e){this.unionExtent(t.getApproximateExtent(e))},Rh.prototype.getExtent=function(){return this._extent.slice()},Rh.prototype.setExtent=function(t,e){var i=this._extent;isNaN(t)||(i[0]=t),isNaN(e)||(i[1]=e)},Rh.prototype.isBlank=function(){return this._isBlank},Rh.prototype.setBlank=function(t){this._isBlank=t},Rh.prototype.getLabel=null,er(Rh),ar(Rh,{registerWhenExtend:!0}),Bh.createByAxisModel=function(t){var e=t.option,i=e.data,n=i&&p(i,Fh);return new Bh({categories:n,needCollect:!n,deduplication:e.dedplication!==!1})};var Ww=Bh.prototype;Ww.getOrdinal=function(t){return Nh(this).get(t)},Ww.parseAndCollect=function(t){var e,i=this._needCollect;if("string"!=typeof t&&!i)return t;if(i&&!this._deduplication)return e=this.categories.length,this.categories[e]=t,e;var n=Nh(this);return e=n.get(t),null==e&&(i?(e=this.categories.length,this.categories[e]=t,n.set(t,e)):e=0/0),e};var Gw=Rh.prototype,Hw=Rh.extend({type:"ordinal",init:function(t,e){(!t||_(t))&&(t=new Bh({categories:t})),this._ordinalMeta=t,this._extent=e||[0,t.categories.length-1]},parse:function(t){return"string"==typeof t?this._ordinalMeta.getOrdinal(t):Math.round(t)},contain:function(t){return t=this.parse(t),Gw.contain.call(this,t)&&null!=this._ordinalMeta.categories[t]},normalize:function(t){return Gw.normalize.call(this,this.parse(t))},scale:function(t){return Math.round(Gw.scale.call(this,t))},getTicks:function(){for(var t=[],e=this._extent,i=e[0];i<=e[1];)t.push(i),i++;return t},getLabel:function(t){return this.isBlank()?void 0:this._ordinalMeta.categories[t]},count:function(){return this._extent[1]-this._extent[0]+1},unionExtentFromData:function(t,e){this.unionExtent(t.getApproximateExtent(e))},getOrdinalMeta:function(){return this._ordinalMeta},niceTicks:V,niceExtent:V});Hw.create=function(){return new Hw};var Zw=$a,Xw=$a,Yw=Rh.extend({type:"interval",_interval:0,_intervalPrecision:2,setExtent:function(t,e){var i=this._extent;isNaN(t)||(i[0]=parseFloat(t)),isNaN(e)||(i[1]=parseFloat(e))},unionExtent:function(t){var e=this._extent;t[0]e[1]&&(e[1]=t[1]),Yw.prototype.setExtent.call(this,e[0],e[1])},getInterval:function(){return this._interval},setInterval:function(t){this._interval=t,this._niceExtent=this._extent.slice(),this._intervalPrecision=Wh(t)},getTicks:function(){return Zh(this._interval,this._extent,this._niceExtent,this._intervalPrecision)},getLabel:function(t,e){if(null==t)return"";var i=e&&e.precision;return null==i?i=Ja(t)||0:"auto"===i&&(i=this._intervalPrecision),t=Xw(t,i,!0),co(t)},niceTicks:function(t,e,i){t=t||5;var n=this._extent,r=n[1]-n[0];if(isFinite(r)){0>r&&(r=-r,n.reverse());var a=Vh(n,t,e,i);this._intervalPrecision=a.intervalPrecision,this._interval=a.interval,this._niceExtent=a.niceTickExtent}},niceExtent:function(t){var e=this._extent;if(e[0]===e[1])if(0!==e[0]){var i=e[0];t.fixMax?e[0]-=i/2:(e[1]+=i/2,e[0]-=i/2)}else e[1]=1;var n=e[1]-e[0];isFinite(n)||(e[0]=0,e[1]=1),this.niceTicks(t.splitNumber,t.minInterval,t.maxInterval);var r=this._interval;t.fixMin||(e[0]=Xw(Math.floor(e[0]/r)*r)),t.fixMax||(e[1]=Xw(Math.ceil(e[1]/r)*r))}});Yw.create=function(){return new Yw};var jw="__ec_stack_",qw=.5,Uw="undefined"!=typeof Float32Array?Float32Array:Array,$w={seriesType:"bar",plan:h_(),reset:function(t){function e(t,e){for(var i,c=new Uw(2*t.count),d=[],f=[],p=0;null!=(i=t.next());)f[h]=e.get(o,i),f[1-h]=e.get(s,i),d=n.dataToPoint(f,null,d),c[p++]=d[0],c[p++]=d[1];e.setLayout({largePoints:c,barWidth:u,valueAxisStart:eu(r,a,!1),valueAxisHorizontal:l})}if(Jh(t)&&tu(t)){var i=t.getData(),n=t.coordinateSystem,r=n.getBaseAxis(),a=n.getOtherAxis(r),o=i.mapDimension(a.dim),s=i.mapDimension(r.dim),l=a.isHorizontal(),h=l?0:1,u=Kh(Uh([t]),r,t).width;return u>qw||(u=qw),{progress:e}}}},Kw=Yw.prototype,Qw=Math.ceil,Jw=Math.floor,tb=1e3,eb=60*tb,ib=60*eb,nb=24*ib,rb=function(t,e,i,n){for(;n>i;){var r=i+n>>>1;t[r][1]a&&(a=e),null!=i&&a>i&&(a=i);var o=ob.length,s=rb(ob,a,0,o),l=ob[Math.min(s,o-1)],h=l[1];if("year"===l[0]){var u=r/h,c=so(u/t,!0);h*=c}var d=this.getSetting("useUTC")?0:60*new Date(+n[0]||+n[1]).getTimezoneOffset()*1e3,f=[Math.round(Qw((n[0]-d)/h)*h+d),Math.round(Jw((n[1]-d)/h)*h+d)];Hh(f,n),this._stepLvl=l,this._interval=h,this._niceExtent=f},parse:function(t){return+ro(t)}});f(["contain","normalize"],function(t){ab.prototype[t]=function(e){return Kw[t].call(this,this.parse(e))}});var ob=[["hh:mm:ss",tb],["hh:mm:ss",5*tb],["hh:mm:ss",10*tb],["hh:mm:ss",15*tb],["hh:mm:ss",30*tb],["hh:mm\nMM-dd",eb],["hh:mm\nMM-dd",5*eb],["hh:mm\nMM-dd",10*eb],["hh:mm\nMM-dd",15*eb],["hh:mm\nMM-dd",30*eb],["hh:mm\nMM-dd",ib],["hh:mm\nMM-dd",2*ib],["hh:mm\nMM-dd",6*ib],["hh:mm\nMM-dd",12*ib],["MM-dd\nyyyy",nb],["MM-dd\nyyyy",2*nb],["MM-dd\nyyyy",3*nb],["MM-dd\nyyyy",4*nb],["MM-dd\nyyyy",5*nb],["MM-dd\nyyyy",6*nb],["week",7*nb],["MM-dd\nyyyy",10*nb],["week",14*nb],["week",21*nb],["month",31*nb],["week",42*nb],["month",62*nb],["week",70*nb],["quarter",95*nb],["month",31*nb*4],["month",31*nb*5],["half-year",380*nb/2],["month",31*nb*8],["month",31*nb*10],["year",380*nb]];ab.create=function(t){return new ab({useUTC:t.ecModel.get("useUTC")})};var sb=Rh.prototype,lb=Yw.prototype,hb=Ja,ub=$a,cb=Math.floor,db=Math.ceil,fb=Math.pow,pb=Math.log,gb=Rh.extend({type:"log",base:10,$constructor:function(){Rh.apply(this,arguments),this._originalScale=new Yw},getTicks:function(){var t=this._originalScale,e=this._extent,i=t.getExtent();return p(lb.getTicks.call(this),function(n){var r=$a(fb(this.base,n));return r=n===e[0]&&t.__fixMin?iu(r,i[0]):r,r=n===e[1]&&t.__fixMax?iu(r,i[1]):r},this)},getLabel:lb.getLabel,scale:function(t){return t=sb.scale.call(this,t),fb(this.base,t)},setExtent:function(t,e){var i=this.base;t=pb(t)/pb(i),e=pb(e)/pb(i),lb.setExtent.call(this,t,e)},getExtent:function(){var t=this.base,e=sb.getExtent.call(this);e[0]=fb(t,e[0]),e[1]=fb(t,e[1]);var i=this._originalScale,n=i.getExtent();return i.__fixMin&&(e[0]=iu(e[0],n[0])),i.__fixMax&&(e[1]=iu(e[1],n[1])),e},unionExtent:function(t){this._originalScale.unionExtent(t);var e=this.base;t[0]=pb(t[0])/pb(e),t[1]=pb(t[1])/pb(e),sb.unionExtent.call(this,t)},unionExtentFromData:function(t,e){this.unionExtent(t.getApproximateExtent(e))},niceTicks:function(t){t=t||10;var e=this._extent,i=e[1]-e[0];if(!(1/0===i||0>=i)){var n=ao(i),r=t/i*n;for(.5>=r&&(n*=10);!isNaN(n)&&Math.abs(n)<1&&Math.abs(n)>0;)n*=10;var a=[$a(db(e[0]/n)*n),$a(cb(e[1]/n)*n)];this._interval=n,this._niceExtent=a}},niceExtent:function(t){lb.niceExtent.call(this,t);var e=this._originalScale;e.__fixMin=t.fixMin,e.__fixMax=t.fixMax}});f(["contain","normalize"],function(t){gb.prototype[t]=function(e){return e=pb(e)/pb(this.base),sb[t].call(this,e)}}),gb.create=function(){return new gb};var vb={getMin:function(t){var e=this.option,i=t||null==e.rangeStart?e.min:e.rangeStart;return this.axis&&null!=i&&"dataMin"!==i&&"function"!=typeof i&&!C(i)&&(i=this.axis.scale.parse(i)),i},getMax:function(t){var e=this.option,i=t||null==e.rangeEnd?e.max:e.rangeEnd;return this.axis&&null!=i&&"dataMax"!==i&&"function"!=typeof i&&!C(i)&&(i=this.axis.scale.parse(i)),i},getNeedCrossZero:function(){var t=this.option;return null!=t.rangeStart||null!=t.rangeEnd?!1:!t.scale},getCoordSysModel:V,setRange:function(t,e){this.option.rangeStart=t,this.option.rangeEnd=e},resetRange:function(){this.option.rangeStart=this.option.rangeEnd=null}},mb=$r({type:"triangle",shape:{cx:0,cy:0,width:0,height:0},buildPath:function(t,e){var i=e.cx,n=e.cy,r=e.width/2,a=e.height/2;t.moveTo(i,n-a),t.lineTo(i+r,n+a),t.lineTo(i-r,n+a),t.closePath()}}),yb=$r({type:"diamond",shape:{cx:0,cy:0,width:0,height:0},buildPath:function(t,e){var i=e.cx,n=e.cy,r=e.width/2,a=e.height/2;t.moveTo(i,n-a),t.lineTo(i+r,n),t.lineTo(i,n+a),t.lineTo(i-r,n),t.closePath()}}),xb=$r({type:"pin",shape:{x:0,y:0,width:0,height:0},buildPath:function(t,e){var i=e.x,n=e.y,r=e.width/5*3,a=Math.max(r,e.height),o=r/2,s=o*o/(a-o),l=n-a+o+s,h=Math.asin(s/o),u=Math.cos(h)*o,c=Math.sin(h),d=Math.cos(h),f=.6*o,p=.7*o;t.moveTo(i-u,l+s),t.arc(i,l,o,Math.PI-h,2*Math.PI+h),t.bezierCurveTo(i+u-c*f,l+s+d*f,i,n-p,i,n),t.bezierCurveTo(i,n-p,i-u+c*f,l+s+d*f,i-u,l+s),t.closePath()}}),_b=$r({type:"arrow",shape:{x:0,y:0,width:0,height:0},buildPath:function(t,e){var i=e.height,n=e.width,r=e.x,a=e.y,o=n/3*2;t.moveTo(r,a),t.lineTo(r+o,a+i),t.lineTo(r,a+i/4*3),t.lineTo(r-o,a+i),t.lineTo(r,a),t.closePath()}}),wb={line:ky,rect:Dy,roundRect:Dy,square:Dy,circle:_y,diamond:yb,pin:xb,arrow:_b,triangle:mb},bb={line:function(t,e,i,n,r){r.x1=t,r.y1=e+n/2,r.x2=t+i,r.y2=e+n/2},rect:function(t,e,i,n,r){r.x=t,r.y=e,r.width=i,r.height=n},roundRect:function(t,e,i,n,r){r.x=t,r.y=e,r.width=i,r.height=n,r.r=Math.min(i,n)/4},square:function(t,e,i,n,r){var a=Math.min(i,n);r.x=t,r.y=e,r.width=a,r.height=a},circle:function(t,e,i,n,r){r.cx=t+i/2,r.cy=e+n/2,r.r=Math.min(i,n)/2},diamond:function(t,e,i,n,r){r.cx=t+i/2,r.cy=e+n/2,r.width=i,r.height=n},pin:function(t,e,i,n,r){r.x=t+i/2,r.y=e+n/2,r.width=i,r.height=n},arrow:function(t,e,i,n,r){r.x=t+i/2,r.y=e+n/2,r.width=i,r.height=n},triangle:function(t,e,i,n,r){r.cx=t+i/2,r.cy=e+n/2,r.width=i,r.height=n}},Sb={};f(wb,function(t,e){Sb[e]=new t});var Mb=$r({type:"symbol",shape:{symbolType:"",x:0,y:0,width:0,height:0},beforeBrush:function(){var t=this.style,e=this.shape;"pin"===e.symbolType&&"inside"===t.textPosition&&(t.textPosition=["50%","40%"],t.textAlign="center",t.textVerticalAlign="middle")},buildPath:function(t,e,i){var n=e.symbolType,r=Sb[n];"none"!==e.symbolType&&(r||(n="rect",r=Sb[n]),bb[n](e.x,e.y,e.width,e.height,r.shape),r.buildPath(t,r.shape,i))}}),Ib={isDimensionStacked:Ph,enableDataStack:kh,getStackedDimension:Lh},Tb=(Object.freeze||Object)({createList:pu,getLayoutRect:bo,dataStack:Ib,createScale:gu,mixinAxisModelCommonMethods:vu,completeDimensions:Ch,createDimensions:Vw,createSymbol:fu}),Cb=1e-8;xu.prototype={constructor:xu,properties:null,getBoundingRect:function(){var t=this._rect;if(t)return t;for(var e=Number.MAX_VALUE,i=[e,e],n=[-e,-e],r=[],a=[],o=this.geometries,s=0;sn;n++)if("polygon"===i[n].type){var a=i[n].exterior,o=i[n].interiors;if(yu(a,t[0],t[1])){for(var s=0;s<(o?o.length:0);s++)if(yu(o[s]))continue t;return!0}}return!1},transformTo:function(t,e,i,n){var r=this.getBoundingRect(),a=r.width/r.height;i?n||(n=i/a):i=a*n;for(var o=new gi(t,e,i,n),s=r.calculateTransform(o),l=this.geometries,h=0;h0}),function(t){var e=t.properties,i=t.geometry,n=i.coordinates,r=[];"Polygon"===i.type&&r.push({type:"polygon",exterior:n[0],interiors:n.slice(1)}),"MultiPolygon"===i.type&&f(n,function(t){t[0]&&r.push({type:"polygon",exterior:t[0],interiors:t.slice(1)})});var a=new xu(e.name,r,e.cp);return a.properties=e,a})},Db=jn(),kb=[0,1],Pb=function(t,e,i){this.dim=t,this.scale=e,this._extent=i||[0,0],this.inverse=!1,this.onBand=!1};Pb.prototype={constructor:Pb,contain:function(t){var e=this._extent,i=Math.min(e[0],e[1]),n=Math.max(e[0],e[1]);return t>=i&&n>=t},containData:function(t){return this.contain(this.dataToCoord(t))},getExtent:function(){return this._extent.slice()},getPixelPrecision:function(t){return to(t||this.scale.getExtent(),this._extent)},setExtent:function(t,e){var i=this._extent;i[0]=t,i[1]=e},dataToCoord:function(t,e){var i=this._extent,n=this.scale;return t=n.normalize(t),this.onBand&&"ordinal"===n.type&&(i=i.slice(),Bu(i,n.count())),qa(t,kb,i,e)},coordToData:function(t,e){var i=this._extent,n=this.scale;this.onBand&&"ordinal"===n.type&&(i=i.slice(),Bu(i,n.count()));var r=qa(t,i,kb,e);return this.scale.scale(r)},pointToData:function(){},getTicksCoords:function(t){t=t||{};var e=t.tickModel||this.getTickModel(),i=Su(this,e),n=i.ticks,r=p(n,function(t){return{coord:this.dataToCoord(t),tickValue:t}},this),a=e.get("alignWithLabel");return Nu(this,r,i.tickCategoryInterval,a,t.clamp),r},getViewLabels:function(){return bu(this).labels},getLabelModel:function(){return this.model.getModel("axisLabel")},getTickModel:function(){return this.model.getModel("axisTick")},getBandWidth:function(){var t=this._extent,e=this.scale.getExtent(),i=e[1]-e[0]+(this.onBand?1:0);0===i&&(i=1);var n=Math.abs(t[1]-t[0]);return Math.abs(n)/i},isHorizontal:null,getRotate:null,calculateCategoryInterval:function(){return Lu(this)}};var Lb=Ab,Ob={};f(["map","each","filter","indexOf","inherits","reduce","filter","bind","curry","isArray","isString","isObject","isFunction","extend","defaults","clone","merge"],function(t){Ob[t]=pg[t]});var zb={};f(["extendShape","extendPath","makePath","makeImage","mergePath","resizePath","createIcon","setHoverStyle","setLabelStyle","setTextStyle","setText","getFont","updateProps","initProps","getTransform","clipPointsByRect","clipRectByRect","Group","Image","Text","Circle","Sector","Ring","Polygon","Polyline","Rect","Line","BezierCurve","Arc","IncrementalDisplayable","CompoundPath","LinearGradient","RadialGradient","BoundingRect"],function(t){zb[t]=Yy[t]});var Eb=function(t){this._axes={},this._dimList=[],this.name=t||""};Eb.prototype={constructor:Eb,type:"cartesian",getAxis:function(t){return this._axes[t]},getAxes:function(){return p(this._dimList,Fu,this)},getAxesByScale:function(t){return t=t.toLowerCase(),v(this.getAxes(),function(e){return e.scale.type===t})},addAxis:function(t){var e=t.dim;this._axes[e]=t,this._dimList.push(e)},dataToCoord:function(t){return this._dataCoordConvert(t,"dataToCoord")},coordToData:function(t){return this._dataCoordConvert(t,"coordToData")},_dataCoordConvert:function(t,e){for(var i=this._dimList,n=t instanceof Array?[]:{},r=0;re[1]&&e.reverse(),e},getOtherAxis:function(){this.grid.getOtherAxis()},pointToData:function(t,e){return this.coordToData(this.toLocalCoord(t["x"===this.dim?0:1]),e)},toLocalCoord:null,toGlobalCoord:null},u(Rb,Pb);var Bb={show:!0,zlevel:0,z:0,inverse:!1,name:"",nameLocation:"end",nameRotate:null,nameTruncate:{maxWidth:null,ellipsis:"...",placeholder:"."},nameTextStyle:{},nameGap:15,silent:!1,triggerEvent:!1,tooltip:{show:!1},axisPointer:{},axisLine:{show:!0,onZero:!0,onZeroAxisIndex:null,lineStyle:{color:"#333",width:1,type:"solid"},symbol:["none","none"],symbolSize:[10,15]},axisTick:{show:!0,inside:!1,length:5,lineStyle:{width:1}},axisLabel:{show:!0,inside:!1,rotate:0,showMinLabel:null,showMaxLabel:null,margin:8,fontSize:12},splitLine:{show:!0,lineStyle:{color:["#ccc"],width:1,type:"solid"}},splitArea:{show:!1,areaStyle:{color:["rgba(250,250,250,0.3)","rgba(200,200,200,0.3)"]}}},Nb={};Nb.categoryAxis=r({boundaryGap:!0,deduplication:null,splitLine:{show:!1},axisTick:{alignWithLabel:!1,interval:"auto"},axisLabel:{interval:"auto"}},Bb),Nb.valueAxis=r({boundaryGap:[0,0],splitNumber:5},Bb),Nb.timeAxis=s({scale:!0,min:"dataMin",max:"dataMax"},Nb.valueAxis),Nb.logAxis=s({scale:!0,logBase:10},Nb.valueAxis);var Fb=["value","category","time","log"],Vb=function(t,e,i,n){f(Fb,function(o){e.extend({type:t+"Axis."+o,mergeDefaultAndTheme:function(e,n){var a=this.layoutMode,s=a?Mo(e):{},l=n.getTheme();r(e,l.get(o+"Axis")),r(e,this.getDefaultOption()),e.type=i(t,e),a&&So(e,s,a)},optionUpdated:function(){var t=this.option;"category"===t.type&&(this.__ordinalMeta=Bh.createByAxisModel(this))},getCategories:function(t){var e=this.option;return"category"===e.type?t?e.data:this.__ordinalMeta.categories:void 0},getOrdinalMeta:function(){return this.__ordinalMeta},defaultOption:a([{},Nb[o+"Axis"],n],!0)})}),yx.registerSubTypeDefaulter(t+"Axis",x(i,t))},Wb=yx.extend({type:"cartesian2dAxis",axis:null,init:function(){Wb.superApply(this,"init",arguments),this.resetRange()},mergeOption:function(){Wb.superApply(this,"mergeOption",arguments),this.resetRange()},restoreData:function(){Wb.superApply(this,"restoreData",arguments),this.resetRange()},getCoordSysModel:function(){return this.ecModel.queryComponents({mainType:"grid",index:this.option.gridIndex,id:this.option.gridId})[0]}});r(Wb.prototype,vb);var Gb={offset:0};Vb("x",Wb,Wu,Gb),Vb("y",Wb,Wu,Gb),yx.extend({type:"grid",dependencies:["xAxis","yAxis"],layoutMode:"box",coordinateSystem:null,defaultOption:{show:!1,zlevel:0,z:0,left:"10%",top:60,right:"10%",bottom:60,containLabel:!1,backgroundColor:"rgba(0,0,0,0)",borderWidth:1,borderColor:"#ccc"}});var Hb=Hu.prototype;Hb.type="grid",Hb.axisPointerEnabled=!0,Hb.getRect=function(){return this._rect},Hb.update=function(t,e){var i=this._axesMap;this._updateScale(t,this.model),f(i.x,function(t){au(t.scale,t.model)}),f(i.y,function(t){au(t.scale,t.model)});var n={};f(i.x,function(t){Zu(i,"y",t,n)}),f(i.y,function(t){Zu(i,"x",t,n)}),this.resize(this.model,e)},Hb.resize=function(t,e,i){function n(){f(a,function(t){var e=t.isHorizontal(),i=e?[0,r.width]:[0,r.height],n=t.inverse?1:0;t.setExtent(i[n],i[1-n]),Yu(t,e?r.x:r.y)})}var r=bo(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()});this._rect=r;var a=this._axesList;n(),!i&&t.get("containLabel")&&(f(a,function(t){if(!t.model.get("axisLabel.inside")){var e=uu(t);if(e){var i=t.isHorizontal()?"height":"width",n=t.model.get("axisLabel.margin");r[i]-=e[i]+n,"top"===t.position?r.y+=e.height+n:"left"===t.position&&(r.x+=e.width+n)}}}),n())},Hb.getAxis=function(t,e){var i=this._axesMap[t];if(null!=i){if(null==e)for(var n in i)if(i.hasOwnProperty(n))return i[n];return i[e]}},Hb.getAxes=function(){return this._axesList.slice()},Hb.getCartesian=function(t,e){if(null!=t&&null!=e){var i="x"+t+"y"+e;return this._coordsMap[i]}S(t)&&(e=t.yAxisIndex,t=t.xAxisIndex);for(var n=0,r=this._coordsList;nt&&(t=e),t}});var Yb=dm([["fill","color"],["stroke","borderColor"],["lineWidth","borderWidth"],["stroke","barBorderColor"],["lineWidth","barBorderWidth"],["opacity"],["shadowBlur"],["shadowOffsetX"],["shadowOffsetY"],["shadowColor"]]),jb={getBarItemStyle:function(t){var e=Yb(this,t);if(this.getBorderLineDash){var i=this.getBorderLineDash();i&&(e.lineDash=i)}return e}},qb=["itemStyle","barBorderWidth"];o(Wa.prototype,jb),ah({type:"bar",render:function(t,e,i){this._updateDrawMode(t);var n=t.get("coordinateSystem");return("cartesian2d"===n||"polar"===n)&&(this._isLargeDraw?this._renderLarge(t,e,i):this._renderNormal(t,e,i)),this.group},incrementalPrepareRender:function(t){this._clear(),this._updateDrawMode(t)},incrementalRender:function(t,e){this._incrementalRenderLarge(t,e)},_updateDrawMode:function(t){var e=t.pipelineContext.large;(null==this._isLargeDraw||e^this._isLargeDraw)&&(this._isLargeDraw=e,this._clear())},_renderNormal:function(t){var e,i=this.group,n=t.getData(),r=this._data,a=t.coordinateSystem,o=a.getBaseAxis();"cartesian2d"===a.type?e=o.isHorizontal():"polar"===a.type&&(e="angle"===o.dim);var s=t.isAnimationEnabled()?t:null;n.diff(r).add(function(r){if(n.hasValue(r)){var o=n.getItemModel(r),l=$b[a.type](n,r,o),h=Ub[a.type](n,r,o,l,e,s);n.setItemGraphicEl(r,h),i.add(h),tc(h,n,r,o,l,t,e,"polar"===a.type)}}).update(function(o,l){var h=r.getItemGraphicEl(l);if(!n.hasValue(o))return void i.remove(h);var u=n.getItemModel(o),c=$b[a.type](n,o,u);h?La(h,{shape:c},s,o):h=Ub[a.type](n,o,u,c,e,s,!0),n.setItemGraphicEl(o,h),i.add(h),tc(h,n,o,u,c,t,e,"polar"===a.type)}).remove(function(t){var e=r.getItemGraphicEl(t);"cartesian2d"===a.type?e&&Qu(t,s,e):e&&Ju(t,s,e)}).execute(),this._data=n},_renderLarge:function(t){this._clear(),ic(t,this.group)},_incrementalRenderLarge:function(t,e){ic(e,this.group,!0)},dispose:V,remove:function(t){this._clear(t)},_clear:function(t){var e=this.group,i=this._data;t&&t.get("animation")&&i&&!this._isLargeDraw?i.eachItemGraphicEl(function(e){"sector"===e.type?Ju(e.dataIndex,t,e):Qu(e.dataIndex,t,e)}):e.removeAll(),this._data=null}});var Ub={cartesian2d:function(t,e,i,n,r,a,s){var l=new Dy({shape:o({},n)});if(a){var h=l.shape,u=r?"height":"width",c={};h[u]=0,c[u]=n[u],Yy[s?"updateProps":"initProps"](l,{shape:c},a,e)}return l},polar:function(t,e,i,n,r,a,o){var l=n.startAngle0?1:-1,o=n.height>0?1:-1;return{x:n.x+a*r/2,y:n.y+o*r/2,width:n.width-a*r,height:n.height-o*r}},polar:function(t,e){var i=t.getItemLayout(e);return{cx:i.cx,cy:i.cy,r0:i.r0,r:i.r,startAngle:i.startAngle,endAngle:i.endAngle}}},Kb=Fr.extend({type:"largeBar",shape:{points:[]},buildPath:function(t,e){for(var i=e.points,n=this.__startPoint,r=this.__valueIdx,a=0;ah[1]?-1:1,c=["start"===r?h[0]-u*l:"end"===r?h[1]+u*l:(h[0]+h[1])/2,uc(r)?t.labelOffset+a*l:0],d=e.get("nameRotate");null!=d&&(d=d*Qb/180);var f;uc(r)?n=eS(t.rotation,null!=d?d:t.rotation,a):(n=ac(t,r,d||0,h),f=t.axisNameAvailableWidth,null!=f&&(f=Math.abs(f/Math.sin(n.rotation)),!isFinite(f)&&(f=null)));var p=s.getFont(),g=e.get("nameTruncate",!0)||{},v=g.ellipsis,m=A(t.nameTruncateMaxWidth,g.maxWidth,f),y=null!=v&&null!=m?hx(i,m,p,v,{minChar:2,placeholder:g.placeholder}):i,x=e.get("tooltip",!0),_=e.mainType,w={componentType:_,name:i,$vars:["name"]};w[_+"Index"]=e.componentIndex;var b=new xy({anid:"name",__fullText:i,__truncatedText:y,position:c,rotation:n.rotation,silent:oc(e),z2:1,tooltip:x&&x.show?o({content:i,formatter:function(){return i},formatterParams:w},x):null});ba(b.style,s,{text:y,textFont:p,textFill:s.getTextColor()||e.get("axisLine.lineStyle.color"),textAlign:n.textAlign,textVerticalAlign:n.textVerticalAlign}),e.get("triggerEvent")&&(b.eventData=rc(e),b.eventData.targetType="axisName",b.eventData.name=i),this._dumbGroup.add(b),b.updateTransform(),this.group.add(b),b.decomposeTransform()}}},eS=Jb.innerTextLayout=function(t,e,i){var n,r,a=io(e-t);return no(a)?(r=i>0?"top":"bottom",n="center"):no(a-Qb)?(r=i>0?"bottom":"top",n="center"):(r="middle",n=a>0&&Qb>a?i>0?"right":"left":i>0?"left":"right"),{rotation:a,textAlign:n,textVerticalAlign:r}},iS=f,nS=x,rS=nh({type:"axis",_axisPointer:null,axisPointerClass:null,render:function(t,e,i,n){this.axisPointerClass&&xc(t),rS.superApply(this,"render",arguments),Mc(this,t,e,i,n,!0)},updateAxisPointer:function(t,e,i,n){Mc(this,t,e,i,n,!1)},remove:function(t,e){var i=this._axisPointer;i&&i.remove(e),rS.superApply(this,"remove",arguments)},dispose:function(t,e){Ic(this,e),rS.superApply(this,"dispose",arguments)}}),aS=[];rS.registerAxisPointerClass=function(t,e){aS[t]=e},rS.getAxisPointerClass=function(t){return t&&aS[t]};var oS=["axisLine","axisTickLabel","axisName"],sS=["splitArea","splitLine"],lS=rS.extend({type:"cartesianAxis",axisPointerClass:"CartesianAxisPointer",render:function(t,e,i,n){this.group.removeAll();var r=this._axisGroup;if(this._axisGroup=new lv,this.group.add(this._axisGroup),t.get("show")){var a=t.getCoordSysModel(),o=Tc(a,t),s=new Jb(t,o);f(oS,s.add,s),this._axisGroup.add(s.getGroup()),f(sS,function(e){t.get(e+".show")&&this["_"+e](t,a)},this),Ba(r,this._axisGroup,t),lS.superCall(this,"render",t,e,i,n)}},remove:function(){this._splitAreaColors=null},_splitLine:function(t,e){var i=t.axis;if(!i.scale.isBlank()){var n=t.getModel("splitLine"),r=n.getModel("lineStyle"),a=r.get("color");a=_(a)?a:[a];for(var o=e.coordinateSystem.getRect(),l=i.isHorizontal(),h=0,u=i.getTicksCoords({tickModel:n}),c=[],d=[],f=r.getLineStyle(),p=0;p0&&Gc(i[r-1]);r--);for(;r>n&&Gc(i[n]);n++);}for(;r>n;)n+=Hc(t,i,n,r,r,1,a.min,a.max,e.smooth,e.smoothMonotone,e.connectNulls)+1}}),IS=Fr.extend({type:"ec-polygon",shape:{points:[],stackedOnPoints:[],smooth:0,stackedOnSmooth:0,smoothConstraint:!0,smoothMonotone:null,connectNulls:!1},brush:by(Fr.prototype.brush),buildPath:function(t,e){var i=e.points,n=e.stackedOnPoints,r=0,a=i.length,o=e.smoothMonotone,s=Yc(i,e.smoothConstraint),l=Yc(n,e.smoothConstraint);if(e.connectNulls){for(;a>0&&Gc(i[a-1]);a--);for(;a>r&&Gc(i[r]);r++);}for(;a>r;){var h=Hc(t,i,r,a,a,1,s.min,s.max,e.smooth,o,e.connectNulls);Hc(t,n,r+h-1,h,a,-1,l.min,l.max,e.stackedOnSmooth,o,e.connectNulls),r+=h+1,t.closePath()}}});Bs.extend({type:"line",init:function(){var t=new lv,e=new zc;this.group.add(e.group),this._symbolDraw=e,this._lineGroup=t},render:function(t,e,i){var n=t.coordinateSystem,r=this.group,a=t.getData(),o=t.getModel("lineStyle"),l=t.getModel("areaStyle"),h=a.mapArray(a.getItemLayout),u="polar"===n.type,c=this._coordSys,d=this._symbolDraw,f=this._polyline,p=this._polygon,g=this._lineGroup,v=t.get("animation"),m=!l.isEmpty(),y=l.get("origin"),x=Nc(n,a,y),_=$c(n,a,x),w=t.get("showSymbol"),b=w&&!u&&id(t,a,n),S=this._data;S&&S.eachItemGraphicEl(function(t,e){t.__temp&&(r.remove(t),S.setItemGraphicEl(e,null))}),w||d.remove(),r.add(g);var M=!u&&t.get("step");f&&c.type===n.type&&M===this._step?(m&&!p?p=this._newPolygon(h,_,n,v):p&&!m&&(g.remove(p),p=this._polygon=null),g.setClipPath(Jc(n,!1,!1,t)),w&&d.updateData(a,{isIgnore:b,clipShape:Jc(n,!1,!0,t)}),a.eachItemGraphicEl(function(t){t.stopAnimation(!0)}),jc(this._stackedOnPoints,_)&&jc(this._points,h)||(v?this._updateAnimation(a,_,n,i,M,y):(M&&(h=td(h,n,M),_=td(_,n,M)),f.setShape({points:h}),p&&p.setShape({points:h,stackedOnPoints:_})))):(w&&d.updateData(a,{isIgnore:b,clipShape:Jc(n,!1,!0,t)}),M&&(h=td(h,n,M),_=td(_,n,M)),f=this._newPolyline(h,n,v),m&&(p=this._newPolygon(h,_,n,v)),g.setClipPath(Jc(n,!0,!1,t)));var I=ed(a,n)||a.getVisual("color");f.useStyle(s(o.getLineStyle(),{fill:"none",stroke:I,lineJoin:"bevel"}));var T=t.get("smooth");if(T=qc(t.get("smooth")),f.setShape({smooth:T,smoothMonotone:t.get("smoothMonotone"),connectNulls:t.get("connectNulls")}),p){var C=a.getCalculationInfo("stackedOnSeries"),A=0;p.useStyle(s(l.getAreaStyle(),{fill:I,opacity:.7,lineJoin:"bevel"})),C&&(A=qc(C.get("smooth"))),p.setShape({smooth:T,stackedOnSmooth:A,smoothMonotone:t.get("smoothMonotone"),connectNulls:t.get("connectNulls")})}this._data=a,this._coordSys=n,this._stackedOnPoints=_,this._points=h,this._step=M,this._valueOrigin=y},dispose:function(){},highlight:function(t,e,i,n){var r=t.getData(),a=Yn(r,n);if(!(a instanceof Array)&&null!=a&&a>=0){var o=r.getItemGraphicEl(a);if(!o){var s=r.getItemLayout(a);if(!s)return;o=new Cc(r,a),o.position=s,o.setZ(t.get("zlevel"),t.get("z")),o.ignore=isNaN(s[0])||isNaN(s[1]),o.__temp=!0,r.setItemGraphicEl(a,o),o.stopSymbolAnimation(!0),this.group.add(o)}o.highlight()}else Bs.prototype.highlight.call(this,t,e,i,n)},downplay:function(t,e,i,n){var r=t.getData(),a=Yn(r,n);if(null!=a&&a>=0){var o=r.getItemGraphicEl(a);o&&(o.__temp?(r.setItemGraphicEl(a,null),this.group.remove(o)):o.downplay())}else Bs.prototype.downplay.call(this,t,e,i,n)},_newPolyline:function(t){var e=this._polyline;return e&&this._lineGroup.remove(e),e=new MS({shape:{points:t},silent:!0,z2:10}),this._lineGroup.add(e),this._polyline=e,e},_newPolygon:function(t,e){var i=this._polygon;return i&&this._lineGroup.remove(i),i=new IS({shape:{points:t,stackedOnPoints:e},silent:!0}),this._lineGroup.add(i),this._polygon=i,i},_updateAnimation:function(t,e,i,n,r,a){var o=this._polyline,s=this._polygon,l=t.hostModel,h=vS(this._data,t,this._stackedOnPoints,e,this._coordSys,i,this._valueOrigin,a),u=h.current,c=h.stackedOnCurrent,d=h.next,f=h.stackedOnNext;r&&(u=td(h.current,i,r),c=td(h.stackedOnCurrent,i,r),d=td(h.next,i,r),f=td(h.stackedOnNext,i,r)),o.shape.__points=h.current,o.shape.points=u,La(o,{shape:{points:d}},l),s&&(s.setShape({points:u,stackedOnPoints:c}),La(s,{shape:{points:d,stackedOnPoints:f}},l));for(var p=[],g=h.status,v=0;ve&&(e=t[i]);return isFinite(e)?e:0/0},min:function(t){for(var e=1/0,i=0;i1){var h;"string"==typeof i?h=AS[i]:"function"==typeof i&&(h=i),h&&t.setData(e.downSample(e.mapDimension(a.dim),1/l,h,DS))}}}}};Jl(TS("line","circle","line")),Ql(CS("line")),jl(ow.PROCESSOR.STATISTIC,kS("line"));var PS=function(t,e,i){e=_(e)&&{coordDimensions:e}||o({},e);var n=t.getSource(),r=Vw(n,e),a=new Bw(r,t);return a.initData(n,i),a},LS={updateSelectedMap:function(t){this._targetList=_(t)?t.slice():[],this._selectTargetMap=g(t||[],function(t,e){return t.set(e.name,e),t},N())},select:function(t,e){var i=null!=e?this._targetList[e]:this._selectTargetMap.get(t),n=this.get("selectedMode");"single"===n&&this._selectTargetMap.each(function(t){t.selected=!1}),i&&(i.selected=!0)},unSelect:function(t,e){var i=null!=e?this._targetList[e]:this._selectTargetMap.get(t);i&&(i.selected=!1)},toggleSelected:function(t,e){var i=null!=e?this._targetList[e]:this._selectTargetMap.get(t);return null!=i?(this[i.selected?"unSelect":"select"](t,e),i.selected):void 0},isSelected:function(t,e){var i=null!=e?this._targetList[e]:this._selectTargetMap.get(t);return i&&i.selected}},OS=rh({type:"series.pie",init:function(t){OS.superApply(this,"init",arguments),this.legendDataProvider=function(){return this.getRawData()},this.updateSelectedMap(this._createSelectableList()),this._defaultLabelLine(t)},mergeOption:function(t){OS.superCall(this,"mergeOption",t),this.updateSelectedMap(this._createSelectableList())},getInitialData:function(){return PS(this,["value"])},_createSelectableList:function(){for(var t=this.getRawData(),e=t.mapDimension("value"),i=[],n=0,r=t.count();r>n;n++)i.push({name:t.getName(n),value:t.get(e,n),selected:Ms(t,n,"selected")});return i},getDataParams:function(t){var e=this.getData(),i=OS.superCall(this,"getDataParams",t),n=[];return e.each(e.mapDimension("value"),function(t){n.push(t)}),i.percent=eo(n,t,e.hostModel.get("percentPrecision")),i.$vars.push("percent"),i},_defaultLabelLine:function(t){Fn(t,"labelLine",["show"]);var e=t.labelLine,i=t.emphasis.labelLine;e.show=e.show&&t.label.show,i.show=i.show&&t.emphasis.label.show},defaultOption:{zlevel:0,z:2,legendHoverLink:!0,hoverAnimation:!0,center:["50%","50%"],radius:[0,"75%"],clockwise:!0,startAngle:90,minAngle:0,selectedOffset:10,hoverOffset:10,avoidLabelOverlap:!0,percentPrecision:2,stillShowZeroSum:!0,label:{rotate:!1,show:!0,position:"outer"},labelLine:{show:!0,length:15,length2:15,smooth:!1,lineStyle:{width:1,type:"solid"}},itemStyle:{borderWidth:1},animationType:"expansion",animationEasing:"cubicOut"}});c(OS,LS);var zS=od.prototype;zS.updateData=function(t,e,i){function n(){a.stopAnimation(!0),a.animateTo({shape:{r:u.r+l.get("hoverOffset")}},300,"elasticOut")}function r(){a.stopAnimation(!0),a.animateTo({shape:{r:u.r}},300,"elasticOut")}var a=this.childAt(0),l=t.hostModel,h=t.getItemModel(e),u=t.getItemLayout(e),c=o({},u);if(c.label=null,i){a.setShape(c);var d=l.getShallow("animationType");"scale"===d?(a.shape.r=u.r0,Oa(a,{shape:{r:u.r}},l,e)):(a.shape.endAngle=u.startAngle,La(a,{shape:{endAngle:u.endAngle}},l,e))}else La(a,{shape:c},l,e);var f=t.getItemVisual(e,"color");a.useStyle(s({lineJoin:"bevel",fill:f},h.getModel("itemStyle").getItemStyle())),a.hoverStyle=h.getModel("emphasis.itemStyle").getItemStyle();var p=h.getShallow("cursor");p&&a.attr("cursor",p),ad(this,t.getItemLayout(e),l.isSelected(null,e),l.get("selectedOffset"),l.get("animation")),a.off("mouseover").off("mouseout").off("emphasis").off("normal"),h.get("hoverAnimation")&&l.isAnimationEnabled()&&a.on("mouseover",n).on("mouseout",r).on("emphasis",n).on("normal",r),this._updateLabel(t,e),xa(this)},zS._updateLabel=function(t,e){var i=this.childAt(1),n=this.childAt(2),r=t.hostModel,a=t.getItemModel(e),o=t.getItemLayout(e),s=o.label,l=t.getItemVisual(e,"color");La(i,{shape:{points:s.linePoints||[[s.x,s.y],[s.x,s.y],[s.x,s.y]]}},r,e),La(n,{style:{x:s.x,y:s.y}},r,e),n.attr({rotation:s.rotation,origin:[s.x,s.y],z2:10});var h=a.getModel("label"),u=a.getModel("emphasis.label"),c=a.getModel("labelLine"),d=a.getModel("emphasis.labelLine"),l=t.getItemVisual(e,"color");wa(n.style,n.hoverStyle={},h,u,{labelFetcher:t.hostModel,labelDataIndex:e,defaultText:t.getName(e),autoColor:l,useInsideStyle:!!s.inside},{textAlign:s.textAlign,textVerticalAlign:s.verticalAlign,opacity:t.getItemVisual(e,"opacity")}),n.ignore=n.normalIgnore=!h.get("show"),n.hoverIgnore=!u.get("show"),i.ignore=i.normalIgnore=!c.get("show"),i.hoverIgnore=!d.get("show"),i.setStyle({stroke:l,opacity:t.getItemVisual(e,"opacity")}),i.setStyle(c.getModel("lineStyle").getLineStyle()),i.hoverStyle=d.getModel("lineStyle").getLineStyle();var f=c.get("smooth");f&&f===!0&&(f=.4),i.setShape({smooth:f})},u(od,lv);var ES=(Bs.extend({type:"pie",init:function(){var t=new lv;this._sectorGroup=t},render:function(t,e,i,n){if(!n||n.from!==this.uid){var r=t.getData(),a=this._data,o=this.group,s=e.get("animation"),l=!a,h=t.get("animationType"),u=x(rd,this.uid,t,s,i),c=t.get("selectedMode");if(r.diff(a).add(function(t){var e=new od(r,t);l&&"scale"!==h&&e.eachChild(function(t){t.stopAnimation(!0)}),c&&e.on("click",u),r.setItemGraphicEl(t,e),o.add(e)}).update(function(t,e){var i=a.getItemGraphicEl(e);i.updateData(r,t),i.off("click"),c&&i.on("click",u),o.add(i),r.setItemGraphicEl(t,i)}).remove(function(t){var e=a.getItemGraphicEl(t);o.remove(e)}).execute(),s&&l&&r.count()>0&&"scale"!==h){var d=r.getItemLayout(0),f=Math.max(i.getWidth(),i.getHeight())/2,p=y(o.removeClipPath,o);o.setClipPath(this._createClipPath(d.cx,d.cy,f,d.startAngle,d.clockwise,p,t))}else o.removeClipPath();this._data=r}},dispose:function(){},_createClipPath:function(t,e,i,n,r,a,o){var s=new Sy({shape:{cx:t,cy:e,r0:0,r:i,startAngle:n,endAngle:n,clockwise:r}});return Oa(s,{shape:{endAngle:n+(r?1:-1)*Math.PI*2}},o,a),s},containPoint:function(t,e){var i=e.getData(),n=i.getItemLayout(0);if(n){var r=t[0]-n.cx,a=t[1]-n.cy,o=Math.sqrt(r*r+a*a);return o<=n.r&&o>=n.r0}}}),function(t,e){f(e,function(e){e.update="updateView",Ul(e,function(i,n){var r={};return n.eachComponent({mainType:"series",subType:t,query:i},function(t){t[e.method]&&t[e.method](i.name,i.dataIndex);var n=t.getData();n.each(function(e){var i=n.getName(e);r[i]=t.isSelected(i)||!1})}),{name:i.name,selected:r}})})}),RS=function(t){return{getTargetSeries:function(e){var i={},n=N();return e.eachSeriesByType(t,function(t){t.__paletteScope=i,n.set(t.uid,t)}),n},reset:function(t){var e=t.getRawData(),i={},n=t.getData();n.each(function(t){var e=n.getRawIndex(t);i[e]=t}),e.each(function(r){var a=i[r],o=null!=a&&n.getItemVisual(a,"color",!0);if(o)e.setItemVisual(r,"color",o);else{var s=e.getItemModel(r),l=s.get("itemStyle.color")||t.getColorFromPalette(e.getName(r)||r+"",t.__paletteScope,e.count());e.setItemVisual(r,"color",l),null!=a&&n.setItemVisual(a,"color",l)}})}}},BS=function(t,e,i,n){var r,a,o=t.getData(),s=[],l=!1;o.each(function(i){var n,h,u,c,d=o.getItemLayout(i),f=o.getItemModel(i),p=f.getModel("label"),g=p.get("position")||f.get("emphasis.label.position"),v=f.getModel("labelLine"),m=v.get("length"),y=v.get("length2"),x=(d.startAngle+d.endAngle)/2,_=Math.cos(x),w=Math.sin(x);r=d.cx,a=d.cy;var b="inside"===g||"inner"===g;if("center"===g)n=d.cx,h=d.cy,c="center";else{var S=(b?(d.r+d.r0)/2*_:d.r*_)+r,M=(b?(d.r+d.r0)/2*w:d.r*w)+a;if(n=S+3*_,h=M+3*w,!b){var I=S+_*(m+e-d.r),T=M+w*(m+e-d.r),C=I+(0>_?-1:1)*y,A=T;n=C+(0>_?-5:5),h=A,u=[[S,M],[I,T],[C,A]]}c=b?"center":_>0?"left":"right"}var D=p.getFont(),k=p.get("rotate")?0>_?-x+Math.PI:-x:0,P=t.getFormattedLabel(i,"normal")||o.getName(i),L=Ei(P,D,c,"top");l=!!k,d.label={x:n,y:h,position:g,height:L.height,len:m,len2:y,linePoints:u,textAlign:c,verticalAlign:"middle",rotation:k,inside:b},b||s.push(d.label)}),!l&&t.get("avoidLabelOverlap")&&ld(s,r,a,e,i,n)},NS=2*Math.PI,FS=Math.PI/180,VS=function(t,e,i){e.eachSeriesByType(t,function(t){var e=t.getData(),n=e.mapDimension("value"),r=t.get("center"),a=t.get("radius");_(a)||(a=[0,a]),_(r)||(r=[r,r]);var o=i.getWidth(),s=i.getHeight(),l=Math.min(o,s),h=Ua(r[0],o),u=Ua(r[1],s),c=Ua(a[0],l/2),d=Ua(a[1],l/2),f=-t.get("startAngle")*FS,p=t.get("minAngle")*FS,g=0;e.each(n,function(t){!isNaN(t)&&g++});var v=e.getSum(n),m=Math.PI/(v||g)*2,y=t.get("clockwise"),x=t.get("roseType"),w=t.get("stillShowZeroSum"),b=e.getDataExtent(n);b[0]=0;var S=NS,M=0,I=f,T=y?1:-1;if(e.each(n,function(t,i){var n;if(isNaN(t))return void e.setItemLayout(i,{angle:0/0,startAngle:0/0,endAngle:0/0,clockwise:y,cx:h,cy:u,r0:c,r:x?0/0:d});n="area"!==x?0===v&&w?m:t*m:NS/g,p>n?(n=p,S-=p):M+=t;var r=I+T*n;e.setItemLayout(i,{angle:n,startAngle:I,endAngle:r,clockwise:y,cx:h,cy:u,r0:c,r:x?qa(t,b,[c,d]):d}),I=r}),NS>S&&g)if(.001>=S){var C=NS/g;e.each(n,function(t,i){if(!isNaN(t)){var n=e.getItemLayout(i);n.angle=C,n.startAngle=f+T*i*C,n.endAngle=f+T*(i+1)*C}})}else m=S/M,I=f,e.each(n,function(t,i){if(!isNaN(t)){var n=e.getItemLayout(i),r=n.angle===p?p:t*m;n.startAngle=I,n.endAngle=I+T*r,I+=T*r}});BS(t,d,o,s)})},WS=function(t){return{seriesType:t,reset:function(t,e){var i=e.findComponents({mainType:"legend"});if(i&&i.length){var n=t.getData();n.filterSelf(function(t){for(var e=n.getName(t),r=0;rn[1]&&n.reverse(),{coordSys:{type:"polar",cx:t.cx,cy:t.cy,r:n[1],r0:n[0]},api:{coord:y(function(n){var r=e.dataToRadius(n[0]),a=i.dataToAngle(n[1]),o=t.coordToPoint([r,a]);return o.push(r,a*Math.PI/180),o}),size:y(dd,t)}}},YS=function(t){var e=t.getRect(),i=t.getRangeInfo();return{coordSys:{type:"calendar",x:e.x,y:e.y,width:e.width,height:e.height,cellWidth:t.getCellWidth(),cellHeight:t.getCellHeight(),rangeInfo:{start:i.start,end:i.end,weeks:i.weeks,dayCount:i.allDay}},api:{coord:function(e,i){return t.dataToPoint(e,i)}}}},jS=["itemStyle"],qS=["emphasis","itemStyle"],US=["label"],$S=["emphasis","label"],KS="e\x00\x00",QS={cartesian2d:GS,geo:HS,singleAxis:ZS,polar:XS,calendar:YS};o_.extend({type:"series.custom",dependencies:["grid","polar","geo","singleAxis","calendar"],defaultOption:{coordinateSystem:"cartesian2d",zlevel:0,z:2,legendHoverLink:!0,useTransform:!0},getInitialData:function(){return Oh(this.getSource(),this)},getDataParams:function(t,e,i){var n=o_.prototype.getDataParams.apply(this,arguments);return i&&(n.info=i.info),n}}),Bs.extend({type:"custom",_data:null,render:function(t,e,i,n){var r=this._data,a=t.getData(),o=this.group,s=vd(t,a,e,i);a.diff(r).add(function(e){yd(null,e,s(e,n),t,o,a)}).update(function(e,i){var l=r.getItemGraphicEl(i);yd(l,e,s(e,n),t,o,a)}).remove(function(t){var e=r.getItemGraphicEl(t);e&&o.remove(e)}).execute(),this._data=a},incrementalPrepareRender:function(){this.group.removeAll(),this._data=null},incrementalRender:function(t,e,i,n,r){function a(t){t.isGroup||(t.incremental=!0,t.useHoverLayer=!0)}for(var o=e.getData(),s=vd(e,o,i,n),l=t.start;l=0},defaultOption:{zlevel:0,z:4,show:!0,orient:"horizontal",left:"center",top:0,align:"auto",backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderRadius:0,borderWidth:0,padding:5,itemGap:10,itemWidth:25,itemHeight:14,inactiveColor:"#ccc",textStyle:{color:"#333"},selectedMode:!0,tooltip:{show:!1}}});Ul("legendToggleSelect","legendselectchanged",x(Ad,"toggleSelected")),Ul("legendSelect","legendselected",x(Ad,"select")),Ul("legendUnSelect","legendunselected",x(Ad,"unSelect"));var tM=x,eM=f,iM=lv,nM=nh({type:"legend.plain",newlineDisabled:!1,init:function(){this.group.add(this._contentGroup=new iM),this._backgroundEl},getContentGroup:function(){return this._contentGroup},render:function(t,e,i){if(this.resetInner(),t.get("show",!0)){var n=t.get("align");n&&"auto"!==n||(n="right"===t.get("left")&&"vertical"===t.get("orient")?"right":"left"),this.renderInner(n,t,e,i);var r=t.getBoxLayoutParams(),a={width:i.getWidth(),height:i.getHeight()},o=t.get("padding"),l=bo(r,a,o),h=this.layoutInner(t,n,l),u=bo(s({width:h.width,height:h.height},r),a,o);this.group.attr("position",[u.x-h.x,u.y-h.y]),this.group.add(this._backgroundEl=Dd(h,t))}},resetInner:function(){this.getContentGroup().removeAll(),this._backgroundEl&&this.group.remove(this._backgroundEl)},renderInner:function(t,e,i,n){var r=this.getContentGroup(),a=N(),o=e.get("selectedMode"),s=[];i.eachRawSeries(function(t){!t.get("legendHoverLink")&&s.push(t.id)}),eM(e.getData(),function(l,h){var u=l.get("name");if(!this.newlineDisabled&&(""===u||"\n"===u))return void r.add(new iM({newline:!0}));var c=i.getSeriesByName(u)[0];if(!a.get(u))if(c){var d=c.getData(),f=d.getVisual("color");"function"==typeof f&&(f=f(c.getDataParams(0)));var p=d.getVisual("legendSymbol")||"roundRect",g=d.getVisual("symbol"),v=this._createItem(u,h,l,e,p,g,t,f,o);v.on("click",tM(kd,u,n)).on("mouseover",tM(Pd,c.name,null,n,s)).on("mouseout",tM(Ld,c.name,null,n,s)),a.set(u,!0)}else i.eachRawSeries(function(i){if(!a.get(u)&&i.legendDataProvider){var r=i.legendDataProvider(),c=r.indexOfName(u);if(0>c)return;var d=r.getItemVisual(c,"color"),f="roundRect",p=this._createItem(u,h,l,e,f,null,t,d,o);p.on("click",tM(kd,u,n)).on("mouseover",tM(Pd,null,u,n,s)).on("mouseout",tM(Ld,null,u,n,s)),a.set(u,!0)}},this)},this)},_createItem:function(t,e,i,n,r,a,s,l,h){var u=n.get("itemWidth"),c=n.get("itemHeight"),d=n.get("inactiveColor"),f=n.get("symbolKeepAspect"),p=n.isSelected(t),g=new iM,v=i.getModel("textStyle"),m=i.get("icon"),y=i.getModel("tooltip"),x=y.parentModel;if(r=m||r,g.add(fu(r,0,0,u,c,p?l:d,null==f?!0:f)),!m&&a&&(a!==r||"none"===a)){var _=.8*c;"none"===a&&(a="circle"),g.add(fu(a,(u-_)/2,(c-_)/2,_,_,p?l:d,null==f?!0:f))}var w="left"===s?u+5:-5,b=s,S=n.get("formatter"),M=t;"string"==typeof S&&S?M=S.replace("{name}",null!=t?t:""):"function"==typeof S&&(M=S(t)),g.add(new xy({style:ba({},v,{text:M,x:w,y:c/2,textFill:p?v.getTextColor():d,textAlign:b,textVerticalAlign:"middle"})}));var I=new Dy({shape:g.getBoundingRect(),invisible:!0,tooltip:y.get("show")?o({content:t,formatter:x.get("formatter",!0)||function(){return t},formatterParams:{componentType:"legend",legendIndex:n.componentIndex,name:t,$vars:["name"]}},y.option):null});return g.add(I),g.eachChild(function(t){t.silent=!0}),I.silent=!h,this.getContentGroup().add(g),xa(g),g.__legendDataIndex=e,g},layoutInner:function(t,e,i){var n=this.getContentGroup();gx(t.get("orient"),n,t.get("itemGap"),i.width,i.height);var r=n.getBoundingRect();return n.attr("position",[-r.x,-r.y]),this.group.getBoundingRect()}}),rM=function(t){var e=t.findComponents({mainType:"legend"});e&&e.length&&t.filterSeries(function(t){for(var i=0;ii[s],f=[-u.x,-u.y];f[o]=n.position[o];var p=[0,0],g=[-c.x,-c.y],v=D(t.get("pageButtonGap",!0),t.get("itemGap",!0));if(d){var m=t.get("pageButtonPosition",!0);"end"===m?g[o]+=i[s]-c[s]:p[o]+=c[s]+v}g[1-o]+=u[l]/2-c[l]/2,n.attr("position",f),r.attr("position",p),a.attr("position",g);var y=this.group.getBoundingRect(),y={x:0,y:0};if(y[s]=d?i[s]:u[s],y[l]=Math.max(u[l],c[l]),y[h]=Math.min(0,c[h]+g[1-o]),r.__rectSize=i[s],d){var x={x:0,y:0};x[s]=Math.max(i[s]-c[s]-v,0),x[l]=y[l],r.setClipPath(new Dy({shape:x})),r.__rectSize=x[s]}else a.eachChild(function(t){t.attr({invisible:!0,silent:!0})});var _=this._getPageInfo(t);return null!=_.pageIndex&&La(n,{position:_.contentPosition},d?t:!1),this._updatePageInfoView(t,_),y},_pageGo:function(t,e,i){var n=this._getPageInfo(e)[t];null!=n&&i.dispatchAction({type:"legendScroll",scrollDataIndex:n,legendId:e.id})},_updatePageInfoView:function(t,e){var i=this._controllerGroup;f(["pagePrev","pageNext"],function(n){var r=null!=e[n+"DataIndex"],a=i.childOfName(n);a&&(a.setStyle("fill",r?t.get("pageIconColor",!0):t.get("pageIconInactiveColor",!0)),a.cursor=r?"pointer":"default")});var n=i.childOfName("pageText"),r=t.get("pageFormatter"),a=e.pageIndex,o=null!=a?a+1:0,s=e.pageCount;n&&r&&n.setStyle("text",b(r)?r.replace("{current}",o).replace("{total}",s):r({current:o,total:s}))},_getPageInfo:function(t){function e(t){var e=t.getBoundingRect().clone();return e[f]+=t.position[u],e}var i,n,r,a,o=t.get("scrollDataIndex",!0),s=this.getContentGroup(),l=s.getBoundingRect(),h=this._containerGroup.__rectSize,u=t.getOrient().index,c=sM[u],d=sM[1-u],f=lM[u],p=s.position.slice();this._showController?s.eachChild(function(t){t.__legendDataIndex===o&&(a=t)}):a=s.childAt(0);var g=h?Math.ceil(l[c]/h):0;if(a){var v=a.getBoundingRect(),m=a.position[u]+v[f];p[u]=-m-l[f],i=Math.floor(g*(m+v[f]+h/2)/l[c]),i=l[c]&&g?Math.max(0,Math.min(g-1,i)):-1;var y={x:0,y:0};y[c]=h,y[d]=l[d],y[f]=-p[u]-l[f];var x,_=s.children();if(s.eachChild(function(t,i){var n=e(t);n.intersect(y)&&(null==x&&(x=i),r=t.__legendDataIndex),i===_.length-1&&n[f]+n[c]<=y[f]+y[c]&&(r=null)}),null!=x){var w=_[x],b=e(w);if(y[f]=b[f]+b[c]-y[c],0>=x&&b[f]>=y[f])n=null;else{for(;x>0&&e(_[x-1]).intersect(y);)x--;n=_[x].__legendDataIndex}}}return{contentPosition:p,pageIndex:i,pageCount:g,pagePrevDataIndex:n,pageNextDataIndex:r}}});Ul("legendScroll","legendscroll",function(t,e){var i=t.scrollDataIndex;null!=i&&e.eachComponent({mainType:"legend",subType:"scroll",query:t},function(t){t.setScrollDataIndex(i)})});var uM=function(t,e){var i,n=[],r=t.seriesIndex;if(null==r||!(i=e.getSeriesByIndex(r)))return{point:[]};var a=i.getData(),o=Yn(a,t);if(null==o||0>o||_(o))return{point:[]};var s=a.getItemGraphicEl(o),l=i.coordinateSystem;if(i.getTooltipPosition)n=i.getTooltipPosition(o)||[];else if(l&&l.dataToPoint)n=l.dataToPoint(a.getValues(p(l.dimensions,function(t){return a.mapDimension(t)}),o,!0))||[];else if(s){var h=s.getBoundingRect().clone();h.applyTransform(s.transform),n=[h.x+h.width/2,h.y+h.height/2]}return{point:n,el:s}},cM=f,dM=x,fM=jn(),pM=function(t,e,i){var n=t.currTrigger,r=[t.x,t.y],a=t,o=t.dispatchAction||y(i.dispatchAction,i),s=e.getComponent("axisPointer").coordSysAxesInfo;if(s){Hd(r)&&(r=uM({seriesIndex:a.seriesIndex,dataIndex:a.dataIndex},e).point);var l=Hd(r),h=a.axesInfo,u=s.axesInfo,c="leave"===n||Hd(r),d={},f={},p={list:[],map:{}},g={showPointer:dM(Rd,f),showTooltip:dM(Bd,p)};cM(s.coordSysMap,function(t,e){var i=l||t.containPoint(r);cM(s.coordSysAxesInfo[e],function(t){var e=t.axis,n=Wd(h,t);if(!c&&i&&(!h||n)){var a=n&&n.value;null!=a||l||(a=e.pointToData(r)),null!=a&&zd(t,a,g,!1,d)}})});var v={};return cM(u,function(t,e){var i=t.linkGroup;i&&!f[e]&&cM(i.axesInfo,function(e,n){var r=f[n];if(e!==t&&r){var a=r.value;i.mapper&&(a=t.axis.scale.parse(i.mapper(a,Gd(e),Gd(t)))),v[t.key]=a}})}),cM(v,function(t,e){zd(u[e],t,g,!0,d)}),Nd(f,u,d),Fd(p,r,t,o),Vd(u,o,i),d}},gM=(ih({type:"axisPointer",coordSysAxesInfo:null,defaultOption:{show:"auto",triggerOn:null,zlevel:0,z:50,type:"line",snap:!1,triggerTooltip:!0,value:null,status:null,link:[],animation:null,animationDurationUpdate:200,lineStyle:{color:"#aaa",width:1,type:"solid"},shadowStyle:{color:"rgba(150,150,150,0.3)"},label:{show:!0,formatter:null,precision:"auto",margin:3,color:"#fff",padding:[5,7,5,7],backgroundColor:"auto",borderColor:null,borderWidth:0,shadowBlur:3,shadowColor:"#aaa"},handle:{show:!1,icon:"M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z",size:45,margin:50,color:"#333",shadowBlur:3,shadowColor:"#aaa",shadowOffsetX:0,shadowOffsetY:2,throttle:40}}}),jn()),vM=f,mM=nh({type:"axisPointer",render:function(t,e,i){var n=e.getComponent("tooltip"),r=t.get("triggerOn")||n&&n.get("triggerOn")||"mousemove|click";Zd("axisPointer",i,function(t,e,i){"none"!==r&&("leave"===t||r.indexOf(t)>=0)&&i({type:"updateAxisPointer",currTrigger:t,x:e&&e.offsetX,y:e&&e.offsetY})})},remove:function(t,e){$d(e.getZr(),"axisPointer"),mM.superApply(this._model,"remove",arguments)},dispose:function(t,e){$d("axisPointer",e),mM.superApply(this._model,"dispose",arguments)}}),yM=jn(),xM=n,_M=y;Kd.prototype={_group:null,_lastGraphicKey:null,_handle:null,_dragging:!1,_lastValue:null,_lastStatus:null,_payloadInfo:null,animationThreshold:15,render:function(t,e,i,n){var r=e.get("value"),a=e.get("status");if(this._axisModel=t,this._axisPointerModel=e,this._api=i,n||this._lastValue!==r||this._lastStatus!==a){this._lastValue=r,this._lastStatus=a;var o=this._group,s=this._handle;if(!a||"hide"===a)return o&&o.hide(),void(s&&s.hide());o&&o.show(),s&&s.show();var l={};this.makeElOption(l,r,t,e,i);var h=l.graphicKey;h!==this._lastGraphicKey&&this.clear(i),this._lastGraphicKey=h;var u=this._moveAnimation=this.determineAnimation(t,e);if(o){var c=x(Qd,e,u);this.updatePointerEl(o,l,c,e),this.updateLabelEl(o,l,c,e)}else o=this._group=new lv,this.createPointerEl(o,l,t,e),this.createLabelEl(o,l,t,e),i.getZr().add(o);nf(o,e,!0),this._renderHandle(r)}},remove:function(t){this.clear(t)},dispose:function(t){this.clear(t)},determineAnimation:function(t,e){var i=e.get("animation"),n=t.axis,r="category"===n.type,a=e.get("snap");if(!a&&!r)return!1;if("auto"===i||null==i){var o=this.animationThreshold;if(r&&n.getBandWidth()>o)return!0;if(a){var s=_c(t).seriesDataCount,l=n.getExtent();return Math.abs(l[0]-l[1])/s>o}return!1}return i===!0},makeElOption:function(){},createPointerEl:function(t,e){var i=e.pointer;if(i){var n=yM(t).pointerEl=new Yy[i.type](xM(e.pointer));t.add(n)}},createLabelEl:function(t,e,i,n){if(e.label){var r=yM(t).labelEl=new Dy(xM(e.label));t.add(r),tf(r,n)}},updatePointerEl:function(t,e,i){var n=yM(t).pointerEl;n&&(n.setStyle(e.pointer.style),i(n,{shape:e.pointer.shape}))},updateLabelEl:function(t,e,i,n){var r=yM(t).labelEl;r&&(r.setStyle(e.label.style),i(r,{shape:e.label.shape,position:e.label.position}),tf(r,n))},_renderHandle:function(t){if(!this._dragging&&this.updateHandleTransform){var e=this._axisPointerModel,i=this._api.getZr(),n=this._handle,r=e.getModel("handle"),a=e.get("status");if(!r.get("show")||!a||"hide"===a)return n&&i.remove(n),void(this._handle=null);var o;this._handle||(o=!0,n=this._handle=Va(r.get("icon"),{cursor:"move",draggable:!0,onmousemove:function(t){Ig(t.event)},onmousedown:_M(this._onHandleDragMove,this,0,0),drift:_M(this._onHandleDragMove,this),ondragend:_M(this._onHandleDragEnd,this)}),i.add(n)),nf(n,e,!1);var s=["color","borderColor","borderWidth","opacity","shadowColor","shadowBlur","shadowOffsetX","shadowOffsetY"];n.setStyle(r.getItemStyle(null,s));var l=r.get("size");_(l)||(l=[l,l]),n.attr("scale",[l[0]/2,l[1]/2]),Hs(this,"_doDispatchAxisPointer",r.get("throttle")||0,"fixRate"),this._moveHandleToValue(t,o)}},_moveHandleToValue:function(t,e){Qd(this._axisPointerModel,!e&&this._moveAnimation,this._handle,ef(this.getHandleTransform(t,this._axisModel,this._axisPointerModel)))},_onHandleDragMove:function(t,e){var i=this._handle;if(i){this._dragging=!0;var n=this.updateHandleTransform(ef(i),[t,e],this._axisModel,this._axisPointerModel);this._payloadInfo=n,i.stopAnimation(),i.attr(ef(n)),yM(i).lastProp=null,this._doDispatchAxisPointer()}},_doDispatchAxisPointer:function(){var t=this._handle;if(t){var e=this._payloadInfo,i=this._axisModel;this._api.dispatchAction({type:"updateAxisPointer",x:e.cursorPoint[0],y:e.cursorPoint[1],tooltipOption:e.tooltipOption,axesInfo:[{axisDim:i.axis.dim,axisIndex:i.componentIndex}]})}},_onHandleDragEnd:function(){this._dragging=!1;var t=this._handle;if(t){var e=this._axisPointerModel.get("value");this._moveHandleToValue(e),this._api.dispatchAction({type:"hideTip"})}},getHandleTransform:null,updateHandleTransform:null,clear:function(t){this._lastValue=null,this._lastStatus=null;var e=t.getZr(),i=this._group,n=this._handle;e&&i&&(this._lastGraphicKey=null,i&&e.remove(i),n&&e.remove(n),this._group=null,this._handle=null,this._payloadInfo=null)},doClear:function(){},buildLabel:function(t,e,i){return i=i||0,{x:t[i],y:t[1-i],width:e[i],height:e[1-i]}}},Kd.prototype.constructor=Kd,er(Kd);var wM=Kd.extend({makeElOption:function(t,e,i,n,r){var a=i.axis,o=a.grid,s=n.get("type"),l=df(o,a).getOtherAxis(a).getGlobalExtent(),h=a.toGlobalCoord(a.dataToCoord(e,!0));if(s&&"none"!==s){var u=rf(n),c=bM[s](a,h,l,u);c.style=u,t.graphicKey=c.type,t.pointer=c}var d=Tc(o.model,i);hf(e,t,d,i,n,r)},getHandleTransform:function(t,e,i){var n=Tc(e.axis.grid.model,e,{labelInside:!1});return n.labelMargin=i.get("handle.margin"),{position:lf(e.axis,t,n),rotation:n.rotation+(n.labelDirection<0?Math.PI:0)}},updateHandleTransform:function(t,e,i){var n=i.axis,r=n.grid,a=n.getGlobalExtent(!0),o=df(r,n).getOtherAxis(n).getGlobalExtent(),s="x"===n.dim?0:1,l=t.position;l[s]+=e[s],l[s]=Math.min(a[1],l[s]),l[s]=Math.max(a[0],l[s]);var h=(o[1]+o[0])/2,u=[h,h];u[s]=l[s];var c=[{verticalAlign:"middle"},{align:"center"}];return{position:l,rotation:t.rotation,cursorPoint:u,tooltipOption:c[s]}}}),bM={line:function(t,e,i,n){var r=uf([e,i[0]],[e,i[1]],ff(t));return ia({shape:r,style:n}),{type:"Line",shape:r}},shadow:function(t,e,i){var n=Math.max(1,t.getBandWidth()),r=i[1]-i[0];return{type:"Rect",shape:cf([e-n/2,i[0]],[n,r],ff(t))}}};rS.registerAxisPointerClass("CartesianAxisPointer",wM),Yl(function(t){if(t){(!t.axisPointer||0===t.axisPointer.length)&&(t.axisPointer={});var e=t.axisPointer.link;e&&!_(e)&&(t.axisPointer.link=[e])}}),jl(ow.PROCESSOR.STATISTIC,function(t,e){t.getComponent("axisPointer").coordSysAxesInfo=fc(t,e)}),Ul({type:"updateAxisPointer",event:"updateAxisPointer",update:":updateAxisPointer"},pM),ih({type:"tooltip",dependencies:["axisPointer"],defaultOption:{zlevel:0,z:60,show:!0,showContent:!0,trigger:"item",triggerOn:"mousemove|click",alwaysShowContent:!1,displayMode:"single",renderMode:"auto",confine:!1,showDelay:0,hideDelay:100,transitionDuration:.4,enterable:!1,backgroundColor:"rgba(50,50,50,0.7)",borderColor:"#333",borderRadius:4,borderWidth:0,padding:5,extraCssText:"",axisPointer:{type:"line",axis:"auto",animation:"auto",animationDurationUpdate:200,animationEasingUpdate:"exponentialOut",crossStyle:{color:"#999",width:1,type:"dashed",textStyle:{}}},textStyle:{color:"#fff",fontSize:14}}});var SM=f,MM=fo,IM=["","-webkit-","-moz-","-o-"],TM="position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;";mf.prototype={constructor:mf,_enterable:!0,update:function(){var t=this._container,e=t.currentStyle||document.defaultView.getComputedStyle(t),i=t.style;"absolute"!==i.position&&"absolute"!==e.position&&(i.position="relative")},show:function(t){clearTimeout(this._hideTimeout);var e=this.el;e.style.cssText=TM+vf(t)+";left:"+this._x+"px;top:"+this._y+"px;"+(t.get("extraCssText")||""),e.style.display=e.innerHTML?"block":"none",e.style.pointerEvents=this._enterable?"auto":"none",this._show=!0},setContent:function(t){this.el.innerHTML=null==t?"":t},setEnterable:function(t){this._enterable=t},getSize:function(){var t=this.el;return[t.clientWidth,t.clientHeight]},moveTo:function(t,e){var i,n=this._zr;n&&n.painter&&(i=n.painter.getViewportRootOffset())&&(t+=i.offsetLeft,e+=i.offsetTop);var r=this.el.style;r.left=t+"px",r.top=e+"px",this._x=t,this._y=e},hide:function(){this.el.style.display="none",this._show=!1},hideLater:function(t){!this._show||this._inContent&&this._enterable||(t?(this._hideDelay=t,this._show=!1,this._hideTimeout=setTimeout(y(this.hide,this),t)):this.hide())},isShow:function(){return this._show},getOuterSize:function(){var t=this.el.clientWidth,e=this.el.clientHeight;if(document.defaultView&&document.defaultView.getComputedStyle){var i=document.defaultView.getComputedStyle(this.el);i&&(t+=parseInt(i.paddingLeft,10)+parseInt(i.paddingRight,10)+parseInt(i.borderLeftWidth,10)+parseInt(i.borderRightWidth,10),e+=parseInt(i.paddingTop,10)+parseInt(i.paddingBottom,10)+parseInt(i.borderTopWidth,10)+parseInt(i.borderBottomWidth,10))}return{width:t,height:e}}},yf.prototype={constructor:yf,_enterable:!0,update:function(){},show:function(){this._hideTimeout&&clearTimeout(this._hideTimeout),this.el.attr("show",!0),this._show=!0},setContent:function(t,e,i){this.el&&this._zr.remove(this.el);for(var n={},r=t,a="{marker",o="|}",s=r.indexOf(a);s>=0;){var l=r.indexOf(o),h=r.substr(s+a.length,l-s-a.length);n["marker"+h]=h.indexOf("sub")>-1?{textWidth:4,textHeight:4,textBorderRadius:2,textBackgroundColor:e[h],textOffset:[3,0]}:{textWidth:10,textHeight:10,textBorderRadius:5,textBackgroundColor:e[h]},r=r.substr(l+1),s=r.indexOf("{marker")}this.el=new xy({style:{rich:n,text:t,textLineHeight:20,textBackgroundColor:i.get("backgroundColor"),textBorderRadius:i.get("borderRadius"),textFill:i.get("textStyle.color"),textPadding:i.get("padding")},z:i.get("z")}),this._zr.add(this.el);var u=this;this.el.on("mouseover",function(){u._enterable&&(clearTimeout(u._hideTimeout),u._show=!0),u._inContent=!0}),this.el.on("mouseout",function(){u._enterable&&u._show&&u.hideLater(u._hideDelay),u._inContent=!1})},setEnterable:function(t){this._enterable=t},getSize:function(){var t=this.el.getBoundingRect();return[t.width,t.height]},moveTo:function(t,e){this.el&&this.el.attr("position",[t,e])},hide:function(){this.el.hide(),this._show=!1},hideLater:function(t){!this._show||this._inContent&&this._enterable||(t?(this._hideDelay=t,this._show=!1,this._hideTimeout=setTimeout(y(this.hide,this),t)):this.hide())},isShow:function(){return this._show},getOuterSize:function(){return this.getSize()}};var CM=y,AM=f,DM=Ua,kM=new Dy({shape:{x:-1,y:-1,width:2,height:2}});nh({type:"tooltip",init:function(t,e){if(!tg.node){var i=t.getComponent("tooltip"),n=i.get("renderMode");this._renderMode=Qn(n);var r;"html"===this._renderMode?(r=new mf(e.getDom(),e),this._newLine="
    "):(r=new yf(e),this._newLine="\n"),this._tooltipContent=r}},render:function(t,e,i){if(!tg.node){this.group.removeAll(),this._tooltipModel=t,this._ecModel=e,this._api=i,this._lastDataByCoordSys=null,this._alwaysShowContent=t.get("alwaysShowContent");var n=this._tooltipContent;n.update(),n.setEnterable(t.get("enterable")),this._initGlobalListener(),this._keepShow()}},_initGlobalListener:function(){var t=this._tooltipModel,e=t.get("triggerOn");Zd("itemTooltip",this._api,CM(function(t,i,n){"none"!==e&&(e.indexOf(t)>=0?this._tryShow(i,n):"leave"===t&&this._hide(n))},this))},_keepShow:function(){var t=this._tooltipModel,e=this._ecModel,i=this._api;if(null!=this._lastX&&null!=this._lastY&&"none"!==t.get("triggerOn")){var n=this;clearTimeout(this._refreshUpdateTimeout),this._refreshUpdateTimeout=setTimeout(function(){n.manuallyShowTip(t,e,i,{x:n._lastX,y:n._lastY})})}},manuallyShowTip:function(t,e,i,n){if(n.from!==this.uid&&!tg.node){var r=_f(n,i);this._ticket="";var a=n.dataByCoordSys;if(n.tooltip&&null!=n.x&&null!=n.y){var o=kM;o.position=[n.x,n.y],o.update(),o.tooltip=n.tooltip,this._tryShow({offsetX:n.x,offsetY:n.y,target:o},r)}else if(a)this._tryShow({offsetX:n.x,offsetY:n.y,position:n.position,event:{},dataByCoordSys:n.dataByCoordSys,tooltipOption:n.tooltipOption},r);else if(null!=n.seriesIndex){if(this._manuallyAxisShowTip(t,e,i,n))return;var s=uM(n,e),l=s.point[0],h=s.point[1];null!=l&&null!=h&&this._tryShow({offsetX:l,offsetY:h,position:n.position,target:s.el,event:{}},r)}else null!=n.x&&null!=n.y&&(i.dispatchAction({type:"updateAxisPointer",x:n.x,y:n.y}),this._tryShow({offsetX:n.x,offsetY:n.y,position:n.position,target:i.getZr().findHover(n.x,n.y).target,event:{}},r))}},manuallyHideTip:function(t,e,i,n){var r=this._tooltipContent;!this._alwaysShowContent&&this._tooltipModel&&r.hideLater(this._tooltipModel.get("hideDelay")),this._lastX=this._lastY=null,n.from!==this.uid&&this._hide(_f(n,i))},_manuallyAxisShowTip:function(t,e,i,n){var r=n.seriesIndex,a=n.dataIndex,o=e.getComponent("axisPointer").coordSysAxesInfo;if(null!=r&&null!=a&&null!=o){var s=e.getSeriesByIndex(r);if(s){var l=s.getData(),t=xf([l.getItemModel(a),s,(s.coordinateSystem||{}).model,t]);if("axis"===t.get("trigger"))return i.dispatchAction({type:"updateAxisPointer",seriesIndex:r,dataIndex:a,position:n.position}),!0}}},_tryShow:function(t,e){var i=t.target,n=this._tooltipModel;if(n){this._lastX=t.offsetX,this._lastY=t.offsetY;var r=t.dataByCoordSys;r&&r.length?this._showAxisTooltip(r,t):i&&null!=i.dataIndex?(this._lastDataByCoordSys=null,this._showSeriesItemTooltip(t,i,e)):i&&i.tooltip?(this._lastDataByCoordSys=null,this._showComponentItemTooltip(t,i,e)):(this._lastDataByCoordSys=null,this._hide(e))}},_showOrMove:function(t,e){var i=t.get("showDelay");e=y(e,this),clearTimeout(this._showTimout),i>0?this._showTimout=setTimeout(e,i):e()},_showAxisTooltip:function(t,e){var i=this._ecModel,n=this._tooltipModel,a=[e.offsetX,e.offsetY],o=[],s=[],l=xf([e.tooltipOption,n]),h=this._renderMode,u=this._newLine,c={};AM(t,function(t){AM(t.dataByAxis,function(t){var e=i.getComponent(t.axisDim+"Axis",t.axisIndex),n=t.value,a=[];if(e&&null!=n){var l=sf(n,e.axis,i,t.seriesDataIndices,t.valueLabelOpt);f(t.seriesDataIndices,function(o){var u=i.getSeriesByIndex(o.seriesIndex),d=o.dataIndexInside,f=u&&u.getDataParams(d);if(f.axisDim=t.axisDim,f.axisIndex=t.axisIndex,f.axisType=t.axisType,f.axisId=t.axisId,f.axisValue=hu(e.axis,n),f.axisValueLabel=l,f){s.push(f);var p,g=u.formatTooltip(d,!0,null,h);if(S(g)){p=g.html;var v=g.markers;r(c,v)}else p=g;a.push(p)}});var d=l;o.push("html"!==h?a.join(u):(d?po(d)+u:"")+a.join(u))}})},this),o.reverse(),o=o.join(this._newLine+this._newLine);var d=e.position;this._showOrMove(l,function(){this._updateContentNotChangedOnAxis(t)?this._updatePosition(l,d,a[0],a[1],this._tooltipContent,s):this._showTooltipContent(l,o,s,Math.random(),a[0],a[1],d,void 0,c)})},_showSeriesItemTooltip:function(t,e,i){var n=this._ecModel,r=e.seriesIndex,a=n.getSeriesByIndex(r),o=e.dataModel||a,s=e.dataIndex,l=e.dataType,h=o.getData(),u=xf([h.getItemModel(s),o,a&&(a.coordinateSystem||{}).model,this._tooltipModel]),c=u.get("trigger");if(null==c||"item"===c){var d,f,p=o.getDataParams(s,l),g=o.formatTooltip(s,!1,l,this._renderMode);S(g)?(d=g.html,f=g.markers):(d=g,f=null);var v="item_"+o.name+"_"+s;this._showOrMove(u,function(){this._showTooltipContent(u,d,p,v,t.offsetX,t.offsetY,t.position,t.target,f)}),i({type:"showTip",dataIndexInside:s,dataIndex:h.getRawIndex(s),seriesIndex:r,from:this.uid})}},_showComponentItemTooltip:function(t,e,i){var n=e.tooltip;if("string"==typeof n){var r=n;n={content:r,formatter:r}}var a=new Wa(n,this._tooltipModel,this._ecModel),o=a.get("content"),s=Math.random();this._showOrMove(a,function(){this._showTooltipContent(a,o,a.get("formatterParams")||{},s,t.offsetX,t.offsetY,t.position,e)}),i({type:"showTip",from:this.uid})},_showTooltipContent:function(t,e,i,n,r,a,o,s,l){if(this._ticket="",t.get("showContent")&&t.get("show")){var h=this._tooltipContent,u=t.get("formatter");o=o||t.get("position");var c=e;if(u&&"string"==typeof u)c=go(u,i,!0);else if("function"==typeof u){var d=CM(function(e,n){e===this._ticket&&(h.setContent(n,l,t),this._updatePosition(t,o,r,a,h,i,s))},this);this._ticket=n,c=u(i,n,d)}h.setContent(c,l,t),h.show(t),this._updatePosition(t,o,r,a,h,i,s)}},_updatePosition:function(t,e,i,n,r,a,o){var s=this._api.getWidth(),l=this._api.getHeight();e=e||t.get("position");var h=r.getSize(),u=t.get("align"),c=t.get("verticalAlign"),d=o&&o.getBoundingRect().clone();if(o&&d.applyTransform(o.transform),"function"==typeof e&&(e=e([i,n],a,r.el,d,{viewSize:[s,l],contentSize:h.slice()})),_(e))i=DM(e[0],s),n=DM(e[1],l);else if(S(e)){e.width=h[0],e.height=h[1];var f=bo(e,{width:s,height:l});i=f.x,n=f.y,u=null,c=null}else if("string"==typeof e&&o){var p=Sf(e,d,h);i=p[0],n=p[1]}else{var p=wf(i,n,r,s,l,u?null:20,c?null:20);i=p[0],n=p[1]}if(u&&(i-=Mf(u)?h[0]/2:"right"===u?h[0]:0),c&&(n-=Mf(c)?h[1]/2:"bottom"===c?h[1]:0),t.get("confine")){var p=bf(i,n,r,s,l);i=p[0],n=p[1]}r.moveTo(i,n)},_updateContentNotChangedOnAxis:function(t){var e=this._lastDataByCoordSys,i=!!e&&e.length===t.length;return i&&AM(e,function(e,n){var r=e.dataByAxis||{},a=t[n]||{},o=a.dataByAxis||[];i&=r.length===o.length,i&&AM(r,function(t,e){var n=o[e]||{},r=t.seriesDataIndices||[],a=n.seriesDataIndices||[];i&=t.value===n.value&&t.axisType===n.axisType&&t.axisId===n.axisId&&r.length===a.length,i&&AM(r,function(t,e){var n=a[e];i&=t.seriesIndex===n.seriesIndex&&t.dataIndex===n.dataIndex})})}),this._lastDataByCoordSys=t,!!i},_hide:function(t){this._lastDataByCoordSys=null,t({type:"hideTip",from:this.uid})},dispose:function(t,e){tg.node||(this._tooltipContent.hide(),$d("itemTooltip",e))}}),Ul({type:"showTip",event:"showTip",update:"tooltip:manuallyShowTip"},function(){}),Ul({type:"hideTip",event:"hideTip",update:"tooltip:manuallyHideTip"},function(){});var PM=co,LM=po,OM=ih({type:"marker",dependencies:["series","grid","polar","geo"],init:function(t,e,i,n){this.mergeDefaultAndTheme(t,i),this.mergeOption(t,i,n.createdBySelf,!0)},isAnimationEnabled:function(){if(tg.node)return!1;var t=this.__hostSeries;return this.getShallow("animation")&&t&&t.isAnimationEnabled()},mergeOption:function(t,e,i,n){var r=this.constructor,a=this.mainType+"Model";i||e.eachSeries(function(t){var i=t.get(this.mainType,!0),s=t[a];return i&&i.data?(s?s.mergeOption(i,e,!0):(n&&If(i),f(i.data,function(t){t instanceof Array?(If(t[0]),If(t[1])):If(t)}),s=new r(i,this,e),o(s,{mainType:this.mainType,seriesIndex:t.seriesIndex,name:t.name,createdBySelf:!0}),s.__hostSeries=t),void(t[a]=s)):void(t[a]=null)},this)},formatTooltip:function(t){var e=this.getData(),i=this.getRawValue(t),n=_(i)?p(i,PM).join(", "):PM(i),r=e.getName(t),a=LM(this.name);return(null!=i||r)&&(a+="
    "),r&&(a+=LM(r),null!=i&&(a+=" : ")),null!=i&&(a+=LM(n)),a},getData:function(){return this._data},setData:function(t){this._data=t}});c(OM,i_),OM.extend({type:"markPoint",defaultOption:{zlevel:0,z:5,symbol:"pin",symbolSize:50,tooltip:{trigger:"item"},label:{show:!0,position:"inside"},itemStyle:{borderWidth:2},emphasis:{label:{show:!0}}}});var zM=h,EM=x,RM={min:EM(Af,"min"),max:EM(Af,"max"),average:EM(Af,"average")},BM=nh({type:"marker",init:function(){this.markerGroupMap=N()},render:function(t,e,i){var n=this.markerGroupMap;n.each(function(t){t.__keep=!1});var r=this.type+"Model";e.eachSeries(function(t){var n=t[r];n&&this.renderSeries(t,n,e,i)},this),n.each(function(t){!t.__keep&&this.group.remove(t.group)},this)},renderSeries:function(){}});BM.extend({type:"markPoint",updateTransform:function(t,e,i){e.eachSeries(function(t){var e=t.markPointModel;e&&(Ef(e.getData(),t,i),this.markerGroupMap.get(t.id).updateLayout(e))},this)},renderSeries:function(t,e,i,n){var r=t.coordinateSystem,a=t.id,o=t.getData(),s=this.markerGroupMap,l=s.get(a)||s.set(a,new zc),h=Rf(r,t,e);e.setData(h),Ef(e.getData(),t,n),h.each(function(t){var i=h.getItemModel(t),n=i.getShallow("symbolSize");"function"==typeof n&&(n=n(e.getRawValue(t),e.getDataParams(t))),h.setItemVisual(t,{symbolSize:n,color:i.get("itemStyle.color")||o.getVisual("color"),symbol:i.getShallow("symbol")})}),l.updateData(h),this.group.add(l.group),h.eachItemGraphicEl(function(t){t.traverse(function(t){t.dataModel=e + })}),l.__keep=!0,l.group.silent=e.get("silent")||t.get("silent")}}),Yl(function(t){t.markPoint=t.markPoint||{}}),OM.extend({type:"markLine",defaultOption:{zlevel:0,z:5,symbol:["circle","arrow"],symbolSize:[8,16],precision:2,tooltip:{trigger:"item"},label:{show:!0,position:"end"},lineStyle:{type:"dashed"},emphasis:{label:{show:!0},lineStyle:{width:3}},animationEasing:"linear"}});var NM=ky.prototype,FM=Ly.prototype,VM=$r({type:"ec-line",style:{stroke:"#000",fill:null},shape:{x1:0,y1:0,x2:0,y2:0,percent:1,cpx1:null,cpy1:null},buildPath:function(t,e){(Bf(e)?NM:FM).buildPath(t,e)},pointAt:function(t){return Bf(this.shape)?NM.pointAt.call(this,t):FM.pointAt.call(this,t)},tangentAt:function(t){var e=this.shape,i=Bf(e)?[e.x2-e.x1,e.y2-e.y1]:FM.tangentAt.call(this,t);return te(i,i)}}),WM=["fromSymbol","toSymbol"],GM=Hf.prototype;GM.beforeUpdate=Gf,GM._createLine=function(t,e,i){var n=t.hostModel,r=t.getItemLayout(e),a=Vf(r);a.shape.percent=0,Oa(a,{shape:{percent:1}},n,e),this.add(a);var o=new xy({name:"label"});this.add(o),f(WM,function(i){var n=Ff(i,t,e);this.add(n),this[Nf(i)]=t.getItemVisual(e,i)},this),this._updateCommonStl(t,e,i)},GM.updateData=function(t,e,i){var n=t.hostModel,r=this.childOfName("line"),a=t.getItemLayout(e),o={shape:{}};Wf(o.shape,a),La(r,o,n,e),f(WM,function(i){var n=t.getItemVisual(e,i),r=Nf(i);if(this[r]!==n){this.remove(this.childOfName(i));var a=Ff(i,t,e);this.add(a)}this[r]=n},this),this._updateCommonStl(t,e,i)},GM._updateCommonStl=function(t,e,i){var n=t.hostModel,r=this.childOfName("line"),a=i&&i.lineStyle,o=i&&i.hoverLineStyle,l=i&&i.labelModel,h=i&&i.hoverLabelModel;if(!i||t.hasItemOption){var u=t.getItemModel(e);a=u.getModel("lineStyle").getLineStyle(),o=u.getModel("emphasis.lineStyle").getLineStyle(),l=u.getModel("label"),h=u.getModel("emphasis.label")}var c=t.getItemVisual(e,"color"),d=k(t.getItemVisual(e,"opacity"),a.opacity,1);r.useStyle(s({strokeNoScale:!0,fill:"none",stroke:c,opacity:d},a)),r.hoverStyle=o,f(WM,function(t){var e=this.childOfName(t);e&&(e.setColor(c),e.setStyle({opacity:d}))},this);var p,g,v=l.getShallow("show"),m=h.getShallow("show"),y=this.childOfName("label");if((v||m)&&(p=c||"#000",g=n.getFormattedLabel(e,"normal",t.dataType),null==g)){var x=n.getRawValue(e);g=null==x?t.getName(e):isFinite(x)?$a(x):x}var _=v?g:null,w=m?D(n.getFormattedLabel(e,"emphasis",t.dataType),g):null,b=y.style;(null!=_||null!=w)&&(ba(y.style,l,{text:_},{autoColor:p}),y.__textAlign=b.textAlign,y.__verticalAlign=b.textVerticalAlign,y.__position=l.get("position")||"middle"),y.hoverStyle=null!=w?{text:w,textFill:h.getTextColor(!0),fontStyle:h.getShallow("fontStyle"),fontWeight:h.getShallow("fontWeight"),fontSize:h.getShallow("fontSize"),fontFamily:h.getShallow("fontFamily")}:{text:null},y.ignore=!v&&!m,xa(this)},GM.highlight=function(){this.trigger("emphasis")},GM.downplay=function(){this.trigger("normal")},GM.updateLayout=function(t,e){this.setLinePoints(t.getItemLayout(e))},GM.setLinePoints=function(t){var e=this.childOfName("line");Wf(e.shape,t),e.dirty()},u(Hf,lv);var HM=Zf.prototype;HM.isPersistent=function(){return!0},HM.updateData=function(t){var e=this,i=e.group,n=e._lineData;e._lineData=t,n||i.removeAll();var r=jf(t);t.diff(n).add(function(i){Xf(e,t,i,r)}).update(function(i,a){Yf(e,n,t,a,i,r)}).remove(function(t){i.remove(n.getItemGraphicEl(t))}).execute()},HM.updateLayout=function(){var t=this._lineData;t&&t.eachItemGraphicEl(function(e,i){e.updateLayout(t,i)},this)},HM.incrementalPrepareUpdate=function(t){this._seriesScope=jf(t),this._lineData=null,this.group.removeAll()},HM.incrementalUpdate=function(t,e){function i(t){t.isGroup||(t.incremental=t.useHoverLayer=!0)}for(var n=t.start;n=0&&"number"==typeof c&&(c=+c.toFixed(Math.min(m,20))),g.coord[f]=v.coord[f]=c,a=[g,v,{type:l,valueIndex:a.valueIndex,value:c}]}return a=[Df(t,a[0]),Df(t,a[1]),o({},a[2])],a[2].type=a[2].type||"",r(a[2],a[0]),r(a[2],a[1]),a};BM.extend({type:"markLine",updateTransform:function(t,e,i){e.eachSeries(function(t){var e=t.markLineModel;if(e){var n=e.getData(),r=e.__from,a=e.__to;r.each(function(e){Jf(r,e,!0,t,i),Jf(a,e,!1,t,i)}),n.each(function(t){n.setItemLayout(t,[r.getItemLayout(t),a.getItemLayout(t)])}),this.markerGroupMap.get(t.id).updateLayout()}},this)},renderSeries:function(t,e,i,n){function r(e,i,r){var a=e.getItemModel(i);Jf(e,i,r,t,n),e.setItemVisual(i,{symbolSize:a.get("symbolSize")||g[r?0:1],symbol:a.get("symbol",!0)||p[r?0:1],color:a.get("itemStyle.color")||s.getVisual("color")})}var a=t.coordinateSystem,o=t.id,s=t.getData(),l=this.markerGroupMap,h=l.get(o)||l.set(o,new Zf);this.group.add(h.group);var u=tp(a,t,e),c=u.from,d=u.to,f=u.line;e.__from=c,e.__to=d,e.setData(f);var p=e.get("symbol"),g=e.get("symbolSize");_(p)||(p=[p,p]),"number"==typeof g&&(g=[g,g]),u.from.each(function(t){r(c,t,!0),r(d,t,!1)}),f.each(function(t){var e=f.getItemModel(t).get("lineStyle.color");f.setItemVisual(t,{color:e||c.getItemVisual(t,"color")}),f.setItemLayout(t,[c.getItemLayout(t),d.getItemLayout(t)]),f.setItemVisual(t,{fromSymbolSize:c.getItemVisual(t,"symbolSize"),fromSymbol:c.getItemVisual(t,"symbol"),toSymbolSize:d.getItemVisual(t,"symbolSize"),toSymbol:d.getItemVisual(t,"symbol")})}),h.updateData(f),u.line.eachItemGraphicEl(function(t){t.traverse(function(t){t.dataModel=e})}),h.__keep=!0,h.group.silent=e.get("silent")||t.get("silent")}}),Yl(function(t){t.markLine=t.markLine||{}}),OM.extend({type:"markArea",defaultOption:{zlevel:0,z:1,tooltip:{trigger:"item"},animation:!1,label:{show:!0,position:"top"},itemStyle:{borderWidth:0},emphasis:{label:{show:!0,position:"top"}}}});var XM=function(t,e,i,n){var r=Df(t,n[0]),o=Df(t,n[1]),s=A,l=r.coord,h=o.coord;l[0]=s(l[0],-1/0),l[1]=s(l[1],-1/0),h[0]=s(h[0],1/0),h[1]=s(h[1],1/0);var u=a([{},r,o]);return u.coord=[r.coord,o.coord],u.x0=r.x,u.y0=r.y,u.x1=o.x,u.y1=o.y,u},YM=[["x0","y0"],["x1","y0"],["x1","y1"],["x0","y1"]];BM.extend({type:"markArea",updateTransform:function(t,e,i){e.eachSeries(function(t){var e=t.markAreaModel;if(e){var n=e.getData();n.each(function(e){var r=p(YM,function(r){return rp(n,e,r,t,i)});n.setItemLayout(e,r);var a=n.getItemGraphicEl(e);a.setShape("points",r)})}},this)},renderSeries:function(t,e,i,n){var r=t.coordinateSystem,a=t.id,o=t.getData(),l=this.markerGroupMap,h=l.get(a)||l.set(a,{group:new lv});this.group.add(h.group),h.__keep=!0;var u=ap(r,t,e);e.setData(u),u.each(function(e){u.setItemLayout(e,p(YM,function(i){return rp(u,e,i,t,n)})),u.setItemVisual(e,{color:o.getVisual("color")})}),u.diff(h.__data).add(function(t){var e=new Cy({shape:{points:u.getItemLayout(t)}});u.setItemGraphicEl(t,e),h.group.add(e)}).update(function(t,i){var n=h.__data.getItemGraphicEl(i);La(n,{shape:{points:u.getItemLayout(t)}},e,t),h.group.add(n),u.setItemGraphicEl(t,n)}).remove(function(t){var e=h.__data.getItemGraphicEl(t);h.group.remove(e)}).execute(),u.eachItemGraphicEl(function(t,i){var n=u.getItemModel(i),r=n.getModel("label"),a=n.getModel("emphasis.label"),o=u.getItemVisual(i,"color");t.useStyle(s(n.getModel("itemStyle").getItemStyle(),{fill:Ke(o,.4),stroke:o})),t.hoverStyle=n.getModel("emphasis.itemStyle").getItemStyle(),wa(t.style,t.hoverStyle,r,a,{labelFetcher:e,labelDataIndex:i,defaultText:u.getName(i)||"",isRectText:!0,autoColor:o}),xa(t,{}),t.dataModel=e}),h.__data=u,h.group.silent=e.get("silent")||t.get("silent")}}),Yl(function(t){t.markArea=t.markArea||{}});var jM=function(t){var e=t&&t.timeline;_(e)||(e=e?[e]:[]),f(e,function(t){t&&op(t)})};yx.registerSubTypeDefaulter("timeline",function(){return"slider"}),Ul({type:"timelineChange",event:"timelineChanged",update:"prepareAndUpdate"},function(t,e){var i=e.getComponent("timeline");return i&&null!=t.currentIndex&&(i.setCurrentIndex(t.currentIndex),!i.get("loop",!0)&&i.isIndexMax()&&i.setPlayState(!1)),e.resetOption("timeline"),s({currentIndex:i.option.currentIndex},t)}),Ul({type:"timelinePlayChange",event:"timelinePlayChanged",update:"update"},function(t,e){var i=e.getComponent("timeline");i&&null!=t.playState&&i.setPlayState(t.playState)});var qM=yx.extend({type:"timeline",layoutMode:"box",defaultOption:{zlevel:0,z:4,show:!0,axisType:"time",realtime:!0,left:"20%",top:null,right:"20%",bottom:0,width:null,height:40,padding:5,controlPosition:"left",autoPlay:!1,rewind:!1,loop:!0,playInterval:2e3,currentIndex:0,itemStyle:{},label:{color:"#000"},data:[]},init:function(t,e,i){this._data,this._names,this.mergeDefaultAndTheme(t,i),this._initData()},mergeOption:function(){qM.superApply(this,"mergeOption",arguments),this._initData()},setCurrentIndex:function(t){null==t&&(t=this.option.currentIndex);var e=this._data.count();this.option.loop?t=(t%e+e)%e:(t>=e&&(t=e-1),0>t&&(t=0)),this.option.currentIndex=t},getCurrentIndex:function(){return this.option.currentIndex},isIndexMax:function(){return this.getCurrentIndex()>=this._data.count()-1},setPlayState:function(t){this.option.autoPlay=!!t},getPlayState:function(){return!!this.option.autoPlay},_initData:function(){var t=this.option,e=t.data||[],i=t.axisType,r=this._names=[];if("category"===i){var a=[];f(e,function(t,e){var i,o=Vn(t);S(t)?(i=n(t),i.value=e):i=e,a.push(i),b(o)||null!=o&&!isNaN(o)||(o=""),r.push(o+"")}),e=a}var o={category:"ordinal",time:"time"}[i]||"number",s=this._data=new Bw([{name:"value",type:o}],this);s.initData(e,r)},getData:function(){return this._data},getCategories:function(){return"category"===this.get("axisType")?this._names.slice():void 0}}),UM=qM.extend({type:"timeline.slider",defaultOption:{backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderWidth:0,orient:"horizontal",inverse:!1,tooltip:{trigger:"item"},symbol:"emptyCircle",symbolSize:10,lineStyle:{show:!0,width:2,color:"#304654"},label:{position:"auto",show:!0,interval:"auto",rotate:0,color:"#304654"},itemStyle:{color:"#304654",borderWidth:1},checkpointStyle:{symbol:"circle",symbolSize:13,color:"#c23531",borderWidth:5,borderColor:"rgba(194,53,49, 0.5)",animation:!0,animationDuration:300,animationEasing:"quinticInOut"},controlStyle:{show:!0,showPlayBtn:!0,showPrevBtn:!0,showNextBtn:!0,itemSize:22,itemGap:12,position:"left",playIcon:"path://M31.6,53C17.5,53,6,41.5,6,27.4S17.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z",stopIcon:"path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z",nextIcon:"path://M18.6,50.8l22.5-22.5c0.2-0.2,0.3-0.4,0.3-0.7c0-0.3-0.1-0.5-0.3-0.7L18.7,4.4c-0.1-0.1-0.2-0.3-0.2-0.5 c0-0.4,0.3-0.8,0.8-0.8c0.2,0,0.5,0.1,0.6,0.3l23.5,23.5l0,0c0.2,0.2,0.3,0.4,0.3,0.7c0,0.3-0.1,0.5-0.3,0.7l-0.1,0.1L19.7,52 c-0.1,0.1-0.3,0.2-0.5,0.2c-0.4,0-0.8-0.3-0.8-0.8C18.4,51.2,18.5,51,18.6,50.8z",prevIcon:"path://M43,52.8L20.4,30.3c-0.2-0.2-0.3-0.4-0.3-0.7c0-0.3,0.1-0.5,0.3-0.7L42.9,6.4c0.1-0.1,0.2-0.3,0.2-0.5 c0-0.4-0.3-0.8-0.8-0.8c-0.2,0-0.5,0.1-0.6,0.3L18.3,28.8l0,0c-0.2,0.2-0.3,0.4-0.3,0.7c0,0.3,0.1,0.5,0.3,0.7l0.1,0.1L41.9,54 c0.1,0.1,0.3,0.2,0.5,0.2c0.4,0,0.8-0.3,0.8-0.8C43.2,53.2,43.1,53,43,52.8z",color:"#304654",borderColor:"#304654",borderWidth:1},emphasis:{label:{show:!0,color:"#c23531"},itemStyle:{color:"#c23531"},controlStyle:{color:"#c23531",borderColor:"#c23531",borderWidth:2}},data:[]}});c(UM,i_);var $M=s_.extend({type:"timeline"}),KM=function(t,e,i,n){Pb.call(this,t,e,i),this.type=n||"value",this.model=null};KM.prototype={constructor:KM,getLabelModel:function(){return this.model.getModel("label")},isHorizontal:function(){return"horizontal"===this.model.get("orient")}},u(KM,Pb);var QM=y,JM=f,tI=Math.PI;$M.extend({type:"timeline.slider",init:function(t,e){this.api=e,this._axis,this._viewRect,this._timer,this._currentPointer,this._mainGroup,this._labelGroup},render:function(t,e,i){if(this.model=t,this.api=i,this.ecModel=e,this.group.removeAll(),t.get("show",!0)){var n=this._layout(t,i),r=this._createGroup("mainGroup"),a=this._createGroup("labelGroup"),o=this._axis=this._createAxis(n,t);t.formatTooltip=function(t){return po(o.scale.getLabel(t))},JM(["AxisLine","AxisTick","Control","CurrentPointer"],function(e){this["_render"+e](n,r,o,t)},this),this._renderAxisLabel(n,a,o,t),this._position(n,t)}this._doPlayStop()},remove:function(){this._clearTimer(),this.group.removeAll()},dispose:function(){this._clearTimer()},_layout:function(t,e){var i=t.get("label.position"),n=t.get("orient"),r=hp(t,e);null==i||"auto"===i?i="horizontal"===n?r.y+r.height/2=0||"+"===i?"left":"right"},o={horizontal:i>=0||"+"===i?"top":"bottom",vertical:"middle"},s={horizontal:0,vertical:tI/2},l="vertical"===n?r.height:r.width,h=t.getModel("controlStyle"),u=h.get("show",!0),c=u?h.get("itemSize"):0,d=u?h.get("itemGap"):0,f=c+d,p=t.get("label.rotate")||0;p=p*tI/180;var g,v,m,y,x=h.get("position",!0),_=u&&h.get("showPlayBtn",!0),w=u&&h.get("showPrevBtn",!0),b=u&&h.get("showNextBtn",!0),S=0,M=l;return"left"===x||"bottom"===x?(_&&(g=[0,0],S+=f),w&&(v=[S,0],S+=f),b&&(m=[M-c,0],M-=f)):(_&&(g=[M-c,0],M-=f),w&&(v=[0,0],S+=f),b&&(m=[M-c,0],M-=f)),y=[S,M],t.get("inverse")&&y.reverse(),{viewRect:r,mainLength:l,orient:n,rotation:s[n],labelRotation:p,labelPosOpt:i,labelAlign:t.get("label.align")||a[n],labelBaseline:t.get("label.verticalAlign")||t.get("label.baseline")||o[n],playPosition:g,prevBtnPosition:v,nextBtnPosition:m,axisExtent:y,controlSize:c,controlGap:d}},_position:function(t){function e(t){var e=t.position;t.origin=[u[0][0]-e[0],u[1][0]-e[1]]}function i(t){return[[t.x,t.x+t.width],[t.y,t.y+t.height]]}function n(t,e,i,n,r){t[n]+=i[n][r]-e[n][r]}var r=this._mainGroup,a=this._labelGroup,o=t.viewRect;if("vertical"===t.orient){var s=be(),l=o.x,h=o.y+o.height;Te(s,s,[-l,-h]),Ce(s,s,-tI/2),Te(s,s,[l,h]),o=o.clone(),o.applyTransform(s)}var u=i(o),c=i(r.getBoundingRect()),d=i(a.getBoundingRect()),f=r.position,p=a.position;p[0]=f[0]=u[0][0];var g=t.labelPosOpt;if(isNaN(g)){var v="+"===g?0:1;n(f,c,u,1,v),n(p,d,u,1,1-v)}else{var v=g>=0?0:1;n(f,c,u,1,v),p[1]=f[1]+g}r.attr("position",f),a.attr("position",p),r.rotation=a.rotation=t.rotation,e(r),e(a)},_createAxis:function(t,e){var i=e.getData(),n=e.get("axisType"),r=ou(e,n);r.getTicks=function(){return i.mapArray(["value"],function(t){return t})};var a=i.getDataExtent("value");r.setExtent(a[0],a[1]),r.niceTicks();var o=new KM("value",r,t.axisExtent,n);return o.model=e,o},_createGroup:function(t){var e=this["_"+t]=new lv;return this.group.add(e),e},_renderAxisLine:function(t,e,i,n){var r=i.getExtent();n.get("lineStyle.show")&&e.add(new ky({shape:{x1:r[0],y1:0,x2:r[1],y2:0},style:o({lineCap:"round"},n.getModel("lineStyle").getLineStyle()),silent:!0,z2:1}))},_renderAxisTick:function(t,e,i,n){var r=n.getData(),a=i.scale.getTicks();JM(a,function(t){var a=i.dataToCoord(t),o=r.getItemModel(t),s=o.getModel("itemStyle"),l=o.getModel("emphasis.itemStyle"),h={position:[a,0],onclick:QM(this._changeTimeline,this,t)},u=cp(o,s,e,h);xa(u,l.getItemStyle()),o.get("tooltip")?(u.dataIndex=t,u.dataModel=n):u.dataIndex=u.dataModel=null},this)},_renderAxisLabel:function(t,e,i,n){var r=i.getLabelModel();if(r.get("show")){var a=n.getData(),o=i.getViewLabels();JM(o,function(n){var r=n.tickValue,o=a.getItemModel(r),s=o.getModel("label"),l=o.getModel("emphasis.label"),h=i.dataToCoord(n.tickValue),u=new xy({position:[h,0],rotation:t.labelRotation-t.rotation,onclick:QM(this._changeTimeline,this,r),silent:!1});ba(u.style,s,{text:n.formattedLabel,textAlign:t.labelAlign,textVerticalAlign:t.labelBaseline}),e.add(u),xa(u,ba({},l))},this)}},_renderControl:function(t,e,i,n){function r(t,i,r,u){if(t){var c={position:t,origin:[a/2,0],rotation:u?-o:0,rectHover:!0,style:s,onclick:r},d=up(n,i,h,c);e.add(d),xa(d,l)}}var a=t.controlSize,o=t.rotation,s=n.getModel("controlStyle").getItemStyle(),l=n.getModel("emphasis.controlStyle").getItemStyle(),h=[0,-a/2,a,a],u=n.getPlayState(),c=n.get("inverse",!0);r(t.nextBtnPosition,"controlStyle.nextIcon",QM(this._changeTimeline,this,c?"-":"+")),r(t.prevBtnPosition,"controlStyle.prevIcon",QM(this._changeTimeline,this,c?"+":"-")),r(t.playPosition,"controlStyle."+(u?"stopIcon":"playIcon"),QM(this._handlePlayClick,this,!u),!0)},_renderCurrentPointer:function(t,e,i,n){var r=n.getData(),a=n.getCurrentIndex(),o=r.getItemModel(a).getModel("checkpointStyle"),s=this,l={onCreate:function(t){t.draggable=!0,t.drift=QM(s._handlePointerDrag,s),t.ondragend=QM(s._handlePointerDragend,s),dp(t,a,i,n,!0)},onUpdate:function(t){dp(t,a,i,n)}};this._currentPointer=cp(o,o,this._mainGroup,{},this._currentPointer,l)},_handlePlayClick:function(t){this._clearTimer(),this.api.dispatchAction({type:"timelinePlayChange",playState:t,from:this.uid})},_handlePointerDrag:function(t,e,i){this._clearTimer(),this._pointerChangeTimeline([i.offsetX,i.offsetY])},_handlePointerDragend:function(t){this._pointerChangeTimeline([t.offsetX,t.offsetY],!0)},_pointerChangeTimeline:function(t,e){var i=this._toAxisCoord(t)[0],n=this._axis,r=Ka(n.getExtent().slice());i>r[1]&&(i=r[1]),is&&(n=s,e=a)}),e},_clearTimer:function(){this._timer&&(clearTimeout(this._timer),this._timer=null)},_changeTimeline:function(t){var e=this.model.getCurrentIndex();"+"===t?t=e+1:"-"===t&&(t=e-1),this.api.dispatchAction({type:"timelineChange",currentIndex:t,from:this.uid})}}),Yl(jM),yx.registerSubTypeDefaulter("dataZoom",function(){return"slider"});var eI=["x","y","z","radius","angle","single"],iI=["cartesian2d","polar","singleAxis"],nI=pp(eI,["axisIndex","axis","index","id"]),rI=f,aI=Ka,oI=function(t,e,i,n){this._dimName=t,this._axisIndex=e,this._valueWindow,this._percentWindow,this._dataExtent,this._minMaxSpan,this.ecModel=n,this._dataZoomModel=i};oI.prototype={constructor:oI,hostedBy:function(t){return this._dataZoomModel===t},getDataValueWindow:function(){return this._valueWindow.slice()},getDataPercentWindow:function(){return this._percentWindow.slice()},getTargetSeriesModels:function(){var t=[],e=this.ecModel;return e.eachSeries(function(i){if(fp(i.get("coordinateSystem"))){var n=this._dimName,r=e.queryComponents({mainType:n+"Axis",index:i.get(n+"AxisIndex"),id:i.get(n+"AxisId")})[0];this._axisIndex===(r&&r.componentIndex)&&t.push(i)}},this),t},getAxisModel:function(){return this.ecModel.getComponent(this._dimName+"Axis",this._axisIndex)},getOtherAxisModel:function(){var t,e,i=this._dimName,n=this.ecModel,r=this.getAxisModel(),a="x"===i||"y"===i;a?(e="gridIndex",t="x"===i?"y":"x"):(e="polarIndex",t="angle"===i?"radius":"angle");var o;return n.eachComponent(t+"Axis",function(t){(t.get(e)||0)===(r.get(e)||0)&&(o=t)}),o},getMinMaxSpan:function(){return n(this._minMaxSpan)},calculateDataWindow:function(t){var e=this._dataExtent,i=this.getAxisModel(),n=i.axis.scale,r=this._dataZoomModel.getRangePropMode(),a=[0,100],o=[t.start,t.end],s=[];return rI(["startValue","endValue"],function(e){s.push(null!=t[e]?n.parse(t[e]):null)}),rI([0,1],function(t){var i=s[t],l=o[t];"percent"===r[t]?(null==l&&(l=a[t]),i=n.parse(qa(l,a,e,!0))):l=qa(i,e,a,!0),s[t]=i,o[t]=l}),{valueWindow:aI(s),percentWindow:aI(o)}},reset:function(t){if(t===this._dataZoomModel){var e=this.getTargetSeriesModels();this._dataExtent=vp(this,this._dimName,e);var i=this.calculateDataWindow(t.option);this._valueWindow=i.valueWindow,this._percentWindow=i.percentWindow,xp(this),yp(this)}},restore:function(t){t===this._dataZoomModel&&(this._valueWindow=this._percentWindow=null,yp(this,!0))},filterData:function(t){function e(t){return t>=a[0]&&t<=a[1]}if(t===this._dataZoomModel){var i=this._dimName,n=this.getTargetSeriesModels(),r=t.get("filterMode"),a=this._valueWindow;"none"!==r&&rI(n,function(t){var n=t.getData(),o=n.mapDimension(i,!0);o.length&&("weakFilter"===r?n.filterSelf(function(t){for(var e,i,r,s=0;sa[1];if(h&&!u&&!c)return!0;h&&(r=!0),u&&(e=!0),c&&(i=!0)}return r&&e&&i}):rI(o,function(i){if("empty"===r)t.setData(n.map(i,function(t){return e(t)?t:0/0}));else{var o={};o[i]=a,n.selectRange(o)}}),rI(o,function(t){n.setApproximateExtent(a,t)}))})}}};var sI=f,lI=nI,hI=ih({type:"dataZoom",dependencies:["xAxis","yAxis","zAxis","radiusAxis","angleAxis","singleAxis","series"],defaultOption:{zlevel:0,z:4,orient:null,xAxisIndex:null,yAxisIndex:null,filterMode:"filter",throttle:null,start:0,end:100,startValue:null,endValue:null,minSpan:null,maxSpan:null,minValueSpan:null,maxValueSpan:null,rangeMode:null},init:function(t,e,i){this._dataIntervalByAxis={},this._dataInfo={},this._axisProxies={},this.textStyleModel,this._autoThrottle=!0,this._rangePropMode=["percent","percent"];var n=_p(t);this.mergeDefaultAndTheme(t,i),this.doInit(n)},mergeOption:function(t){var e=_p(t);r(this.option,t,!0),this.doInit(e)},doInit:function(t){var e=this.option;tg.canvasSupported||(e.realtime=!1),this._setDefaultThrottle(t),wp(this,t),sI([["start","startValue"],["end","endValue"]],function(t,i){"value"===this._rangePropMode[i]&&(e[t[0]]=null)},this),this.textStyleModel=this.getModel("textStyle"),this._resetTarget(),this._giveAxisProxies()},_giveAxisProxies:function(){var t=this._axisProxies;this.eachTargetAxis(function(e,i,n,r){var a=this.dependentModels[e.axis][i],o=a.__dzAxisProxy||(a.__dzAxisProxy=new oI(e.name,i,this,r));t[e.name+"_"+i]=o},this)},_resetTarget:function(){var t=this.option,e=this._judgeAutoMode();lI(function(e){var i=e.axisIndex;t[i]=Nn(t[i])},this),"axisIndex"===e?this._autoSetAxisIndex():"orient"===e&&this._autoSetOrient()},_judgeAutoMode:function(){var t=this.option,e=!1;lI(function(i){null!=t[i.axisIndex]&&(e=!0)},this);var i=t.orient;return null==i&&e?"orient":e?void 0:(null==i&&(t.orient="horizontal"),"axisIndex")},_autoSetAxisIndex:function(){var t=!0,e=this.get("orient",!0),i=this.option,n=this.dependentModels;if(t){var r="vertical"===e?"y":"x";n[r+"Axis"].length?(i[r+"AxisIndex"]=[0],t=!1):sI(n.singleAxis,function(n){t&&n.get("orient",!0)===e&&(i.singleAxisIndex=[n.componentIndex],t=!1)})}t&&lI(function(e){if(t){var n=[],r=this.dependentModels[e.axis];if(r.length&&!n.length)for(var a=0,o=r.length;o>a;a++)"category"===r[a].get("type")&&n.push(a);i[e.axisIndex]=n,n.length&&(t=!1)}},this),t&&this.ecModel.eachSeries(function(t){this._isSeriesHasAllAxesTypeOf(t,"value")&&lI(function(e){var n=i[e.axisIndex],r=t.get(e.axisIndex),a=t.get(e.axisId),o=t.ecModel.queryComponents({mainType:e.axis,index:r,id:a})[0];r=o.componentIndex,h(n,r)<0&&n.push(r)})},this)},_autoSetOrient:function(){var t;this.eachTargetAxis(function(e){!t&&(t=e.name)},this),this.option.orient="y"===t?"vertical":"horizontal"},_isSeriesHasAllAxesTypeOf:function(t,e){var i=!0;return lI(function(n){var r=t.get(n.axisIndex),a=this.dependentModels[n.axis][r];a&&a.get("type")===e||(i=!1)},this),i},_setDefaultThrottle:function(t){if(t.hasOwnProperty("throttle")&&(this._autoThrottle=!1),this._autoThrottle){var e=this.ecModel.option;this.option.throttle=e.animation&&e.animationDurationUpdate>0?100:20}},getFirstTargetAxisModel:function(){var t;return lI(function(e){if(null==t){var i=this.get(e.axisIndex);i.length&&(t=this.dependentModels[e.axis][i[0]])}},this),t},eachTargetAxis:function(t,e){var i=this.ecModel;lI(function(n){sI(this.get(n.axisIndex),function(r){t.call(e,n,r,this,i)},this)},this)},getAxisProxy:function(t,e){return this._axisProxies[t+"_"+e]},getAxisModel:function(t,e){var i=this.getAxisProxy(t,e);return i&&i.getAxisModel()},setRawRange:function(t,e){var i=this.option;sI([["start","startValue"],["end","endValue"]],function(e){(null!=t[e[0]]||null!=t[e[1]])&&(i[e[0]]=t[e[0]],i[e[1]]=t[e[1]])},this),!e&&wp(this,t)},getPercentRange:function(){var t=this.findRepresentativeAxisProxy();return t?t.getDataPercentWindow():void 0},getValueRange:function(t,e){if(null!=t||null!=e)return this.getAxisProxy(t,e).getDataValueWindow();var i=this.findRepresentativeAxisProxy();return i?i.getDataValueWindow():void 0},findRepresentativeAxisProxy:function(t){if(t)return t.__dzAxisProxy;var e=this._axisProxies;for(var i in e)if(e.hasOwnProperty(i)&&e[i].hostedBy(this))return e[i];for(var i in e)if(e.hasOwnProperty(i)&&!e[i].hostedBy(this))return e[i]},getRangePropMode:function(){return this._rangePropMode.slice()}}),uI=s_.extend({type:"dataZoom",render:function(t,e,i){this.dataZoomModel=t,this.ecModel=e,this.api=i},getTargetCoordInfo:function(){function t(t,e,i,n){for(var r,a=0;aa&&(e[1-n]=e[n]+u.sign*a),e}),dI=Dy,fI=qa,pI=Ka,gI=y,vI=f,mI=7,yI=1,xI=30,_I="horizontal",wI="vertical",bI=5,SI=["line","bar","candlestick","scatter"],MI=uI.extend({type:"dataZoom.slider",init:function(t,e){this._displayables={},this._orient,this._range,this._handleEnds,this._size,this._handleWidth,this._handleHeight,this._location,this._dragging,this._dataShadowInfo,this.api=e},render:function(t,e,i,n){return MI.superApply(this,"render",arguments),Hs(this,"_dispatchZoomAction",this.dataZoomModel.get("throttle"),"fixRate"),this._orient=t.get("orient"),this.dataZoomModel.get("show")===!1?void this.group.removeAll():(n&&"dataZoom"===n.type&&n.from===this.uid||this._buildView(),void this._updateView())},remove:function(){MI.superApply(this,"remove",arguments),Zs(this,"_dispatchZoomAction")},dispose:function(){MI.superApply(this,"dispose",arguments),Zs(this,"_dispatchZoomAction")},_buildView:function(){var t=this.group;t.removeAll(),this._resetLocation(),this._resetInterval();var e=this._displayables.barGroup=new lv;this._renderBackground(),this._renderHandle(),this._renderDataShadow(),t.add(e),this._positionGroup()},_resetLocation:function(){var t=this.dataZoomModel,e=this.api,i=this._findCoordRect(),n={width:e.getWidth(),height:e.getHeight()},r=this._orient===_I?{right:n.width-i.x-i.width,top:n.height-xI-mI,width:i.width,height:xI}:{right:mI,top:i.y,width:xI,height:i.height},a=Mo(t.option);f(["right","top","width","height"],function(t){"ph"===a[t]&&(a[t]=r[t])});var o=bo(a,n,t.padding);this._location={x:o.x,y:o.y},this._size=[o.width,o.height],this._orient===wI&&this._size.reverse()},_positionGroup:function(){var t=this.group,e=this._location,i=this._orient,n=this.dataZoomModel.getFirstTargetAxisModel(),r=n&&n.get("inverse"),a=this._displayables.barGroup,o=(this._dataShadowInfo||{}).otherAxisInverse;a.attr(i!==_I||r?i===_I&&r?{scale:o?[-1,1]:[-1,-1]}:i!==wI||r?{scale:o?[-1,-1]:[-1,1],rotation:Math.PI/2}:{scale:o?[1,-1]:[1,1],rotation:Math.PI/2}:{scale:o?[1,1]:[1,-1]});var s=t.getBoundingRect([a]);t.attr("position",[e.x-s.x,e.y-s.y])},_getViewExtent:function(){return[0,this._size[0]]},_renderBackground:function(){var t=this.dataZoomModel,e=this._size,i=this._displayables.barGroup;i.add(new dI({silent:!0,shape:{x:0,y:0,width:e[0],height:e[1]},style:{fill:t.get("backgroundColor")},z2:-40})),i.add(new dI({shape:{x:0,y:0,width:e[0],height:e[1]},style:{fill:"transparent"},z2:0,onclick:y(this._onClickPanelClick,this)}))},_renderDataShadow:function(){var t=this._dataShadowInfo=this._prepareDataShadowInfo();if(t){var e=this._size,i=t.series,n=i.getRawData(),r=i.getShadowDim?i.getShadowDim():t.otherDim;if(null!=r){var a=n.getDataExtent(r),o=.3*(a[1]-a[0]);a=[a[0]-o,a[1]+o];var l,h=[0,e[1]],u=[0,e[0]],c=[[e[0],0],[0,0]],d=[],f=u[1]/(n.count()-1),p=0,g=Math.round(n.count()/e[0]);n.each([r],function(t,e){if(g>0&&e%g)return void(p+=f);var i=null==t||isNaN(t)||""===t,n=i?0:fI(t,a,h,!0);i&&!l&&e?(c.push([c[c.length-1][0],0]),d.push([d[d.length-1][0],0])):!i&&l&&(c.push([p,0]),d.push([p,0])),c.push([p,n]),d.push([p,n]),p+=f,l=i});var v=this.dataZoomModel;this._displayables.barGroup.add(new Cy({shape:{points:c},style:s({fill:v.get("dataBackgroundColor")},v.getModel("dataBackground.areaStyle").getAreaStyle()),silent:!0,z2:-20})),this._displayables.barGroup.add(new Ay({shape:{points:d},style:v.getModel("dataBackground.lineStyle").getLineStyle(),silent:!0,z2:-19}))}}},_prepareDataShadowInfo:function(){var t=this.dataZoomModel,e=t.get("showDataShadow");if(e!==!1){var i,n=this.ecModel;return t.eachTargetAxis(function(r,a){var o=t.getAxisProxy(r.name,a).getTargetSeriesModels();f(o,function(t){if(!(i||e!==!0&&h(SI,t.get("type"))<0)){var o,s=n.getComponent(r.axis,a).axis,l=Mp(r.name),u=t.coordinateSystem;null!=l&&u.getOtherAxis&&(o=u.getOtherAxis(s).inverse),l=t.getData().mapDimension(l),i={thisAxis:s,series:t,thisDim:r.name,otherDim:l,otherAxisInverse:o}}},this)},this),i}},_renderHandle:function(){var t=this._displayables,e=t.handles=[],i=t.handleLabels=[],n=this._displayables.barGroup,r=this._size,a=this.dataZoomModel;n.add(t.filler=new dI({draggable:!0,cursor:Ip(this._orient),drift:gI(this._onDragMove,this,"all"),onmousemove:function(t){Ig(t.event)},ondragstart:gI(this._showDataInfo,this,!0),ondragend:gI(this._onDragEnd,this),onmouseover:gI(this._showDataInfo,this,!0),onmouseout:gI(this._showDataInfo,this,!1),style:{fill:a.get("fillerColor"),textPosition:"inside"}})),n.add(new dI(na({silent:!0,shape:{x:0,y:0,width:r[0],height:r[1]},style:{stroke:a.get("dataBackgroundColor")||a.get("borderColor"),lineWidth:yI,fill:"rgba(0,0,0,0)"}}))),vI([0,1],function(t){var r=Va(a.get("handleIcon"),{cursor:Ip(this._orient),draggable:!0,drift:gI(this._onDragMove,this,t),onmousemove:function(t){Ig(t.event) + },ondragend:gI(this._onDragEnd,this),onmouseover:gI(this._showDataInfo,this,!0),onmouseout:gI(this._showDataInfo,this,!1)},{x:-1,y:0,width:2,height:2}),o=r.getBoundingRect();this._handleHeight=Ua(a.get("handleSize"),this._size[1]),this._handleWidth=o.width/o.height*this._handleHeight,r.setStyle(a.getModel("handleStyle").getItemStyle());var s=a.get("handleColor");null!=s&&(r.style.fill=s),n.add(e[t]=r);var l=a.textStyleModel;this.group.add(i[t]=new xy({silent:!0,invisible:!0,style:{x:0,y:0,text:"",textVerticalAlign:"middle",textAlign:"center",textFill:l.getTextColor(),textFont:l.getFont()},z2:10}))},this)},_resetInterval:function(){var t=this._range=this.dataZoomModel.getPercentRange(),e=this._getViewExtent();this._handleEnds=[fI(t[0],[0,100],e,!0),fI(t[1],[0,100],e,!0)]},_updateInterval:function(t,e){var i=this.dataZoomModel,n=this._handleEnds,r=this._getViewExtent(),a=i.findRepresentativeAxisProxy().getMinMaxSpan(),o=[0,100];cI(e,n,r,i.get("zoomLock")?"all":t,null!=a.minSpan?fI(a.minSpan,o,r,!0):null,null!=a.maxSpan?fI(a.maxSpan,o,r,!0):null);var s=this._range,l=this._range=pI([fI(n[0],r,o,!0),fI(n[1],r,o,!0)]);return!s||s[0]!==l[0]||s[1]!==l[1]},_updateView:function(t){var e=this._displayables,i=this._handleEnds,n=pI(i.slice()),r=this._size;vI([0,1],function(t){var n=e.handles[t],a=this._handleHeight;n.attr({scale:[a/2,a/2],position:[i[t],r[1]/2-a/2]})},this),e.filler.setShape({x:n[0],y:0,width:n[1]-n[0],height:r[1]}),this._updateDataInfo(t)},_updateDataInfo:function(t){function e(t){var e=za(n.handles[t].parent,this.group),i=Ra(0===t?"right":"left",e),s=this._handleWidth/2+bI,l=Ea([c[t]+(0===t?-s:s),this._size[1]/2],e);r[t].setStyle({x:l[0],y:l[1],textVerticalAlign:a===_I?"middle":i,textAlign:a===_I?i:"center",text:o[t]})}var i=this.dataZoomModel,n=this._displayables,r=n.handleLabels,a=this._orient,o=["",""];if(i.get("showDetail")){var s=i.findRepresentativeAxisProxy();if(s){var l=s.getAxisModel().axis,h=this._range,u=t?s.calculateDataWindow({start:h[0],end:h[1]}).valueWindow:s.getDataValueWindow();o=[this._formatLabel(u[0],l),this._formatLabel(u[1],l)]}}var c=pI(this._handleEnds.slice());e.call(this,0),e.call(this,1)},_formatLabel:function(t,e){var i=this.dataZoomModel,n=i.get("labelFormatter"),r=i.get("labelPrecision");(null==r||"auto"===r)&&(r=e.getPixelPrecision());var a=null==t||isNaN(t)?"":"category"===e.type||"time"===e.type?e.scale.getLabel(Math.round(t)):t.toFixed(Math.min(r,20));return w(n)?n(t,a):b(n)?n.replace("{value}",a):a},_showDataInfo:function(t){t=this._dragging||t;var e=this._displayables.handleLabels;e[0].attr("invisible",!t),e[1].attr("invisible",!t)},_onDragMove:function(t,e,i){this._dragging=!0;var n=this._displayables.barGroup.getLocalTransform(),r=Ea([e,i],n,!0),a=this._updateInterval(t,r[0]),o=this.dataZoomModel.get("realtime");this._updateView(!o),a&&o&&this._dispatchZoomAction()},_onDragEnd:function(){this._dragging=!1,this._showDataInfo(!1);var t=this.dataZoomModel.get("realtime");!t&&this._dispatchZoomAction()},_onClickPanelClick:function(t){var e=this._size,i=this._displayables.barGroup.transformCoordToLocal(t.offsetX,t.offsetY);if(!(i[0]<0||i[0]>e[0]||i[1]<0||i[1]>e[1])){var n=this._handleEnds,r=(n[0]+n[1])/2,a=this._updateInterval("all",i[0]-r);this._updateView(),a&&this._dispatchZoomAction()}},_dispatchZoomAction:function(){var t=this._range;this.api.dispatchAction({type:"dataZoom",from:this.uid,dataZoomId:this.dataZoomModel.id,start:t[0],end:t[1]})},_findCoordRect:function(){var t;if(vI(this.getTargetCoordInfo(),function(e){if(!t&&e.length){var i=e[0].model.coordinateSystem;t=i.getRect&&i.getRect()}}),!t){var e=this.api.getWidth(),i=this.api.getHeight();t={x:.2*e,y:.2*i,width:.6*e,height:.6*i}}return t}});hI.extend({type:"dataZoom.inside",defaultOption:{disabled:!1,zoomLock:!1,zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!1,preventDefaultMouseMove:!0}});var II="\x00_ec_interaction_mutex";Ul({type:"takeGlobalCursor",event:"globalCursorTaken",update:"update"},function(){}),c(Ap,bg);var TI="\x00_ec_dataZoom_roams",CI=y,AI=uI.extend({type:"dataZoom.inside",init:function(){this._range},render:function(t,e,i){AI.superApply(this,"render",arguments),this._range=t.getPercentRange(),f(this.getTargetCoordInfo(),function(e,n){var r=p(e,function(t){return Fp(t.model)});f(e,function(e){var a=e.model,o={};f(["pan","zoom","scrollMove"],function(t){o[t]=CI(DI[t],this,e,n)},this),Bp(i,{coordId:Fp(a),allCoordIds:r,containsPoint:function(t,e,i){return a.coordinateSystem.containPoint([e,i])},dataZoomId:t.id,dataZoomModel:t,getRange:o})},this)},this)},dispose:function(){Np(this.api,this.dataZoomModel.id),AI.superApply(this,"dispose",arguments),this._range=null}}),DI={zoom:function(t,e,i,n){var r=this._range,a=r.slice(),o=t.axisModels[0];if(o){var s=kI[e](null,[n.originX,n.originY],o,i,t),l=(s.signal>0?s.pixelStart+s.pixelLength-s.pixel:s.pixel-s.pixelStart)/s.pixelLength*(a[1]-a[0])+a[0],h=Math.max(1/n.scale,0);a[0]=(a[0]-l)*h+l,a[1]=(a[1]-l)*h+l;var u=this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();return cI(0,a,[0,100],0,u.minSpan,u.maxSpan),this._range=a,r[0]!==a[0]||r[1]!==a[1]?a:void 0}},pan:Xp(function(t,e,i,n,r,a){var o=kI[n]([a.oldX,a.oldY],[a.newX,a.newY],e,r,i);return o.signal*(t[1]-t[0])*o.pixel/o.pixelLength}),scrollMove:Xp(function(t,e,i,n,r,a){var o=kI[n]([0,0],[a.scrollDelta,a.scrollDelta],e,r,i);return o.signal*(t[1]-t[0])*a.scrollDelta})},kI={grid:function(t,e,i,n,r){var a=i.axis,o={},s=r.model.coordinateSystem.getRect();return t=t||[0,0],"x"===a.dim?(o.pixel=e[0]-t[0],o.pixelLength=s.width,o.pixelStart=s.x,o.signal=a.inverse?1:-1):(o.pixel=e[1]-t[1],o.pixelLength=s.height,o.pixelStart=s.y,o.signal=a.inverse?-1:1),o},polar:function(t,e,i,n,r){var a=i.axis,o={},s=r.model.coordinateSystem,l=s.getRadiusAxis().getExtent(),h=s.getAngleAxis().getExtent();return t=t?s.pointToCoord(t):[0,0],e=s.pointToCoord(e),"radiusAxis"===i.mainType?(o.pixel=e[0]-t[0],o.pixelLength=l[1]-l[0],o.pixelStart=l[0],o.signal=a.inverse?1:-1):(o.pixel=e[1]-t[1],o.pixelLength=h[1]-h[0],o.pixelStart=h[0],o.signal=a.inverse?-1:1),o},singleAxis:function(t,e,i,n,r){var a=i.axis,o=r.model.coordinateSystem.getRect(),s={};return t=t||[0,0],"horizontal"===a.orient?(s.pixel=e[0]-t[0],s.pixelLength=o.width,s.pixelStart=o.x,s.signal=a.inverse?1:-1):(s.pixel=e[1]-t[1],s.pixelLength=o.height,s.pixelStart=o.y,s.signal=a.inverse?-1:1),s}};jl({getTargetSeries:function(t){var e=N();return t.eachComponent("dataZoom",function(t){t.eachTargetAxis(function(t,i,n){var r=n.getAxisProxy(t.name,i);f(r.getTargetSeriesModels(),function(t){e.set(t.uid,t)})})}),e},modifyOutputEnd:!0,overallReset:function(t,e){t.eachComponent("dataZoom",function(t){t.eachTargetAxis(function(t,i,n){n.getAxisProxy(t.name,i).reset(n,e)}),t.eachTargetAxis(function(t,i,n){n.getAxisProxy(t.name,i).filterData(n,e)})}),t.eachComponent("dataZoom",function(t){var e=t.findRepresentativeAxisProxy(),i=e.getDataPercentWindow(),n=e.getDataValueWindow();t.setRawRange({start:i[0],end:i[1],startValue:n[0],endValue:n[1]},!0)})}}),Ul("dataZoom",function(t,e){var i=gp(y(e.eachComponent,e,"dataZoom"),nI,function(t,e){return t.get(e.axisIndex)}),n=[];e.eachComponent({mainType:"dataZoom",query:t},function(t){n.push.apply(n,i(t).nodes)}),f(n,function(e){e.setRawRange({start:t.start,end:t.end,startValue:t.startValue,endValue:t.endValue})})});var PI,LI="urn:schemas-microsoft-com:vml",OI="undefined"==typeof window?null:window,zI=!1,EI=OI&&OI.document;if(EI&&!tg.canvasSupported)try{!EI.namespaces.zrvml&&EI.namespaces.add("zrvml",LI),PI=function(t){return EI.createElement("')}}catch(RI){PI=function(t){return EI.createElement("<"+t+' xmlns="'+LI+'" class="zrvml">')}}var BI=qm.CMD,NI=Math.round,FI=Math.sqrt,VI=Math.abs,WI=Math.cos,GI=Math.sin,HI=Math.max;if(!tg.canvasSupported){var ZI=",",XI="progid:DXImageTransform.Microsoft",YI=21600,jI=YI/2,qI=1e5,UI=1e3,$I=function(t){t.style.cssText="position:absolute;left:0;top:0;width:1px;height:1px;",t.coordsize=YI+","+YI,t.coordorigin="0,0"},KI=function(t){return String(t).replace(/&/g,"&").replace(/"/g,""")},QI=function(t,e,i){return"rgb("+[t,e,i].join(",")+")"},JI=function(t,e){e&&t&&e.parentNode!==t&&t.appendChild(e)},tT=function(t,e){e&&t&&e.parentNode===t&&t.removeChild(e)},eT=function(t,e,i){return(parseFloat(t)||0)*qI+(parseFloat(e)||0)*UI+i},iT=function(t,e){return"string"==typeof t?t.lastIndexOf("%")>=0?parseFloat(t)/100*e:parseFloat(t):t},nT=function(t,e,i){var n=He(e);i=+i,isNaN(i)&&(i=1),n&&(t.color=QI(n[0],n[1],n[2]),t.opacity=i*n[3])},rT=function(t){var e=He(t);return[QI(e[0],e[1],e[2]),e[3]]},aT=function(t,e,i){var n=e.fill;if(null!=n)if(n instanceof Ey){var r,a=0,o=[0,0],s=0,l=1,h=i.getBoundingRect(),u=h.width,c=h.height;if("linear"===n.type){r="gradient";var d=i.transform,f=[n.x*u,n.y*c],p=[n.x2*u,n.y2*c];d&&(ae(f,f,d),ae(p,p,d));var g=p[0]-f[0],v=p[1]-f[1];a=180*Math.atan2(g,v)/Math.PI,0>a&&(a+=360),1e-6>a&&(a=0)}else{r="gradientradial";var f=[n.x*u,n.y*c],d=i.transform,m=i.scale,y=u,x=c;o=[(f[0]-h.x)/y,(f[1]-h.y)/x],d&&ae(f,f,d),y/=m[0]*YI,x/=m[1]*YI;var _=HI(y,x);s=0/_,l=2*n.r/_-s}var w=n.colorStops.slice();w.sort(function(t,e){return t.offset-e.offset});for(var b=w.length,S=[],M=[],I=0;b>I;I++){var T=w[I],C=rT(T.color);M.push(T.offset*l+s+" "+C[0]),(0===I||I===b-1)&&S.push(C)}if(b>=2){var A=S[0][0],D=S[1][0],k=S[0][1]*e.opacity,P=S[1][1]*e.opacity;t.type=r,t.method="none",t.focus="100%",t.angle=a,t.color=A,t.color2=D,t.colors=M.join(","),t.opacity=P,t.opacity2=k}"radial"===r&&(t.focusposition=o.join(","))}else nT(t,n,e.opacity)},oT=function(t,e){null!=e.lineDash&&(t.dashstyle=e.lineDash.join(" ")),null==e.stroke||e.stroke instanceof Ey||nT(t,e.stroke,e.opacity)},sT=function(t,e,i,n){var r="fill"==e,a=t.getElementsByTagName(e)[0];null!=i[e]&&"none"!==i[e]&&(r||!r&&i.lineWidth)?(t[r?"filled":"stroked"]="true",i[e]instanceof Ey&&tT(t,a),a||(a=Yp(e)),r?aT(a,i,n):oT(a,i),JI(t,a)):(t[r?"filled":"stroked"]="false",tT(t,a))},lT=[[],[],[]],hT=function(t,e){var i,n,r,a,o,s,l=BI.M,h=BI.C,u=BI.L,c=BI.A,d=BI.Q,f=[],p=t.data,g=t.len();for(a=0;g>a;){switch(r=p[a++],n="",i=0,r){case l:n=" m ",i=1,o=p[a++],s=p[a++],lT[0][0]=o,lT[0][1]=s;break;case u:n=" l ",i=1,o=p[a++],s=p[a++],lT[0][0]=o,lT[0][1]=s;break;case d:case h:n=" c ",i=3;var v,m,y=p[a++],x=p[a++],_=p[a++],w=p[a++];r===d?(v=_,m=w,_=(_+2*y)/3,w=(w+2*x)/3,y=(o+2*y)/3,x=(s+2*x)/3):(v=p[a++],m=p[a++]),lT[0][0]=y,lT[0][1]=x,lT[1][0]=_,lT[1][1]=w,lT[2][0]=v,lT[2][1]=m,o=v,s=m;break;case c:var b=0,S=0,M=1,I=1,T=0;e&&(b=e[4],S=e[5],M=FI(e[0]*e[0]+e[1]*e[1]),I=FI(e[2]*e[2]+e[3]*e[3]),T=Math.atan2(-e[1]/I,e[0]/M));var C=p[a++],A=p[a++],D=p[a++],k=p[a++],P=p[a++]+T,L=p[a++]+P+T;a++;var O=p[a++],z=C+WI(P)*D,E=A+GI(P)*k,y=C+WI(L)*D,x=A+GI(L)*k,R=O?" wa ":" at ";Math.abs(z-y)<1e-4&&(Math.abs(L-P)>.01?O&&(z+=270/YI):Math.abs(E-A)<1e-4?O&&C>z||!O&&z>C?x-=270/YI:x+=270/YI:O&&A>E||!O&&E>A?y+=270/YI:y-=270/YI),f.push(R,NI(((C-D)*M+b)*YI-jI),ZI,NI(((A-k)*I+S)*YI-jI),ZI,NI(((C+D)*M+b)*YI-jI),ZI,NI(((A+k)*I+S)*YI-jI),ZI,NI((z*M+b)*YI-jI),ZI,NI((E*I+S)*YI-jI),ZI,NI((y*M+b)*YI-jI),ZI,NI((x*I+S)*YI-jI)),o=y,s=x;break;case BI.R:var B=lT[0],N=lT[1];B[0]=p[a++],B[1]=p[a++],N[0]=B[0]+p[a++],N[1]=B[1]+p[a++],e&&(ae(B,B,e),ae(N,N,e)),B[0]=NI(B[0]*YI-jI),N[0]=NI(N[0]*YI-jI),B[1]=NI(B[1]*YI-jI),N[1]=NI(N[1]*YI-jI),f.push(" m ",B[0],ZI,B[1]," l ",N[0],ZI,B[1]," l ",N[0],ZI,N[1]," l ",B[0],ZI,N[1]);break;case BI.Z:f.push(" x ")}if(i>0){f.push(n);for(var F=0;i>F;F++){var V=lT[F];e&&ae(V,V,e),f.push(NI(V[0]*YI-jI),ZI,NI(V[1]*YI-jI),i-1>F?ZI:"")}}}return f.join("")};Fr.prototype.brushVML=function(t){var e=this.style,i=this._vmlEl;i||(i=Yp("shape"),$I(i),this._vmlEl=i),sT(i,"fill",e,this),sT(i,"stroke",e,this);var n=this.transform,r=null!=n,a=i.getElementsByTagName("stroke")[0];if(a){var o=e.lineWidth;if(r&&!e.strokeNoScale){var s=n[0]*n[3]-n[1]*n[2];o*=FI(VI(s))}a.weight=o+"px"}var l=this.path||(this.path=new qm);this.__dirtyPath&&(l.beginPath(),this.buildPath(l,this.shape),l.toStatic(),this.__dirtyPath=!1),i.path=hT(l,this.transform),i.style.zIndex=eT(this.zlevel,this.z,this.z2),JI(t,i),null!=e.text?this.drawRectText(t,this.getBoundingRect()):this.removeRectText(t)},Fr.prototype.onRemove=function(t){tT(t,this._vmlEl),this.removeRectText(t)},Fr.prototype.onAdd=function(t){JI(t,this._vmlEl),this.appendRectText(t)};var uT=function(t){return"object"==typeof t&&t.tagName&&"IMG"===t.tagName.toUpperCase()};yn.prototype.brushVML=function(t){var e,i,n=this.style,r=n.image;if(uT(r)){var a=r.src;if(a===this._imageSrc)e=this._imageWidth,i=this._imageHeight;else{var o=r.runtimeStyle,s=o.width,l=o.height;o.width="auto",o.height="auto",e=r.width,i=r.height,o.width=s,o.height=l,this._imageSrc=a,this._imageWidth=e,this._imageHeight=i}r=a}else r===this._imageSrc&&(e=this._imageWidth,i=this._imageHeight);if(r){var h=n.x||0,u=n.y||0,c=n.width,d=n.height,f=n.sWidth,p=n.sHeight,g=n.sx||0,v=n.sy||0,m=f&&p,y=this._vmlEl;y||(y=EI.createElement("div"),$I(y),this._vmlEl=y);var x,_=y.style,w=!1,b=1,S=1;if(this.transform&&(x=this.transform,b=FI(x[0]*x[0]+x[1]*x[1]),S=FI(x[2]*x[2]+x[3]*x[3]),w=x[1]||x[2]),w){var M=[h,u],I=[h+c,u],T=[h,u+d],C=[h+c,u+d];ae(M,M,x),ae(I,I,x),ae(T,T,x),ae(C,C,x);var A=HI(M[0],I[0],T[0],C[0]),D=HI(M[1],I[1],T[1],C[1]),k=[];k.push("M11=",x[0]/b,ZI,"M12=",x[2]/S,ZI,"M21=",x[1]/b,ZI,"M22=",x[3]/S,ZI,"Dx=",NI(h*b+x[4]),ZI,"Dy=",NI(u*S+x[5])),_.padding="0 "+NI(A)+"px "+NI(D)+"px 0",_.filter=XI+".Matrix("+k.join("")+", SizingMethod=clip)"}else x&&(h=h*b+x[4],u=u*S+x[5]),_.filter="",_.left=NI(h)+"px",_.top=NI(u)+"px";var P=this._imageEl,L=this._cropEl;P||(P=EI.createElement("div"),this._imageEl=P);var O=P.style;if(m){if(e&&i)O.width=NI(b*e*c/f)+"px",O.height=NI(S*i*d/p)+"px";else{var z=new Image,E=this;z.onload=function(){z.onload=null,e=z.width,i=z.height,O.width=NI(b*e*c/f)+"px",O.height=NI(S*i*d/p)+"px",E._imageWidth=e,E._imageHeight=i,E._imageSrc=r},z.src=r}L||(L=EI.createElement("div"),L.style.overflow="hidden",this._cropEl=L);var R=L.style;R.width=NI((c+g*c/f)*b),R.height=NI((d+v*d/p)*S),R.filter=XI+".Matrix(Dx="+-g*c/f*b+",Dy="+-v*d/p*S+")",L.parentNode||y.appendChild(L),P.parentNode!=L&&L.appendChild(P)}else O.width=NI(b*c)+"px",O.height=NI(S*d)+"px",y.appendChild(P),L&&L.parentNode&&(y.removeChild(L),this._cropEl=null);var B="",N=n.opacity;1>N&&(B+=".Alpha(opacity="+NI(100*N)+") "),B+=XI+".AlphaImageLoader(src="+r+", SizingMethod=scale)",O.filter=B,y.style.zIndex=eT(this.zlevel,this.z,this.z2),JI(t,y),null!=n.text&&this.drawRectText(t,this.getBoundingRect())}},yn.prototype.onRemove=function(t){tT(t,this._vmlEl),this._vmlEl=null,this._cropEl=null,this._imageEl=null,this.removeRectText(t)},yn.prototype.onAdd=function(t){JI(t,this._vmlEl),this.appendRectText(t)};var cT,dT="normal",fT={},pT=0,gT=100,vT=document.createElement("div"),mT=function(t){var e=fT[t];if(!e){pT>gT&&(pT=0,fT={});var i,n=vT.style;try{n.font=t,i=n.fontFamily.split(",")[0]}catch(r){}e={style:n.fontStyle||dT,variant:n.fontVariant||dT,weight:n.fontWeight||dT,size:0|parseFloat(n.fontSize||12),family:i||"Microsoft YaHei"},fT[t]=e,pT++}return e};Oi("measureText",function(t,e){var i=EI;cT||(cT=i.createElement("div"),cT.style.cssText="position:absolute;top:-20000px;left:0;padding:0;margin:0;border:none;white-space:pre;",EI.body.appendChild(cT));try{cT.style.font=e}catch(n){}return cT.innerHTML="",cT.appendChild(i.createTextNode(t)),{width:cT.offsetWidth}});for(var yT=new gi,xT=function(t,e,i,n){var r=this.style;this.__dirty&&Qi(r,!0);var a=r.text;if(null!=a&&(a+=""),a){if(r.rich){var o=qi(a,r);a=[];for(var s=0;s>> 6) & 0x1F), + 0x80 | (x & 0x3F)); + } else if (x <= 0xFFFF) { + output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), + 0x80 | ((x >>> 6) & 0x3F), + 0x80 | (x & 0x3F)); + } else if (x <= 0x1FFFFF) { + output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), + 0x80 | ((x >>> 12) & 0x3F), + 0x80 | ((x >>> 6) & 0x3F), + 0x80 | (x & 0x3F)); + } + } + } + return output; + } + + function utf8Decode(str) { + var i, ac, c1, c2, c3, arr = [], + l; + i = ac = c1 = c2 = c3 = 0; + + if (str && str.length) { + l = str.length; + str += ''; + + while (i < l) { + c1 = str.charCodeAt(i); + ac += 1; + if (c1 < 128) { + arr[ac] = String.fromCharCode(c1); + i += 1; + } else if (c1 > 191 && c1 < 224) { + c2 = str.charCodeAt(i + 1); + arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63)); + i += 2; + } else { + c2 = str.charCodeAt(i + 1); + c3 = str.charCodeAt(i + 2); + arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + } + } + return arr.join(''); + } + + /** + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ + + function safe_add(x, y) { + var lsw = (x & 0xFFFF) + (y & 0xFFFF), + msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); + } + + /** + * Bitwise rotate a 32-bit number to the left. + */ + + function bit_rol(num, cnt) { + return (num << cnt) | (num >>> (32 - cnt)); + } + + /** + * Convert a raw string to a hex string + */ + + function rstr2hex(input, hexcase) { + var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef', + output = '', + x, i = 0, + l = input.length; + for (; i < l; i += 1) { + x = input.charCodeAt(i); + output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F); + } + return output; + } + + /** + * Encode a string as utf-16 + */ + + function str2rstr_utf16le(input) { + var i, l = input.length, + output = ''; + for (i = 0; i < l; i += 1) { + output += String.fromCharCode(input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF); + } + return output; + } + + function str2rstr_utf16be(input) { + var i, l = input.length, + output = ''; + for (i = 0; i < l; i += 1) { + output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF); + } + return output; + } + + /** + * Convert an array of big-endian words to a string + */ + + function binb2rstr(input) { + var i, l = input.length * 32, + output = ''; + for (i = 0; i < l; i += 8) { + output += String.fromCharCode((input[i >> 5] >>> (24 - i % 32)) & 0xFF); + } + return output; + } + + /** + * Convert an array of little-endian words to a string + */ + + function binl2rstr(input) { + var i, l = input.length * 32, + output = ''; + for (i = 0; i < l; i += 8) { + output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF); + } + return output; + } + + /** + * Convert a raw string to an array of little-endian words + * Characters >255 have their high-byte silently ignored. + */ + + function rstr2binl(input) { + var i, l = input.length * 8, + output = Array(input.length >> 2), + lo = output.length; + for (i = 0; i < lo; i += 1) { + output[i] = 0; + } + for (i = 0; i < l; i += 8) { + output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32); + } + return output; + } + + /** + * Convert a raw string to an array of big-endian words + * Characters >255 have their high-byte silently ignored. + */ + + function rstr2binb(input) { + var i, l = input.length * 8, + output = Array(input.length >> 2), + lo = output.length; + for (i = 0; i < lo; i += 1) { + output[i] = 0; + } + for (i = 0; i < l; i += 8) { + output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32); + } + return output; + } + + /** + * Convert a raw string to an arbitrary string encoding + */ + + function rstr2any(input, encoding) { + var divisor = encoding.length, + remainders = Array(), + i, q, x, ld, quotient, dividend, output, full_length; + + /* Convert to an array of 16-bit big-endian values, forming the dividend */ + dividend = Array(Math.ceil(input.length / 2)); + ld = dividend.length; + for (i = 0; i < ld; i += 1) { + dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); + } + + /** + * Repeatedly perform a long division. The binary array forms the dividend, + * the length of the encoding is the divisor. Once computed, the quotient + * forms the dividend for the next step. We stop when the dividend is zerHashes. + * All remainders are stored for later use. + */ + while (dividend.length > 0) { + quotient = Array(); + x = 0; + for (i = 0; i < dividend.length; i += 1) { + x = (x << 16) + dividend[i]; + q = Math.floor(x / divisor); + x -= q * divisor; + if (quotient.length > 0 || q > 0) { + quotient[quotient.length] = q; + } + } + remainders[remainders.length] = x; + dividend = quotient; + } + + /* Convert the remainders to the output string */ + output = ''; + for (i = remainders.length - 1; i >= 0; i--) { + output += encoding.charAt(remainders[i]); + } + + /* Append leading zero equivalents */ + full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2))); + for (i = output.length; i < full_length; i += 1) { + output = encoding[0] + output; + } + return output; + } + + /** + * Convert a raw string to a base-64 string + */ + + function rstr2b64(input, b64pad) { + var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', + output = '', + len = input.length, + i, j, triplet; + b64pad = b64pad || '='; + for (i = 0; i < len; i += 3) { + triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0); + for (j = 0; j < 4; j += 1) { + if (i * 8 + j * 6 > input.length * 8) { + output += b64pad; + } else { + output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F); + } + } + } + return output; + } + + Hashes = { + /** + * @property {String} version + * @readonly + */ + VERSION: '1.0.6', + /** + * @member Hashes + * @class Base64 + * @constructor + */ + Base64: function () { + // private properties + var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', + pad = '=', // default pad according with the RFC standard + url = false, // URL encoding support @todo + utf8 = true; // by default enable UTF-8 support encoding + + // public method for encoding + this.encode = function (input) { + var i, j, triplet, + output = '', + len = input.length; + + pad = pad || '='; + input = (utf8) ? utf8Encode(input) : input; + + for (i = 0; i < len; i += 3) { + triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0); + for (j = 0; j < 4; j += 1) { + if (i * 8 + j * 6 > len * 8) { + output += pad; + } else { + output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F); + } + } + } + return output; + }; + + // public method for decoding + this.decode = function (input) { + // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + var i, o1, o2, o3, h1, h2, h3, h4, bits, ac, + dec = '', + arr = []; + if (!input) { + return input; + } + + i = ac = 0; + input = input.replace(new RegExp('\\' + pad, 'gi'), ''); // use '=' + //input += ''; + + do { // unpack four hexets into three octets using index points in b64 + h1 = tab.indexOf(input.charAt(i += 1)); + h2 = tab.indexOf(input.charAt(i += 1)); + h3 = tab.indexOf(input.charAt(i += 1)); + h4 = tab.indexOf(input.charAt(i += 1)); + + bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; + + o1 = bits >> 16 & 0xff; + o2 = bits >> 8 & 0xff; + o3 = bits & 0xff; + ac += 1; + + if (h3 === 64) { + arr[ac] = String.fromCharCode(o1); + } else if (h4 === 64) { + arr[ac] = String.fromCharCode(o1, o2); + } else { + arr[ac] = String.fromCharCode(o1, o2, o3); + } + } while (i < input.length); + + dec = arr.join(''); + dec = (utf8) ? utf8Decode(dec) : dec; + + return dec; + }; + + // set custom pad string + this.setPad = function (str) { + pad = str || pad; + return this; + }; + // set custom tab string characters + this.setTab = function (str) { + tab = str || tab; + return this; + }; + this.setUTF8 = function (bool) { + if (typeof bool === 'boolean') { + utf8 = bool; + } + return this; + }; + }, + + /** + * CRC-32 calculation + * @member Hashes + * @method CRC32 + * @static + * @param {String} str Input String + * @return {String} + */ + CRC32: function (str) { + var crc = 0, + x = 0, + y = 0, + table, i, iTop; + str = utf8Encode(str); + + table = [ + '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ', + '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ', + '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ', + '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ', + 'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ', + '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ', + 'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ', + '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ', + 'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ', + '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ', + 'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ', + '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ', + 'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ', + '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ', + '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ', + '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ', + '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ', + 'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ', + '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ', + 'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ', + '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ', + 'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ', + '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ', + 'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ', + '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ', + 'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D' + ].join(''); + + crc = crc ^ (-1); + for (i = 0, iTop = str.length; i < iTop; i += 1) { + y = (crc ^ str.charCodeAt(i)) & 0xFF; + x = '0x' + table.substr(y * 9, 8); + crc = (crc >>> 8) ^ x; + } + // always return a positive number (that's what >>> 0 does) + return (crc ^ (-1)) >>> 0; + }, + /** + * @member Hashes + * @class MD5 + * @constructor + * @param {Object} [config] + * + * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + * Digest Algorithm, as defined in RFC 1321. + * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * See for more infHashes. + */ + MD5: function (options) { + /** + * Private config properties. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase} + */ + var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase + b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance + utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding + + // privileged (public) methods + this.hex = function (s) { + return rstr2hex(rstr(s, utf8), hexcase); + }; + this.b64 = function (s) { + return rstr2b64(rstr(s), b64pad); + }; + this.any = function (s, e) { + return rstr2any(rstr(s, utf8), e); + }; + this.raw = function (s) { + return rstr(s, utf8); + }; + this.hex_hmac = function (k, d) { + return rstr2hex(rstr_hmac(k, d), hexcase); + }; + this.b64_hmac = function (k, d) { + return rstr2b64(rstr_hmac(k, d), b64pad); + }; + this.any_hmac = function (k, d, e) { + return rstr2any(rstr_hmac(k, d), e); + }; + /** + * Perform a simple self-test to see if the VM is working + * @return {String} Hexadecimal hash sample + */ + this.vm_test = function () { + return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72'; + }; + /** + * Enable/disable uppercase hexadecimal returned string + * @param {Boolean} + * @return {Object} this + */ + this.setUpperCase = function (a) { + if (typeof a === 'boolean') { + hexcase = a; + } + return this; + }; + /** + * Defines a base64 pad string + * @param {String} Pad + * @return {Object} this + */ + this.setPad = function (a) { + b64pad = a || b64pad; + return this; + }; + /** + * Defines a base64 pad string + * @param {Boolean} + * @return {Object} [this] + */ + this.setUTF8 = function (a) { + if (typeof a === 'boolean') { + utf8 = a; + } + return this; + }; + + // private methods + + /** + * Calculate the MD5 of a raw string + */ + + function rstr(s) { + s = (utf8) ? utf8Encode(s) : s; + return binl2rstr(binl(rstr2binl(s), s.length * 8)); + } + + /** + * Calculate the HMAC-MD5, of a key and some data (raw strings) + */ + + function rstr_hmac(key, data) { + var bkey, ipad, opad, hash, i; + + key = (utf8) ? utf8Encode(key) : key; + data = (utf8) ? utf8Encode(data) : data; + bkey = rstr2binl(key); + if (bkey.length > 16) { + bkey = binl(bkey, key.length * 8); + } + + ipad = Array(16), opad = Array(16); + for (i = 0; i < 16; i += 1) { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8); + return binl2rstr(binl(opad.concat(hash), 512 + 128)); + } + + /** + * Calculate the MD5 of an array of little-endian words, and a bit length. + */ + + function binl(x, len) { + var i, olda, oldb, oldc, oldd, + a = 1732584193, + b = -271733879, + c = -1732584194, + d = 271733878; + + /* append padding */ + x[len >> 5] |= 0x80 << ((len) % 32); + x[(((len + 64) >>> 9) << 4) + 14] = len; + + for (i = 0; i < x.length; i += 16) { + olda = a; + oldb = b; + oldc = c; + oldd = d; + + a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936); + d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586); + c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819); + b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330); + a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897); + d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426); + c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341); + b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983); + a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416); + d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417); + c = md5_ff(c, d, a, b, x[i + 10], 17, -42063); + b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162); + a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682); + d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101); + c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290); + b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329); + + a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510); + d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632); + c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713); + b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302); + a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691); + d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083); + c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335); + b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848); + a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438); + d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690); + c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961); + b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501); + a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467); + d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784); + c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473); + b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734); + + a = md5_hh(a, b, c, d, x[i + 5], 4, -378558); + d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463); + c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562); + b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556); + a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060); + d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353); + c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632); + b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640); + a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174); + d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222); + c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979); + b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189); + a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487); + d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835); + c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520); + b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651); + + a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844); + d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415); + c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905); + b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055); + a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571); + d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606); + c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523); + b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799); + a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359); + d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744); + c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380); + b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649); + a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070); + d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379); + c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259); + b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551); + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + } + return Array(a, b, c, d); + } + + /** + * These functions implement the four basic operations the algorithm uses. + */ + + function md5_cmn(q, a, b, x, s, t) { + return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b); + } + + function md5_ff(a, b, c, d, x, s, t) { + return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); + } + + function md5_gg(a, b, c, d, x, s, t) { + return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); + } + + function md5_hh(a, b, c, d, x, s, t) { + return md5_cmn(b ^ c ^ d, a, b, x, s, t); + } + + function md5_ii(a, b, c, d, x, s, t) { + return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); + } + }, + /** + * @member Hashes + * @class Hashes.SHA1 + * @param {Object} [config] + * @constructor + * + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1 + * Version 2.2 Copyright Paul Johnston 2000 - 2009. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * See http://pajhome.org.uk/crypt/md5 for details. + */ + SHA1: function (options) { + /** + * Private config properties. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase} + */ + var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase + b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance + utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding + + // public methods + this.hex = function (s) { + return rstr2hex(rstr(s, utf8), hexcase); + }; + this.b64 = function (s) { + return rstr2b64(rstr(s, utf8), b64pad); + }; + this.any = function (s, e) { + return rstr2any(rstr(s, utf8), e); + }; + this.raw = function (s) { + return rstr(s, utf8); + }; + this.hex_hmac = function (k, d) { + return rstr2hex(rstr_hmac(k, d)); + }; + this.b64_hmac = function (k, d) { + return rstr2b64(rstr_hmac(k, d), b64pad); + }; + this.any_hmac = function (k, d, e) { + return rstr2any(rstr_hmac(k, d), e); + }; + /** + * Perform a simple self-test to see if the VM is working + * @return {String} Hexadecimal hash sample + * @public + */ + this.vm_test = function () { + return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72'; + }; + /** + * @description Enable/disable uppercase hexadecimal returned string + * @param {boolean} + * @return {Object} this + * @public + */ + this.setUpperCase = function (a) { + if (typeof a === 'boolean') { + hexcase = a; + } + return this; + }; + /** + * @description Defines a base64 pad string + * @param {string} Pad + * @return {Object} this + * @public + */ + this.setPad = function (a) { + b64pad = a || b64pad; + return this; + }; + /** + * @description Defines a base64 pad string + * @param {boolean} + * @return {Object} this + * @public + */ + this.setUTF8 = function (a) { + if (typeof a === 'boolean') { + utf8 = a; + } + return this; + }; + + // private methods + + /** + * Calculate the SHA-512 of a raw string + */ + + function rstr(s) { + s = (utf8) ? utf8Encode(s) : s; + return binb2rstr(binb(rstr2binb(s), s.length * 8)); + } + + /** + * Calculate the HMAC-SHA1 of a key and some data (raw strings) + */ + + function rstr_hmac(key, data) { + var bkey, ipad, opad, i, hash; + key = (utf8) ? utf8Encode(key) : key; + data = (utf8) ? utf8Encode(data) : data; + bkey = rstr2binb(key); + + if (bkey.length > 16) { + bkey = binb(bkey, key.length * 8); + } + ipad = Array(16), opad = Array(16); + for (i = 0; i < 16; i += 1) { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8); + return binb2rstr(binb(opad.concat(hash), 512 + 160)); + } + + /** + * Calculate the SHA-1 of an array of big-endian words, and a bit length + */ + + function binb(x, len) { + var i, j, t, olda, oldb, oldc, oldd, olde, + w = Array(80), + a = 1732584193, + b = -271733879, + c = -1732584194, + d = 271733878, + e = -1009589776; + + /* append padding */ + x[len >> 5] |= 0x80 << (24 - len % 32); + x[((len + 64 >> 9) << 4) + 15] = len; + + for (i = 0; i < x.length; i += 16) { + olda = a; + oldb = b; + oldc = c; + oldd = d; + olde = e; + + for (j = 0; j < 80; j += 1) { + if (j < 16) { + w[j] = x[i + j]; + } else { + w[j] = bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); + } + t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)), + safe_add(safe_add(e, w[j]), sha1_kt(j))); + e = d; + d = c; + c = bit_rol(b, 30); + b = a; + a = t; + } + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + e = safe_add(e, olde); + } + return Array(a, b, c, d, e); + } + + /** + * Perform the appropriate triplet combination function for the current + * iteration + */ + + function sha1_ft(t, b, c, d) { + if (t < 20) { + return (b & c) | ((~b) & d); + } + if (t < 40) { + return b ^ c ^ d; + } + if (t < 60) { + return (b & c) | (b & d) | (c & d); + } + return b ^ c ^ d; + } + + /** + * Determine the appropriate additive constant for the current iteration + */ + + function sha1_kt(t) { + return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : + (t < 60) ? -1894007588 : -899497514; + } + }, + /** + * @class Hashes.SHA256 + * @param {config} + * + * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2 + * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * See http://pajhome.org.uk/crypt/md5 for details. + * Also http://anmar.eu.org/projects/jssha2/ + */ + SHA256: function (options) { + /** + * Private properties configuration variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + * @see this.setUpperCase() method + * @see this.setPad() method + */ + var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase */ + b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', + /* base-64 pad character. Default '=' for strict RFC compliance */ + utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, + /* enable/disable utf8 encoding */ + sha256_K; + + /* privileged (public) methods */ + this.hex = function (s) { + return rstr2hex(rstr(s, utf8)); + }; + this.b64 = function (s) { + return rstr2b64(rstr(s, utf8), b64pad); + }; + this.any = function (s, e) { + return rstr2any(rstr(s, utf8), e); + }; + this.raw = function (s) { + return rstr(s, utf8); + }; + this.hex_hmac = function (k, d) { + return rstr2hex(rstr_hmac(k, d)); + }; + this.b64_hmac = function (k, d) { + return rstr2b64(rstr_hmac(k, d), b64pad); + }; + this.any_hmac = function (k, d, e) { + return rstr2any(rstr_hmac(k, d), e); + }; + /** + * Perform a simple self-test to see if the VM is working + * @return {String} Hexadecimal hash sample + * @public + */ + this.vm_test = function () { + return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72'; + }; + /** + * Enable/disable uppercase hexadecimal returned string + * @param {boolean} + * @return {Object} this + * @public + */ + this.setUpperCase = function (a) { + if (typeof a === 'boolean') { + hexcase = a; + } + return this; + }; + /** + * @description Defines a base64 pad string + * @param {string} Pad + * @return {Object} this + * @public + */ + this.setPad = function (a) { + b64pad = a || b64pad; + return this; + }; + /** + * Defines a base64 pad string + * @param {boolean} + * @return {Object} this + * @public + */ + this.setUTF8 = function (a) { + if (typeof a === 'boolean') { + utf8 = a; + } + return this; + }; + + // private methods + + /** + * Calculate the SHA-512 of a raw string + */ + + function rstr(s, utf8) { + s = (utf8) ? utf8Encode(s) : s; + return binb2rstr(binb(rstr2binb(s), s.length * 8)); + } + + /** + * Calculate the HMAC-sha256 of a key and some data (raw strings) + */ + + function rstr_hmac(key, data) { + key = (utf8) ? utf8Encode(key) : key; + data = (utf8) ? utf8Encode(data) : data; + var hash, i = 0, + bkey = rstr2binb(key), + ipad = Array(16), + opad = Array(16); + + if (bkey.length > 16) { + bkey = binb(bkey, key.length * 8); + } + + for (; i < 16; i += 1) { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8); + return binb2rstr(binb(opad.concat(hash), 512 + 256)); + } + + /* + * Main sha256 function, with its support functions + */ + + function sha256_S(X, n) { + return (X >>> n) | (X << (32 - n)); + } + + function sha256_R(X, n) { + return (X >>> n); + } + + function sha256_Ch(x, y, z) { + return ((x & y) ^ ((~x) & z)); + } + + function sha256_Maj(x, y, z) { + return ((x & y) ^ (x & z) ^ (y & z)); + } + + function sha256_Sigma0256(x) { + return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22)); + } + + function sha256_Sigma1256(x) { + return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25)); + } + + function sha256_Gamma0256(x) { + return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3)); + } + + function sha256_Gamma1256(x) { + return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10)); + } + + function sha256_Sigma0512(x) { + return (sha256_S(x, 28) ^ sha256_S(x, 34) ^ sha256_S(x, 39)); + } + + function sha256_Sigma1512(x) { + return (sha256_S(x, 14) ^ sha256_S(x, 18) ^ sha256_S(x, 41)); + } + + function sha256_Gamma0512(x) { + return (sha256_S(x, 1) ^ sha256_S(x, 8) ^ sha256_R(x, 7)); + } + + function sha256_Gamma1512(x) { + return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6)); + } + + sha256_K = [ + 1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993, -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987, + 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522, + 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585, + 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, + 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885, -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344, + 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, + 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872, -1866530822, -1538233109, -1090935817, -965641998 + ]; + + function binb(m, l) { + var HASH = [1779033703, -1150833019, 1013904242, -1521486534, + 1359893119, -1694144372, 528734635, 1541459225 + ]; + var W = new Array(64); + var a, b, c, d, e, f, g, h; + var i, j, T1, T2; + + /* append padding */ + m[l >> 5] |= 0x80 << (24 - l % 32); + m[((l + 64 >> 9) << 4) + 15] = l; + + for (i = 0; i < m.length; i += 16) { + a = HASH[0]; + b = HASH[1]; + c = HASH[2]; + d = HASH[3]; + e = HASH[4]; + f = HASH[5]; + g = HASH[6]; + h = HASH[7]; + + for (j = 0; j < 64; j += 1) { + if (j < 16) { + W[j] = m[j + i]; + } else { + W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]), + sha256_Gamma0256(W[j - 15])), W[j - 16]); + } + + T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)), + sha256_K[j]), W[j]); + T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c)); + h = g; + g = f; + f = e; + e = safe_add(d, T1); + d = c; + c = b; + b = a; + a = safe_add(T1, T2); + } + + HASH[0] = safe_add(a, HASH[0]); + HASH[1] = safe_add(b, HASH[1]); + HASH[2] = safe_add(c, HASH[2]); + HASH[3] = safe_add(d, HASH[3]); + HASH[4] = safe_add(e, HASH[4]); + HASH[5] = safe_add(f, HASH[5]); + HASH[6] = safe_add(g, HASH[6]); + HASH[7] = safe_add(h, HASH[7]); + } + return HASH; + } + + }, + + /** + * @class Hashes.SHA512 + * @param {config} + * + * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2 + * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * See http://pajhome.org.uk/crypt/md5 for details. + */ + SHA512: function (options) { + /** + * Private properties configuration variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + * @see this.setUpperCase() method + * @see this.setPad() method + */ + var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, + /* hexadecimal output case format. false - lowercase; true - uppercase */ + b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', + /* base-64 pad character. Default '=' for strict RFC compliance */ + utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, + /* enable/disable utf8 encoding */ + sha512_k; + + /* privileged (public) methods */ + this.hex = function (s) { + return rstr2hex(rstr(s)); + }; + this.b64 = function (s) { + return rstr2b64(rstr(s), b64pad); + }; + this.any = function (s, e) { + return rstr2any(rstr(s), e); + }; + this.raw = function (s) { + return rstr(s, utf8); + }; + this.hex_hmac = function (k, d) { + return rstr2hex(rstr_hmac(k, d)); + }; + this.b64_hmac = function (k, d) { + return rstr2b64(rstr_hmac(k, d), b64pad); + }; + this.any_hmac = function (k, d, e) { + return rstr2any(rstr_hmac(k, d), e); + }; + /** + * Perform a simple self-test to see if the VM is working + * @return {String} Hexadecimal hash sample + * @public + */ + this.vm_test = function () { + return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72'; + }; + /** + * @description Enable/disable uppercase hexadecimal returned string + * @param {boolean} + * @return {Object} this + * @public + */ + this.setUpperCase = function (a) { + if (typeof a === 'boolean') { + hexcase = a; + } + return this; + }; + /** + * @description Defines a base64 pad string + * @param {string} Pad + * @return {Object} this + * @public + */ + this.setPad = function (a) { + b64pad = a || b64pad; + return this; + }; + /** + * @description Defines a base64 pad string + * @param {boolean} + * @return {Object} this + * @public + */ + this.setUTF8 = function (a) { + if (typeof a === 'boolean') { + utf8 = a; + } + return this; + }; + + /* private methods */ + + /** + * Calculate the SHA-512 of a raw string + */ + + function rstr(s) { + s = (utf8) ? utf8Encode(s) : s; + return binb2rstr(binb(rstr2binb(s), s.length * 8)); + } + /* + * Calculate the HMAC-SHA-512 of a key and some data (raw strings) + */ + + function rstr_hmac(key, data) { + key = (utf8) ? utf8Encode(key) : key; + data = (utf8) ? utf8Encode(data) : data; + + var hash, i = 0, + bkey = rstr2binb(key), + ipad = Array(32), + opad = Array(32); + + if (bkey.length > 32) { + bkey = binb(bkey, key.length * 8); + } + + for (; i < 32; i += 1) { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8); + return binb2rstr(binb(opad.concat(hash), 1024 + 512)); + } + + /** + * Calculate the SHA-512 of an array of big-endian dwords, and a bit length + */ + + function binb(x, len) { + var j, i, l, + W = new Array(80), + hash = new Array(16), + //Initial hash values + H = [ + new int64(0x6a09e667, -205731576), + new int64(-1150833019, -2067093701), + new int64(0x3c6ef372, -23791573), + new int64(-1521486534, 0x5f1d36f1), + new int64(0x510e527f, -1377402159), + new int64(-1694144372, 0x2b3e6c1f), + new int64(0x1f83d9ab, -79577749), + new int64(0x5be0cd19, 0x137e2179) + ], + T1 = new int64(0, 0), + T2 = new int64(0, 0), + a = new int64(0, 0), + b = new int64(0, 0), + c = new int64(0, 0), + d = new int64(0, 0), + e = new int64(0, 0), + f = new int64(0, 0), + g = new int64(0, 0), + h = new int64(0, 0), + //Temporary variables not specified by the document + s0 = new int64(0, 0), + s1 = new int64(0, 0), + Ch = new int64(0, 0), + Maj = new int64(0, 0), + r1 = new int64(0, 0), + r2 = new int64(0, 0), + r3 = new int64(0, 0); + + if (sha512_k === undefined) { + //SHA512 constants + sha512_k = [ + new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd), + new int64(-1245643825, -330482897), new int64(-373957723, -2121671748), + new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031), + new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736), + new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe), + new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302), + new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1), + new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428), + new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3), + new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65), + new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483), + new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459), + new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210), + new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340), + new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395), + new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70), + new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926), + new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473), + new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8), + new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b), + new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023), + new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30), + new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910), + new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8), + new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53), + new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016), + new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893), + new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397), + new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60), + new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec), + new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047), + new int64(-1090935817, -1295615723), new int64(-965641998, -479046869), + new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207), + new int64(-354779690, -840897762), new int64(-176337025, -294727304), + new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026), + new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b), + new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493), + new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620), + new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430), + new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817) + ]; + } + + for (i = 0; i < 80; i += 1) { + W[i] = new int64(0, 0); + } + + // append padding to the source string. The format is described in the FIPS. + x[len >> 5] |= 0x80 << (24 - (len & 0x1f)); + x[((len + 128 >> 10) << 5) + 31] = len; + l = x.length; + for (i = 0; i < l; i += 32) { //32 dwords is the block size + int64copy(a, H[0]); + int64copy(b, H[1]); + int64copy(c, H[2]); + int64copy(d, H[3]); + int64copy(e, H[4]); + int64copy(f, H[5]); + int64copy(g, H[6]); + int64copy(h, H[7]); + + for (j = 0; j < 16; j += 1) { + W[j].h = x[i + 2 * j]; + W[j].l = x[i + 2 * j + 1]; + } + + for (j = 16; j < 80; j += 1) { + //sigma1 + int64rrot(r1, W[j - 2], 19); + int64revrrot(r2, W[j - 2], 29); + int64shr(r3, W[j - 2], 6); + s1.l = r1.l ^ r2.l ^ r3.l; + s1.h = r1.h ^ r2.h ^ r3.h; + //sigma0 + int64rrot(r1, W[j - 15], 1); + int64rrot(r2, W[j - 15], 8); + int64shr(r3, W[j - 15], 7); + s0.l = r1.l ^ r2.l ^ r3.l; + s0.h = r1.h ^ r2.h ^ r3.h; + + int64add4(W[j], s1, W[j - 7], s0, W[j - 16]); + } + + for (j = 0; j < 80; j += 1) { + //Ch + Ch.l = (e.l & f.l) ^ (~e.l & g.l); + Ch.h = (e.h & f.h) ^ (~e.h & g.h); + + //Sigma1 + int64rrot(r1, e, 14); + int64rrot(r2, e, 18); + int64revrrot(r3, e, 9); + s1.l = r1.l ^ r2.l ^ r3.l; + s1.h = r1.h ^ r2.h ^ r3.h; + + //Sigma0 + int64rrot(r1, a, 28); + int64revrrot(r2, a, 2); + int64revrrot(r3, a, 7); + s0.l = r1.l ^ r2.l ^ r3.l; + s0.h = r1.h ^ r2.h ^ r3.h; + + //Maj + Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l); + Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h); + + int64add5(T1, h, s1, Ch, sha512_k[j], W[j]); + int64add(T2, s0, Maj); + + int64copy(h, g); + int64copy(g, f); + int64copy(f, e); + int64add(e, d, T1); + int64copy(d, c); + int64copy(c, b); + int64copy(b, a); + int64add(a, T1, T2); + } + int64add(H[0], H[0], a); + int64add(H[1], H[1], b); + int64add(H[2], H[2], c); + int64add(H[3], H[3], d); + int64add(H[4], H[4], e); + int64add(H[5], H[5], f); + int64add(H[6], H[6], g); + int64add(H[7], H[7], h); + } + + //represent the hash as an array of 32-bit dwords + for (i = 0; i < 8; i += 1) { + hash[2 * i] = H[i].h; + hash[2 * i + 1] = H[i].l; + } + return hash; + } + + //A constructor for 64-bit numbers + + function int64(h, l) { + this.h = h; + this.l = l; + //this.toString = int64toString; + } + + //Copies src into dst, assuming both are 64-bit numbers + + function int64copy(dst, src) { + dst.h = src.h; + dst.l = src.l; + } + + //Right-rotates a 64-bit number by shift + //Won't handle cases of shift>=32 + //The function revrrot() is for that + + function int64rrot(dst, x, shift) { + dst.l = (x.l >>> shift) | (x.h << (32 - shift)); + dst.h = (x.h >>> shift) | (x.l << (32 - shift)); + } + + //Reverses the dwords of the source and then rotates right by shift. + //This is equivalent to rotation by 32+shift + + function int64revrrot(dst, x, shift) { + dst.l = (x.h >>> shift) | (x.l << (32 - shift)); + dst.h = (x.l >>> shift) | (x.h << (32 - shift)); + } + + //Bitwise-shifts right a 64-bit number by shift + //Won't handle shift>=32, but it's never needed in SHA512 + + function int64shr(dst, x, shift) { + dst.l = (x.l >>> shift) | (x.h << (32 - shift)); + dst.h = (x.h >>> shift); + } + + //Adds two 64-bit numbers + //Like the original implementation, does not rely on 32-bit operations + + function int64add(dst, x, y) { + var w0 = (x.l & 0xffff) + (y.l & 0xffff); + var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16); + var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16); + var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16); + dst.l = (w0 & 0xffff) | (w1 << 16); + dst.h = (w2 & 0xffff) | (w3 << 16); + } + + //Same, except with 4 addends. Works faster than adding them one by one. + + function int64add4(dst, a, b, c, d) { + var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff); + var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16); + var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16); + var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16); + dst.l = (w0 & 0xffff) | (w1 << 16); + dst.h = (w2 & 0xffff) | (w3 << 16); + } + + //Same, except with 5 addends + + function int64add5(dst, a, b, c, d, e) { + var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff), + w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16), + w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16), + w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16); + dst.l = (w0 & 0xffff) | (w1 << 16); + dst.h = (w2 & 0xffff) | (w3 << 16); + } + }, + /** + * @class Hashes.RMD160 + * @constructor + * @param {Object} [config] + * + * A JavaScript implementation of the RIPEMD-160 Algorithm + * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * See http://pajhome.org.uk/crypt/md5 for details. + * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/ + */ + RMD160: function (options) { + /** + * Private properties configuration variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + * @see this.setUpperCase() method + * @see this.setPad() method + */ + var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, + /* hexadecimal output case format. false - lowercase; true - uppercase */ + b64pad = (options && typeof options.pad === 'string') ? options.pa : '=', + /* base-64 pad character. Default '=' for strict RFC compliance */ + utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, + /* enable/disable utf8 encoding */ + rmd160_r1 = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, + 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, + 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, + 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 + ], + rmd160_r2 = [ + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, + 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, + 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, + 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, + 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 + ], + rmd160_s1 = [ + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, + 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, + 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, + 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, + 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 + ], + rmd160_s2 = [ + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, + 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, + 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, + 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, + 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 + ]; + + /* privileged (public) methods */ + this.hex = function (s) { + return rstr2hex(rstr(s, utf8)); + }; + this.b64 = function (s) { + return rstr2b64(rstr(s, utf8), b64pad); + }; + this.any = function (s, e) { + return rstr2any(rstr(s, utf8), e); + }; + this.raw = function (s) { + return rstr(s, utf8); + }; + this.hex_hmac = function (k, d) { + return rstr2hex(rstr_hmac(k, d)); + }; + this.b64_hmac = function (k, d) { + return rstr2b64(rstr_hmac(k, d), b64pad); + }; + this.any_hmac = function (k, d, e) { + return rstr2any(rstr_hmac(k, d), e); + }; + /** + * Perform a simple self-test to see if the VM is working + * @return {String} Hexadecimal hash sample + * @public + */ + this.vm_test = function () { + return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72'; + }; + /** + * @description Enable/disable uppercase hexadecimal returned string + * @param {boolean} + * @return {Object} this + * @public + */ + this.setUpperCase = function (a) { + if (typeof a === 'boolean') { + hexcase = a; + } + return this; + }; + /** + * @description Defines a base64 pad string + * @param {string} Pad + * @return {Object} this + * @public + */ + this.setPad = function (a) { + if (typeof a !== 'undefined') { + b64pad = a; + } + return this; + }; + /** + * @description Defines a base64 pad string + * @param {boolean} + * @return {Object} this + * @public + */ + this.setUTF8 = function (a) { + if (typeof a === 'boolean') { + utf8 = a; + } + return this; + }; + + /* private methods */ + + /** + * Calculate the rmd160 of a raw string + */ + + function rstr(s) { + s = (utf8) ? utf8Encode(s) : s; + return binl2rstr(binl(rstr2binl(s), s.length * 8)); + } + + /** + * Calculate the HMAC-rmd160 of a key and some data (raw strings) + */ + + function rstr_hmac(key, data) { + key = (utf8) ? utf8Encode(key) : key; + data = (utf8) ? utf8Encode(data) : data; + var i, hash, + bkey = rstr2binl(key), + ipad = Array(16), + opad = Array(16); + + if (bkey.length > 16) { + bkey = binl(bkey, key.length * 8); + } + + for (i = 0; i < 16; i += 1) { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8); + return binl2rstr(binl(opad.concat(hash), 512 + 160)); + } + + /** + * Convert an array of little-endian words to a string + */ + + function binl2rstr(input) { + var i, output = '', + l = input.length * 32; + for (i = 0; i < l; i += 8) { + output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF); + } + return output; + } + + /** + * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length. + */ + + function binl(x, len) { + var T, j, i, l, + h0 = 0x67452301, + h1 = 0xefcdab89, + h2 = 0x98badcfe, + h3 = 0x10325476, + h4 = 0xc3d2e1f0, + A1, B1, C1, D1, E1, + A2, B2, C2, D2, E2; + + /* append padding */ + x[len >> 5] |= 0x80 << (len % 32); + x[(((len + 64) >>> 9) << 4) + 14] = len; + l = x.length; + + for (i = 0; i < l; i += 16) { + A1 = A2 = h0; + B1 = B2 = h1; + C1 = C2 = h2; + D1 = D2 = h3; + E1 = E2 = h4; + for (j = 0; j <= 79; j += 1) { + T = safe_add(A1, rmd160_f(j, B1, C1, D1)); + T = safe_add(T, x[i + rmd160_r1[j]]); + T = safe_add(T, rmd160_K1(j)); + T = safe_add(bit_rol(T, rmd160_s1[j]), E1); + A1 = E1; + E1 = D1; + D1 = bit_rol(C1, 10); + C1 = B1; + B1 = T; + T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2)); + T = safe_add(T, x[i + rmd160_r2[j]]); + T = safe_add(T, rmd160_K2(j)); + T = safe_add(bit_rol(T, rmd160_s2[j]), E2); + A2 = E2; + E2 = D2; + D2 = bit_rol(C2, 10); + C2 = B2; + B2 = T; + } + + T = safe_add(h1, safe_add(C1, D2)); + h1 = safe_add(h2, safe_add(D1, E2)); + h2 = safe_add(h3, safe_add(E1, A2)); + h3 = safe_add(h4, safe_add(A1, B2)); + h4 = safe_add(h0, safe_add(B1, C2)); + h0 = T; + } + return [h0, h1, h2, h3, h4]; + } + + // specific algorithm methods + + function rmd160_f(j, x, y, z) { + return (0 <= j && j <= 15) ? (x ^ y ^ z) : + (16 <= j && j <= 31) ? (x & y) | (~x & z) : + (32 <= j && j <= 47) ? (x | ~y) ^ z : + (48 <= j && j <= 63) ? (x & z) | (y & ~z) : + (64 <= j && j <= 79) ? x ^ (y | ~z) : + 'rmd160_f: j out of range'; + } + + function rmd160_K1(j) { + return (0 <= j && j <= 15) ? 0x00000000 : + (16 <= j && j <= 31) ? 0x5a827999 : + (32 <= j && j <= 47) ? 0x6ed9eba1 : + (48 <= j && j <= 63) ? 0x8f1bbcdc : + (64 <= j && j <= 79) ? 0xa953fd4e : + 'rmd160_K1: j out of range'; + } + + function rmd160_K2(j) { + return (0 <= j && j <= 15) ? 0x50a28be6 : + (16 <= j && j <= 31) ? 0x5c4dd124 : + (32 <= j && j <= 47) ? 0x6d703ef3 : + (48 <= j && j <= 63) ? 0x7a6d76e9 : + (64 <= j && j <= 79) ? 0x00000000 : + 'rmd160_K2: j out of range'; + } + } + }; + + // new MD5 instance + var MD5 = new Hashes.MD5 + // new SHA1 instance + var SHA1 = new Hashes.SHA1 + // new SHA256 instance + var SHA256 = new Hashes.SHA256 + // new SHA512 instace + var SHA512 = new Hashes.SHA512 + // new RIPEMD-160 instace + var RMD160 = new Hashes.RMD160 + var Base64 = new Hashes.Base64 + + Hashes.md5 = function (str) { + return MD5.hex(str); + } + Hashes.sha1 = function (str) { + return SHA1.hex(str); + } + Hashes.sha256 = function (str) { + return SHA256.hex(str); + } + Hashes.sha512 = function (str) { + return SHA512.hex(str); + } + Hashes.crc32 = Hashes.CRC32; + Hashes.rmd160 = function (str) { + return RMD160.hex(str); + } + Hashes.Base64Encode = function (str) { + return Base64.encode(str); + } + Hashes.Base64Decode = function (str) { + return Base64.decode(str); + } + exports('hashes', Hashes); +}); \ No newline at end of file diff --git a/public/js/lay-module/inputTag/inputTag.css b/public/js/lay-module/inputTag/inputTag.css new file mode 100644 index 0000000..9fffc34 --- /dev/null +++ b/public/js/lay-module/inputTag/inputTag.css @@ -0,0 +1,87 @@ +@keyframes fariy-fadein { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} +.fairy-tag-container { + width: auto; + min-height: 100px; + padding: 5px; + border: 1px solid #e6e6e6; + background-color: #ffffff; +} +.fairy-tag-container:hover { + border-color: #d2d2d2; +} +.fairy-tag-container span.fairy-tag { + float: left; + font-size: 13px; + padding: 5px 8px; + margin-right: 5px; + margin-bottom: 5px; + border-radius: 2px; + line-height: 16px; +} +.fairy-tag-container span.fairy-tag a { + font-size: 11px; + font-weight: bolder; + color: #ffffff; + text-decoration: none; + margin-left: 6px; +} +.fairy-tag-container span.fairy-tag a:hover { + cursor: pointer; +} +.fairy-tag-container span.fairy-bg-red { + background-color: #FF5722; +} +.fairy-tag-container span.fairy-bg-orange { + background-color: #FFB800; +} +.fairy-tag-container span.fairy-bg-green { + background-color: #009688; +} +.fairy-tag-container span.fairy-bg-cyan { + background-color: #2F4056; +} +.fairy-tag-container span.fairy-bg-blue { + background-color: #1E9FFF; +} +.fairy-tag-container span.fairy-bg-black { + background-color: #393D49; +} +.fairy-tag-container span.fairy-bg-red, +.fairy-tag-container span.fairy-bg-orange, +.fairy-tag-container span.fairy-bg-green, +.fairy-tag-container span.fairy-bg-cyan, +.fairy-tag-container span.fairy-bg-blue, +.fairy-tag-container span.fairy-bg-black { + color: #ffffff; +} +.fairy-tag-container .fairy-anim-fadein { + animation: fariy-fadein 0.3s both; +} +.fairy-tag-container .fairy-tag-input[type='text'] { + width: 80px; + font-size: 13px; + padding: 6px; + background: transparent; + border: 0 none; + outline: 0; +} +.fairy-tag-container .fairy-tag-input[type='text']:focus::-webkit-input-placeholder { + color: transparent; +} +.fairy-tag-container .fairy-tag-input[type='text']:focus:-moz-placeholder { + color: transparent; +} +.fairy-tag-container .fairy-tag-input[type='text']:focus:-moz-placeholder { + color: transparent; +} +.fairy-tag-container .fairy-tag-input[type='text']:focus:-ms-input-placeholder { + color: transparent; +} +/*# sourceMappingURL=inputTag.css.map */ \ No newline at end of file diff --git a/public/js/lay-module/inputTag/inputTag.js b/public/js/lay-module/inputTag/inputTag.js new file mode 100644 index 0000000..2b7b2bb --- /dev/null +++ b/public/js/lay-module/inputTag/inputTag.js @@ -0,0 +1,150 @@ +/* + * Name: inputTag + * Author: cshaptx4869 + * Project: https://github.com/cshaptx4869/inputTag + */ +(function (define) { + define(['jquery'], function ($) { + "use strict"; + + class InputTag { + + options = { + elem: '.fairy-tag-input', + theme: ['fairy-bg-red', 'fairy-bg-orange', 'fairy-bg-green', 'fairy-bg-cyan', 'fairy-bg-blue', 'fairy-bg-black'], + data: [], + removeKeyNum: 8, + createKeyNum: 13, + permanentData: [], + }; + + get elem() { + return $(this.options.elem); + } + + get copyData() { + return [...this.options.data]; + } + + constructor(options) { + this.render(options); + } + + render(options) { + this.init(options); + this.listen(); + } + + init(options) { + var spans = '', that = this; + this.options = $.extend(this.options, options); + !this.elem.attr('placeholder') && this.elem.attr('placeholder', '添加标签'); + $.each(this.options.data, function (index, item) { + spans += that.spanHtml(item); + }); + this.elem.before(spans); + } + + listen() { + var that = this; + + this.elem.parent().on('click', 'a', function () { + that.removeItem($(this).parent('span')); + }); + + this.elem.parent().on('click', function () { + that.elem.focus(); + }); + + this.elem.keydown(function (event) { + var keyNum = (event.keyCode ? event.keyCode : event.which); + if (keyNum === that.options.removeKeyNum) { + if (!that.elem.val().trim()) { + var closeItems = that.elem.parent().find('a'); + if (closeItems.length) { + that.removeItem($(closeItems[closeItems.length - 1]).parent('span')); + event.preventDefault(); + } + } + } else if (keyNum === that.options.createKeyNum) { + that.createItem(); + event.preventDefault(); + } + }); + } + + createItem() { + var value = this.elem.val().trim(); + + if (this.options.beforeCreate && typeof this.options.beforeCreate === 'function') { + var modifiedValue = this.options.beforeCreate(this.copyData, value); + if (typeof modifiedValue == 'string' && modifiedValue) { + value = modifiedValue; + } else { + value = ''; + } + } + + if (value) { + if (!this.options.data.includes(value)) { + this.options.data.push(value); + this.elem.before(this.spanHtml(value)); + this.onChange(value, 'create'); + } + } + + this.elem.val(''); + } + + removeItem(target) { + var that = this; + var closeSpan = target.remove(), + closeSpanText = $(closeSpan).children('span').text(); + var value = that.options.data.splice($.inArray(closeSpanText, that.options.data), 1); + value.length === 1 && that.onChange(value[0], 'remove'); + } + + randomColor() { + return this.options.theme[Math.floor(Math.random() * this.options.theme.length)]; + } + + spanHtml(value) { + return '' + + '' + value + '' + + (this.options.permanentData.includes(value) ? '' : '
    ×') + + ''; + } + + onChange(value, type) { + this.options.onChange && typeof this.options.onChange === 'function' && this.options.onChange(this.copyData, value, type); + } + + getData() { + return this.copyData; + } + + clearData() { + this.options.data = []; + this.elem.prevAll('span.fairy-tag').remove(); + } + } + + return { + render(options) { + return new InputTag(options); + } + } + }); +}(typeof define === 'function' && define.amd ? define : function (deps, factory) { + layui.link(layui.cache.base + "inputTag/inputTag.css?v="+(new Date).getTime()); + var MOD_NAME = 'inputTag'; + if (typeof module !== 'undefined' && module.exports) { //Node + module.exports = factory(require('jquery')); + } else if (window.layui && layui.define) { + layui.define('jquery', function (exports) { //layui加载 + exports(MOD_NAME, factory(layui.jquery)); + }); + } else { + window[MOD_NAME] = factory(window.jQuery); + } +})); \ No newline at end of file diff --git a/public/js/lay-module/ipicker/iPicker.js b/public/js/lay-module/ipicker/iPicker.js new file mode 100644 index 0000000..c8c4d50 --- /dev/null +++ b/public/js/lay-module/ipicker/iPicker.js @@ -0,0 +1,1322 @@ +layui.define(['laypage', 'form'], function (exports) { + "use strict"; + // 工具 + const util = { + + // 检测类型 + type: obj => Object.prototype.toString.call( obj ).slice( 8, -1 ).toLowerCase(), + + // 检测是否为不为空的纯对象 + isNotEmptyPlainObject: obj => !!( util.type( obj ) === "object" && Object.keys( obj ).length ), + + // 检测是否为大于或等于零的安全整数 + isCorrectNumber: ( obj, zero ) => !!( Number.isSafeInteger( obj ) && ( !zero ? obj > 0 : obj >= 0 ) ), + + // 检测是否为函数 + isFunction: obj => util.type( obj ) === "function", + + // 检测是否为 Promise + isPromise: obj => util.type( obj ) === "promise", + + // 创建唯一标识符 + uid ( useSymbol ) { + const random = Math.random().toString( 36 ).slice( 2, 10 ); + return useSymbol ? Symbol( random ) : random; + }, + + // 定时器 + delay ( delay = 0 ) { + return new Promise( resolve => { + let timer = window.setTimeout( () => { + window.clearTimeout( timer ); + timer = null; + resolve(); + }, delay ) + } ) + }, + + // 合并参数 + mergeParam ( params = {}, defaultParams ) { + const result = {}; + for ( const key in defaultParams ) { + const v = params[ key ]; + if ( util.type( v ) === "object" ) { + result[ key ] = util.mergeParam( v, defaultParams[ key ] ); + } else { + result[ key ] = v === 0 ? v : ( v || defaultParams[ key ] ); + } + } + return result; + } + }; + + // DOM 系统 + const $ = ( () => { + function buildDom ( domArray ) { + const length = domArray[ 0 ] ? domArray.length : 0; + this.length = length; + for ( let i = 0; i < length; i++ ) { + this[ i ] = domArray[ i ]; + } + return this; + } + buildDom.prototype = { + each ( callback ) { + for ( let i = 0, j = this.length; i < j; i++ ) { + callback.call( this[ i ], i, this[ i ] ); + } + return this; + }, + get ( index = 0 ) { + return this[ index ]; + }, + click ( callback ) { + return this.each( function () { + this.addEventListener( "click", function ( event ) { + callback.call( this, event ); + } ) + } ) + }, + hasClass ( className ) { + return this[ 0 ].classList.contains( className ); + }, + addClass ( className ) { + return this.each( function () { + for ( const name of className.split( " " ) ) { + this.classList.add( name ); + } + } ) + }, + removeClass ( className ) { + return this.each( function () { + for ( const name of className.split( " " ) ) { + this.classList.remove( name ); + } + } ) + }, + toggleClass ( className ) { + return this.each( function () { + for ( const name of className.split( " " ) ) { + this.classList.toggle( name ); + } + } ) + }, + css ( name, value ) { + function getStyle ( elem, prop ) { + return document.defaultView.getComputedStyle( elem, null ).getPropertyValue( prop ); + } + function setStyle ( el, _name, _value ) { + el.style[ _name ] = _value; + } + return ( typeof name === "string" && !value ) ? + getStyle( this[ 0 ], name ) : + this.each( function () { + if ( name && value ) { + setStyle( this, name, value ); + } + if ( util.isNotEmptyPlainObject( name ) && !value ) { + for ( const key in name ) { + setStyle( this, key, name[ key ] ); + } + } + } ) + }, + html ( html ) { + return this.each( function () { + this.innerHTML = html; + } ) + }, + text () { + return this[ 0 ].textContent; + }, + val ( val ) { + return this.each( function () { + this.value = val; + } ) + }, + eq ( index ) { + if ( typeof index === "number" ) { + const eqArr = []; + if ( index < this.length ) { + eqArr.push( this[ index ] ); + } + return $( eqArr ); + } + }, + index () { + if ( this[ 0 ] ) { + let child = this[ 0 ]; + let i = 0; + while ( ( child = child.previousSibling ) !== null ) { + child.nodeType === 1 && i++; + } + return i; + } + }, + prev () { + const prevArr = []; + this.each( function () { + const p = this.previousElementSibling; + p && prevArr.push( p ); + } ) + return $( prevArr ); + }, + next () { + const nextArr = []; + this.each( function () { + const n = this.nextElementSibling; + n && nextArr.push( n ); + } ) + return $( nextArr ); + }, + nextAll () { + const nextAll = []; + this.each( function () { + let next = this.nextElementSibling; + function findNext () { + if ( next ) { + nextAll.push( next ); + next = $( next ).get().nextElementSibling; + findNext(); + } + } + findNext(); + } ); + return $( nextAll ); + }, + parent () { + const parentArr = []; + this.each( function () { + parentArr.push( this.parentNode ); + } ) + return $( parentArr ); + }, + find ( dom ) { + const findArr = []; + this.each( function () { + const node = this.querySelectorAll( dom ); + for ( let i = 0, j = node.length; i < j; i++ ) { + ( node[ i ].nodeType === 1 ) && findArr.push( node[ i ] ); + } + } ) + return $( findArr ); + }, + siblings: function () { + const sibling = []; + this.each( function () { + const child = this.parentNode.children; + for ( let i = 0, j = child.length; i < j; i++ ) { + if ( child[ i ] !== this ) { + sibling.push( child[ i ] ); + } + } + } ); + return $( sibling ); + }, + add ( elem ) { + let dom = this; + const addElem = $( elem ); + for ( let i = 0, j = addElem.length; i < j; i++ ) { + dom[ dom.length ] = addElem[ i ]; + dom.length++; + } + return dom; + }, + data ( name, value ) { + return ( typeof name === "string" && !value ) ? + this[ 0 ].dataset[ name ] : + this.each( function () { + if ( name && value ) { + this.dataset[ name ] = value; + } else { + for ( const key in name ) { + this.dataset[ key ] = name[ key ]; + } + } + } ) + }, + remove () { + return this.each( function () { + this.parentNode && this.parentNode.removeChild( this ); + } ) + }, + show () { + return this.each( function () { + this.style.display = "block"; + } ) + }, + hide () { + return this.each( function () { + this.style.display = ""; + if ( $( this ).css( "display" ) !== "none" ) { + this.style.display = "none"; + } + } ) + } + }; + return function ( selector ) { + let result = []; + if ( typeof selector === "string" ) { + result = document.querySelectorAll( selector ); + } + if ( selector.nodeType || selector === document ) { + result.push( selector ); + } else if ( selector.length > 0 && selector[ 0 ] && selector[ 0 ].nodeType ) { + for ( let i = 0, j = selector.length; i < j; i++ ) { + result.push( selector[ i ] ); + } + } + return new buildDom( result ); + } + } )(); + + // 默认配置 + const defaults = { + theme: "select", + data: { + props: { + code: "code", + name: "name" + }, + source: null, + when: null + }, + level: 6, + radius: 2, + width: 200, + height: 34, + maxHeight: 300, + disabled: [], + disabledItem: [], + tips:'请选择', + selected: [], + selectedCallback: () => {}, + placeholder: [ "省", "市", "区" ], + placeholder1: [ "省份", "城市", "区县" ], + separator: "/", + clearable: false, + strict: false, + onlyShowLastLevel: false, + icon: "arrow", + onClear: () => {}, + onSelect: () => {} + }; + + // 存储组件数据 + const cacheIPicker = { + + // 原始目标元素 >> elem: "..." + originalElem: new WeakMap(), + + // 组件配置信息 >> elem: opt + options: new WeakMap(), + + // 组件选中结果 >> elem: [ [], [], [] ] + value: new WeakMap(), + + // 组件唯一标识 >> elem: uid + id: new WeakMap(), + + // 组件目标信息 >> uid: elem + target: new Map() + }; + + // 缓存数据源 + const cacheCustomData = new Map(); + + // 样式 id + const styleId = "iPicker-default-style"; + + // 功能模块 + const modules = { + createFrame ( $target, { placeholder1,theme, level, icon, clearable }, uid ) { + let frame = ` +
    +
    + + + ${ clearable ? "" : "" } +
    +
    ___
    +
    + `; + switch ( theme ) { + case "select": + frame = frame.replace( "___", "
      " ).repeat( level ); + break; + case "cascader": + frame = frame.replace( "___", "
        ".repeat( level ) ); + break; + case "panel": + frame = frame.replace( "___", ` +
        +
        + ${placeholder1[0]} +
        + ${ level > 1 ? "
        "+placeholder1[1]+"
        " : "" } + ${ level > 2 ? "
        "+placeholder1[2]+"
        " : "" } + ${ level > 3 ? "
        "+placeholder1[3]+"
        " : "" } + ${ level > 4 ? "
        "+placeholder1[4]+"
        " : "" } + ${ level > 5 ? "
        "+placeholder1[5]+"
        " : "" } + ${ level > 6 ? "
        "+placeholder1[6]+"
        " : "" } +
        +
        ${ "
          ".repeat( level ) }
          + ` ); + break; + } + $target.addClass( "iPicker-target" ).html( frame ).data( { + theme: theme, + id: uid.toString().replace( /(\(|\))/g, "" ) + } ); + }, + createList ( data, opt, isInnerData,nowlevel) { + return new Promise( resolve => { + let list = ""; + const isCascader = opt.theme === "cascader"; + if ( !isInnerData ) { + const { code, name } = opt.data.props || {}; + data.forEach( obj => { + list += ` +
        • + ${ obj[ name ] } + ${ ( isCascader ? "" : "" ) } +
        • + `; + } ) + } else { + for ( const key in data ) { + list += ` +
        • + ${ data[ key ] } + ${ ( isCascader ? "" : "" ) } +
        • + `; + } + } + resolve( list ); + } ) + }, + getData ( code, level, opt, isInnerData ) { + + // 优先使用本地缓存的数据源 + return new Promise( resolve => { + + // 通过 opt.data.when 函数可对数据进行一次最后的处理 + function when ( data, level ) { + if ( util.isFunction( opt.data.when ) ) { + return opt.data.when( data, level ); + } else { + return data; + } + } + + // 自定义数据源 + if ( !isInnerData ) { + const hasCache = cacheCustomData.get( code ); + if ( hasCache ) { + resolve( when( hasCache, level ) ); + } else { + const dataSource = opt.data.source( code, level ); + if ( util.type( dataSource ) === "object" && util.isFunction( dataSource.then ) ) { + dataSource.then( res => { + cacheCustomData.set( code, res ); + resolve( when( res, level ) ); + } ) + } + } + } else { + + // 内置数据源 + opt.data.source.then( res => { + resolve( when( res[ code ], level ) ); + } ) + } + } ) + }, + getSelected ( $target ) { + + // 根据被选中列表含有 "特征类" 来获取选中项 + const $active = $target.find( ".iPicker-list-active" ); + const activeSize = $active.length; + const [ code, name, map ] = [ [], [], [] ]; + if ( activeSize ) { + $active.each( function () { + const dataCode = $( this ).data( "code" ); + const dataName = $( this ).data( "name" ); + code.push( dataCode ); + name.push( dataName ); + map.push( { + code: dataCode, + name: dataName + } ); + } ) + } + + return [ code, name, map ]; + }, + cacheSelected ( target, value ) { + cacheIPicker.value.set( target, value ); + } + }; + + // 核心程序 + const iPicker = ( target, options ) => { + + // 对必选参数进行校验 + if ( + !target || + !options || + typeof target !== "string" || + !target.trim() || + !util.isNotEmptyPlainObject( options ) || + !util.isNotEmptyPlainObject( options.data ) || + !options.data.source || + ( !util.isFunction( options.data.source ) && !util.isPromise( options.data.source ) ) + ) { + return; + } + + const $target = $( target ); + const _target = $target.get(); + + if ( !_target ) { + return; + } + + // 合并参数 + const opt = util.mergeParam( options, defaults ); + + // 检验并处理 level + if ( !util.isCorrectNumber( opt.level ) || opt.level < 1 || opt.level > 6 ) { + opt.level = 6; + } + + // 缓存主题类型 + const selectTheme = opt.theme === "select"; + const cascaderTheme = opt.theme === "cascader"; + const panelTheme = opt.theme === "panel"; + + // 缓存数据源类型 + const isInnerData = util.isPromise( opt.data.source ); + + // 检测 onClear 和 onSelect 是否为函数 + const onClearIsFunc = util.isFunction( opt.onClear ); + const onSelectIsFunc = util.isFunction( opt.onSelect ); + + // 创建一个全局唯一标识符 + const uid = _target.iPickerID || util.uid( true ); + const panelwidth= opt.width; + // 存储 + cacheIPicker.originalElem.set( _target, target ); + cacheIPicker.options.set( _target, opt ); + cacheIPicker.target.set( uid, _target ); + cacheIPicker.id.set( _target, uid ); + _target.iPickerID = uid; + + // 添加样式 + if ( !document.getElementById( styleId ) ) { + document.head.insertAdjacentHTML( "afterbegin", ` + + ` ); + } + + // 生成组件结构 + modules.createFrame( $target, opt, uid ); + + // 缓存 dom + const $container = $target.find( ".iPicker-container" ); + const $result = $target.find( ".iPicker-result" ); + const $input = $target.find( ".iPicker-input" ); + const $list = $target.find( ".iPicker-list" ); + const $ul = $list.find( "ul" ); + + // 添加索引标记 + $ul.each( function ( i ) { + $( this ).data( "level", ++i ); + } ) + + // 设置列表最大高度 + // 最小有效值是 100,如果设置了小于 100 的值则按照默认配置进行处理 + if ( util.isCorrectNumber( opt.maxHeight ) && opt.maxHeight >= 100 ) { + $list.css( "maxHeight", `${ opt.maxHeight }px` ); + if ( cascaderTheme ) { + $ul.css( "maxHeight", `${ opt.maxHeight }px` ); + } + if ( panelTheme ) { + $list.find( ".iPicker-panel-content" ).css( "height", `${ opt.maxHeight - 38 }px` ); + } + } + + // 设置结果展示框宽度 + // 当宽度是数字时最小有效值是 100,如果设置了小于 100 的值则按照默认配置进行处理 + // 当宽度是字符串时必须是百分比形式 + if ( util.isCorrectNumber( opt.width ) && opt.width >= 100 ) { + $result.css( "width", `${ opt.width }px` ); + if ( selectTheme ) { + $list.css( "width", `${ opt.width }px` ); + } + } + if ( typeof opt.width === "string" && opt.width.trim().endsWith( "%" ) ) { + $result.css( "width", opt.width ); + if ( selectTheme ) { + $list.css( "width", opt.width ); + } else { + $container.css( "width", opt.width ); + } + } + + // 设置结果展示框高度 + // 最小有效值是 20,如果设置了小于 20 的值则按照默认配置进行处理 + if ( util.isCorrectNumber( opt.height ) && opt.height >= 20 ) { + $result.css( "height", `${ opt.height }px` ); + $input.css( "height", `${ opt.height - 2 }px` ); + $input.next().css( "height", `${ opt.height - 2 }px` ); + } + + // 禁用指定地区 + if ( opt.disabledItem === true ) { + new MutationObserver( () => { + $target.find( "li" ).addClass( "iPicker-list-disabled" ); + } ).observe( _target, { + childList: true, + subtree: true + } ) + } + if ( Array.isArray( opt.disabledItem ) && opt.disabledItem.length ) { + for ( const code of [ ...new Set( opt.disabledItem ) ] ) { + new MutationObserver( () => { + const li = _target.querySelector( `[data-code="${ code }"]:not(.iPicker-list-disabled)` ); + if ( li ) { + li.classList.add( "iPicker-list-disabled" ); + } + } ).observe( _target, { + childList: true, + subtree: true + } ) + } + } + + // 禁用指定的结果展示框 + if ( opt.disabled === true ) { + opt.disabled = [ 1, 2, 3, 4 ].slice( 0, opt.level ); + } + if ( util.isCorrectNumber( opt.disabled ) ) { + opt.disabled = [ opt.disabled ]; + } + if ( Array.isArray( opt.disabled ) && opt.disabled.length ) { + for ( const level of [ ...new Set( opt.disabled ) ] ) { + if ( util.isCorrectNumber( level ) && level >= 1 && level <= 6 ) { + $result.eq( level - 1 ).addClass( "iPicker-disabled" ); + } + } + } + + // 设置 placeholder + if ( selectTheme && Array.isArray( opt.placeholder ) ) { + opt.placeholder.forEach( ( v, i ) => { + const input = $input.eq( i ).get(); + if ( input ) { + input.setAttribute( "placeholder", opt.placeholder[ i ] || defaults.placeholder[ i ] ); + } + } ) + } + if ( cascaderTheme || panelTheme ) { + if ( typeof opt.placeholder !== "string" || !opt.placeholder.trim() ) { + opt.placeholder = "请选择地区"; + } + $input.eq( 0 ).get().setAttribute( "placeholder", opt.tips ); + } + + // 设置结果展示框的圆角值 + if ( util.isCorrectNumber( opt.radius, true ) ) { + $result.add( $input ).css( "borderRadius", `${ opt.radius }px` ); + } + + // 设置清空按钮 + $result.find( ".clear-icon" ).hide(); + $result.each( function () { + + // 鼠标滑入滑出显示或隐藏清空按钮 + const el = $( this ).get(); + el.addEventListener( "mouseenter", () => { + const input = el.querySelector( "input" ); + if ( input ) { + if ( input.value && !el.classList.contains( "iPicker-disabled" ) ) { + $( this ).find( ".clear-icon" ).show().prev().hide(); + } + } + } ) + el.addEventListener( "mouseleave", () => { + $( this ).find( ".clear-icon" ).hide().prev().show(); + } ) + } ) + const $clear = $target.find( ".clear-icon" ); + if ( !selectTheme ) { + $clear.click( () => { + $clear.hide().prev().show(); + iPicker.clear( uid ); + if ( onClearIsFunc ) { + opt.onClear(); + } + } ) + } else { + $clear.each( function () { + const $this = $( this ); + $this.click( function () { + $this.hide().prev().show(); + const $parent = $this.parent(); + const $ul = $parent.next().find( "ul" ); + const index = +$ul.data( "index" ); + $parent.find( "input" ).val( "" ); + $ul.find( ".iPicker-list-active" ).removeClass( "iPicker-list-active" ); + $parent + .parent() + .nextAll() + .find( "input" ) + .val( "" ) + .parent() + .next() + .find( "ul" ) + .html( "" ); + + getCacheShow(); + closeList( $parent.next() ); + if ( onClearIsFunc ) { + opt.onClear(); + } + } ) + } ) + } + + // 通过点击展示框来展开或关闭列表 + $result.each( function () { + $( this ).find( "input, .arrow-icon" ).click( function () { + const $this = $( this ).parent(); + const $next = $this.next(); + const id = $this.parent().parent().data( "id" ); + + // 关闭其它列表 + const $otherList = $( `.iPicker-target:not([data-id="${ id }"]) .iPicker-list` ); + if ( $otherList.length ) { + closeList( $otherList ); + } + + // 组件必须在 "启用" 状态下才有效 + if ( !$this.hasClass( "iPicker-disabled" ) ) { + + // 列表中必须有数据 + if ( !$next.find( "li" ).length ) { + return; + } + + $this.toggleClass( "iPicker-result-active" ); + + if ( $next.hasClass( "iPicker-list-show" ) ) { + closeList( $next ); + } else { + if ( panelTheme ) { + $target + .find( ".iPicker-panel-tab > div:first-child" ) + .addClass( "iPicker-panel-tab-active" ) + .siblings() + .removeClass( "iPicker-panel-tab-active" ); + $target.find( ".iPicker-panel-content > ul:first-child" ).show().siblings().hide(); + } + + /* + # 自动检测展示框的位置 + # 实时调整下拉列表的位置 + */ + let flag = false; + const resultHeight = parseInt( $this.css( "height" ) ); + const positionListener = () => { + if ( flag ) { + return; + } + flag = true; + $next.addClass( "iPicker-list-show-temporary" ); + const bottom = document.documentElement.clientHeight - $this.get().getBoundingClientRect().bottom; + const height = parseInt( $next.css( "height" ) ); + if ( bottom < height ) { + $next.css( "marginTop", `-${ height + resultHeight }px` ).addClass( "iPicker-list-ontop" ); + } else { + $next.css( "marginTop", "0px" ).removeClass( "iPicker-list-ontop" ); + } + flag = false; + $next.removeClass( "iPicker-list-show-temporary" ); + } + positionListener(); + window.addEventListener( "scroll", positionListener ); + window.addEventListener( "resize", positionListener ); + $next.addClass( "iPicker-list-show" ).removeClass( "iPicker-list-hide" ); + } + } + } ) + } ) + + // 关闭列表函数 + function closeList ( $list ) { + if ( $list.hasClass( "iPicker-list-show" ) ) { + $list + .addClass( "iPicker-list-hide" ) + .removeClass( "iPicker-list-show" ) + .prev() + .removeClass( "iPicker-result-active" ); + $list.show(); + util.delay( 200 ).then( () => { + $list.get().style.removeProperty( "display" ); + } ) + + if ( !selectTheme ) { + getCacheShow(); + } + + // 检测 strict 模式 + const $target = $list.parent().parent(); + const opt = cacheIPicker.options.get( $target.get() ); + if ( opt.strict ) { + + // 利用定时器进行延时处理 + // 在列表关闭动画结束后执行相关程序 + util.delay( 200 ).then( () => { + const [ code ] = cacheIPicker.value.get( $target.get() ); + const codeLen = code.length; + + // 只有在至少选择了一个层级 + // 但又没有完整选择全部指定的层级的情况下 + // 才能执行后续程序 + if ( codeLen && codeLen !== opt.level ) { + new Promise( resolve => { + if ( codeLen === 1 ) { + if ( opt.level <= 6 ) { + resolve(); + } else { + addList( $ul.eq( 1 ).find( "li:first-child" ).data( "code" ), 6 ).then( () => { + resolve(); + } ); + } + } else { + resolve(); + } + } ).then( () => { + $ul.each( function () { + if ( !$( this ).find( ".iPicker-list-active" ).length ) { + $( this ).find( "li:first-child" ).addClass( "iPicker-list-active" ); + } + } ) + getCacheShow(); + } ) + } + } ) + } + } + } + + // 添加列表 + function addList ( code, level ) { + return new Promise( resolve => { + modules.getData( code, !selectTheme ? level : level > 6 ? 6 : level, opt, isInnerData ).then( res => { + modules.createList( res, opt, isInnerData,level ).then( content => { + const $targetUL = $ul.eq( level - 1 ); + $targetUL.html( content ).nextAll().html( "" ); + if ( selectTheme ) { + $targetUL.parent().parent().nextAll().find( "ul" ).html( "" ); + } + if ( cascaderTheme ) { + let size = 0; + $ul.each( function () { + if ( this.innerHTML ) { + size++; + } + } ) + $list.css( "width", `${ 200 * size }px` ); + $ul.eq( level - 1 ).show().nextAll().hide(); + } + if ( panelTheme ) { + $ul.eq( level - 1 ).show().siblings().hide(); + $target + .find( `.iPicker-panel-tab > div:nth-child(${ level })` ) + .addClass( "iPicker-panel-tab-active" ) + .siblings() + .removeClass( "iPicker-panel-tab-active" ); + } + selectTheme ? getCacheShow() : level <= opt.level && getCacheShow(); + resolve(); + } ); + } ) + } ) + } + + // 获取,存储,显示结果 + function getCacheShow () { + util.delay( 10 ).then( () => { + + // 获取并存储选中结果 + const getSelected = modules.getSelected( $target ); + modules.cacheSelected( _target, getSelected ); + + // 显示选中结果 + const separator = opt.separator.trim().charAt( 0 ); + function showResult ( result ) { + if ( result ) { + if ( cascaderTheme || panelTheme ) { + if ( opt.onlyShowLastLevel ) { + result = result.split( separator ).slice( -1 )[ 0 ].trim(); + } + } + } + return result; + } + + // 显示选中结果 + if ( selectTheme ) { + getSelected[ 1 ].forEach( ( item, index ) => { + $input.eq( index ).val( showResult( item ) ); + } ) + } else { + const name = getSelected[ 1 ].join( ` ${ separator } ` ); + $input.eq( 0 ).val( showResult( name ) ); + } + + // 执行 onSelect 事件 + if ( onSelectIsFunc ) { + if ( getSelected[ 1 ].length ) { + opt.onSelect( ...cacheIPicker.value.get( _target ) ); + } + } + } ) + } + + // 自动获取第一层级的数据 + // 含有对 "默认选中项" 的处理 + addList( ( isInnerData ? "a86" : null ), 1 ).then( () => { + _target.dataset.promise = "true"; + + // 设置默认选中项 + // selected + // 默认选中项中不能含有已被禁用的选项,即: + // selected 数组中的值不能在 disabledItem 中也存在,否则无效 + if ( Array.isArray( opt.selected ) && opt.selected.length ) { + opt.selected = [ ...new Set( opt.selected ) ] + for ( const code of opt.selected ) { + if ( opt.disabledItem.includes( code ) ) { + return; + } + } + !( function selected ( i ) { + addList( opt.selected[ i - 1 ], i + 1 ).then( () => { + i++; + if ( i < opt.level ) { + selected( i ); + } else { + opt.selected.forEach( (item,index) => { + //要考虑层级 + $target.find( `li[data-code2="${(index+1)}_${ item }"]` ).addClass( "iPicker-list-active" ); + // $target.find( `li[data-code="${ item }"]` ).addClass( "iPicker-list-active_"+(index+1)+"_"+item ); + } ) + getCacheShow(); + + // 执行 selectedCallback 函数 + if ( util.isFunction( opt.selectedCallback ) ) { + opt.selectedCallback(); + } + } + } ) + } )( 1 ); + } + } ); + + // 点击选择事件 + $target.click( event => { + if ( event.target.nodeName.toLowerCase() !== "li" ) { + return; + } + const $li = $( event.target ); + const $ul = $li.parent(); + if ( $li.hasClass( "iPicker-list-disabled" ) ) { + return; + } + $li.addClass( "iPicker-list-active" ).siblings().removeClass( "iPicker-list-active" ); + addList( $li.data( "code" ), +$ul.data( "level" ) + 1 ); + + // select 主题下,点击选择后自动关闭列表 + if ( selectTheme ) { + closeList( $ul.parent() ); + $ul.parent().parent().nextAll().find( ".iPicker-result input" ).val( "" ); + } + + // cascader 和 panel 模式下,如果点击选择的是最后一级的数据 + // 则自动关闭列表 + if ( $ul.index() === opt.level - 1 ) { + if ( cascaderTheme ) { + closeList( $ul.parent() ); + } + if ( panelTheme ) { + closeList( $ul.parent().parent() ); + } + } + } ) + + // cascader 主题下需要强制设置高度 + if ( cascaderTheme ) { + $ul.css( { + minHeight: `${ opt.maxHeight }px`, + maxHeight: `${ opt.maxHeight }px` + } ); + } + + // panel 主题下的切换 + if ( panelTheme ) { + $target.find( ".iPicker-panel-tab > div" ).click( function () { + const index = $( this ).index(); + if ( !$( this ).parent().next().find( "ul" ).eq( index ).find( "li" ).length ) { + return; + } + $( this ) + .addClass( "iPicker-panel-tab-active" ) + .siblings() + .removeClass( "iPicker-panel-tab-active" ); + $target + .find( ".iPicker-panel-content ul" ) + .eq( $( this ).index() ) + .show() + .siblings() + .hide(); + } ) + } + + // 点击空白处隐藏列表 + $( document ).click( function ( event ) { + $container.each( function ( i ) { + if ( event.target !== this && !this.contains( event.target ) ) { + closeList( $list.eq( i ) ); + } + } ) + } ) + + return uid; + } + + // 创建组件 + // 等同于 iPicker 函数 + iPicker.create = ( target, options ) => iPicker( target, options ) + + // 设置值 + // value: + iPicker.set = ( id, value ) => { + const _target = cacheIPicker.target.get( id ); + if ( !id || !_target || !value || !Array.isArray( value ) || !value.length ) { + return; + } + + // 清除已选项 + iPicker.clear( id ); + + // 如果目标元素已经设置了 data-promise 属性 + // 说明已经获取到了第一层级的数据 + // 可直接执行 fn 函数 + if ( _target.dataset.promise ) { + + fn(); + } else { + + // 尚未获取到数据时 + // 调用 MutationObserver 方法监听 data-promise 属性的变化 + // 以此来判断是否已经获取到了第一层级的数据 + // 当获取到数据时执行 fn 函数 + new MutationObserver( () => { + fn(); + } ).observe( _target, { + attributes: true + } ); + } + + function fn () { + const ul = _target.querySelectorAll( "ul" ); + !( function set ( i ) { + const li = _target.querySelector( `[data-code="${ value[ i ] }"]` ); + if ( ul[ i + 1 ] ) { + + // 监听 ul 子元素的变化 + // 一旦已经生成了列表 + // 就可以执行后续操作 + new MutationObserver( () => { + ++i; + if ( i < value.length ) { + set( i ); + } + } ).observe( ul[ i + 1 ], { + childList: true + } ); + + li.click(); + } else { + li && li.click(); + } + } )( 0 ); + } + } + + // 获取值 + iPicker.get = ( id, type ) => { + const _target = cacheIPicker.target.get( id ); + if ( !id || !_target ) { + return; + } + + const result = cacheIPicker.value.get( _target ); + + // 获取地区行政编码 + if ( type === "code" || type === undefined ) { + return result[ 0 ]; + } + + // 获取地区名称 + if ( type === "name" ) { + return result[ 1 ]; + } + + // 获取地区行政编码和名称 + if ( type === "all" ) { + return result[ 2 ]; + } + } + + // 清空值 + iPicker.clear = id => { + const _target = cacheIPicker.target.get( id ); + if ( !id || !_target ) { + return; + } + + const $target = $( _target ); + const opt = cacheIPicker.options.get( _target ); + + // 清空值 + cacheIPicker.value.set( _target, [ [], [], [] ] ); + + $target.find( "input" ).val( "" ); + $target.find( "li" ).removeClass( "iPicker-list-active" ); + $target.find( "ul" ).each( function ( i ) { + const $this = $( this ); + + // 移除第一级以外的其它层级的内容 + if ( i ) { + $this.html( "" ); + if ( opt.theme === "cascader" ) { + $this.parent().css( "width", "200px" ); + $this.get().style.removeProperty( "display" ); + } + + } + } ) + if ( opt.theme === "panel" ) { + $target + .find( ".iPicker-panel-tab > div" ) + .eq( 0 ) + .addClass( "iPicker-panel-tab-active" ) + .siblings() + .removeClass( "iPicker-panel-tab-active" ); + $target.find( ".iPicker-panel-content > ul" ).eq( 0 ).show().siblings().hide(); + } + + // 滚动条回顶 + $target.find( ".iPicker-list" ).get().scrollTop = 0; + $target.find( "ul" ).get().scrollTop = 0; + + return id; + } + + // 重置组件 + iPicker.reset = id => { + const _target = cacheIPicker.target.get( id ); + if ( !id || !_target ) { + return; + } + + return iPicker( + cacheIPicker.originalElem.get( _target ), + cacheIPicker.options.get( _target ) + ); + } + + // 销毁组件 + iPicker.destroy = id => { + const _target = cacheIPicker.target.get( id ); + if ( !id || !_target ) { + return; + } + + // 移除存储 + cacheIPicker.originalElem.delete( _target ); + cacheIPicker.value.delete( _target ); + cacheIPicker.options.delete( _target ); + cacheIPicker.id.delete( _target ); + cacheIPicker.target.delete( _target.iPickerID ); + + // 移除自定义属性 + delete _target.iPickerID; + + // 清空容器 + _target.innerHTML = ""; + + // 当页面中没有 iPicker 组件时移除样式 + if ( !document.querySelector( ".iPicker-container" ) ) { + $( `#${ styleId }` ).remove(); + } + } + + // 启用组件 + iPicker.enabled = ( id, level ) => { + const _target = cacheIPicker.target.get( id ); + if ( !id || !_target || !level ) { + return; + } + + const $target = $( _target ); + const $result = $target.find( ".iPicker-result" ); + + // 启用所有层级 + if ( level === true ) { + $result.removeClass( "iPicker-disabled" ); + } + + // 启用指定的层级 + if ( util.isCorrectNumber( level ) ) { + level = [ level ]; + } + if ( Array.isArray( level ) && level.length ) { + level.forEach( v => { + if ( util.isCorrectNumber( v ) && v >= 1 && v <= 6 ) { + $result.eq( v - 1 ).removeClass( "iPicker-disabled" ); + } + } ) + } + + return id; + } + + // 禁用组件 + iPicker.disabled = ( id, level ) => { + const _target = cacheIPicker.target.get( id ); + if ( !id || !_target || !level ) { + return; + } + + const $target = $( _target ); + const $result = $target.find( ".iPicker-result" ); + + // 禁用所有层级 + if ( level === true ) { + $result.addClass( "iPicker-disabled" ); + } + + // 禁用指定的层级 + if ( util.isCorrectNumber( level ) ) { + level = [ level ]; + } + if ( Array.isArray( level ) && level.length ) { + level.forEach( v => { + if ( util.isCorrectNumber( v ) && v >= 1 && v <= 6 ) { + $result.eq( v - 1 ).addClass( "iPicker-disabled" ); + } + } ) + } + + return id; + } + + // 检测 code 合法性 + // 用于 enabledItem 和 disabledItem 方法 + const checkCode = code => code.filter( item => typeof item === "string" && item.match( /^\d{6,12}$/ ) ); + + // 启用全部或指定地区 + iPicker.enabledItem = ( id, code ) => { + const _target = cacheIPicker.target.get( id ); + if ( !id || !_target || !code ) { + return; + } + + // 观察器的配置 + const options = { + childList: true, + subtree: true + }; + + // 启用全部地区 + if ( code === true ) { + new MutationObserver( () => { + $( _target ).find( "li" ).removeClass( "iPicker-list-disabled" ); + } ).observe( _target, options ); + } + + // 启用指定地区 + if ( Array.isArray( code ) && code.length ) { + + // 检测与去重 + code = checkCode( [ ...new Set( code ) ] ); + const codeLen = code.length; + + new MutationObserver( () => { + for ( let i = 0; i < codeLen; i++ ) { + const li = _target.querySelector( `[data-code="${ code[ i ] }"]` ); + if ( li ) { + li.classList.remove( "iPicker-list-disabled" ); + } + } + } ).observe( _target, options ); + } + + return id; + } + + // 禁用全部或指定地区 + iPicker.disabledItem = ( id, code ) => { + const _target = cacheIPicker.target.get( id ); + if ( !id || !_target || !code ) { + return; + } + + // 观察器的配置 + const options = { + childList: true, + subtree: true + }; + + // 禁用全部地区 + if ( code === true ) { + new MutationObserver( () => { + $( _target ).find( "li" ).addClass( "iPicker-list-disabled" ); + } ).observe( _target, options ); + } + + // 禁用指定地区 + if ( Array.isArray( code ) && code.length ) { + + // 检测与去重 + code = checkCode( [ ...new Set( code ) ] ); + const codeLen = code.length; + + new MutationObserver( () => { + for ( let i = 0; i < codeLen; i++ ) { + const li = _target.querySelector( `[data-code="${ code[ i ] }"]` ); + if ( li ) { + li.classList.add( "iPicker-list-disabled" ); + } + } + } ).observe( _target, options ); + } + + return id; + } + + // return iPicker; + exports('iPicker', iPicker); + +} ); \ No newline at end of file diff --git a/public/js/lay-module/jmSheet/jmSheet.css b/public/js/lay-module/jmSheet/jmSheet.css new file mode 100644 index 0000000..6f61bf1 --- /dev/null +++ b/public/js/lay-module/jmSheet/jmSheet.css @@ -0,0 +1,80 @@ +.jmSheet { + width: 100%; + background-color: #f8f8f8 !important; +} + +.jmSheet li { + height: 55px; + border-bottom: 1px solid #eee; + display: flex; + /* justify-content: center; */ + align-items: center; + box-sizing: border-box; + background-color: #fff; + gap: 10px; + padding: 0 10px; +} + +.jmSheet li>.text { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} + +.jmSheet li:hover { + cursor: pointer; +} + +.jmSheet li:hover span { + color: #16b777; +} + +.jmSheet li .img { + position: relative; + width: 24px; + height: 24px; + overflow: hidden; + border-radius: 5px; +} + +.jmSheet li .img img { + position: absolute; + width: 100%; + height: 100%; + object-fit: cover; +} + +.jmSheet li span { + font-size: 14px; + color: rgba(0, 0, 0, .8); + font-weight: 600; +} + +.jmSheet li .desc { + font-weight: normal; + color: #c2c2c2; + font-size: 12px; +} + +.jmSheet .close { + height: 55px; + margin-top: 8px; + display: flex; + justify-content: center; + align-items: center; + background: #fff; +} + +.jmSheet>h3 { + height: 40px; + display: flex; + justify-content: center; + align-items: center; + background-color: #fff; + font-weight: normal; + font-size: 14px; + border-bottom: 1px solid #eee; + color: #00000080; + box-sizing: border-box; +} \ No newline at end of file diff --git a/public/js/lay-module/jmSheet/jmSheet.js b/public/js/lay-module/jmSheet/jmSheet.js new file mode 100644 index 0000000..fe0f9ea --- /dev/null +++ b/public/js/lay-module/jmSheet/jmSheet.js @@ -0,0 +1,88 @@ +layui.define(["jquery", "layer"], function (exports) { + "use strict"; + + var $ = layui.$; + var layer = layui.layer; + + var jmSheet = { + open: function (obj) { + + if (typeof obj.shadeClose === 'undefined') { + obj.shadeClose = true; + } + var align = "center" + if (typeof obj.align !== "undefined" && obj.align != "") { + switch (obj.align) { + case "left": + align = "flex-start"; + break; + case "right": + align = "flex-end"; + break; + default: + align = "center"; + } + } + + var html = ""; + $.each(obj.content, function (k, v) { + var img = ""; + if (typeof v.img !== 'undefined' && v.img != "") { + img = `
          `; + } + html += `
        • + ${img} +
          + ${v.text} + ${v.desc || ''} +
          +
        • ` + }) + + var btnClose = ""; + var btnCloseSize = 0; + if (typeof obj.shadeClose !== 'undefined' && !obj.shadeClose) { + btnClose = `取消`; + btnCloseSize = (55 + 8); + } + var title = ""; + var titleSize = 0; + if (typeof obj.title !== 'undefined' && obj.title != "") { + title = `

          ${obj.title}

          `; + titleSize = 40; + } + + //弹出 + var open = layer.open({ + type: 1, + title: false, + closeBtn: 0, + offset: 'b', + anim: 'slideUp', // 从下往上 + area: ['100%', titleSize + btnCloseSize + 55 * obj.content.length + 'px'], + shade: 0.1, + shadeClose: obj.shadeClose, // 是否点击遮罩关闭 + id: obj.id || '', + content: + `
          + ${title} +
            ${html}
          + ${btnClose} +
          ` + }); + + //关闭 + $(`#layui-layer${open} .jmSheet .close`).on("click", function () { + layer.close(open); + }) + + //点击列表 + $(`#layui-layer${open} .jmSheet`).on("click", "li", function () { + obj.callback($(this).index(), this, obj.content[$(this).index()]); + }) + } + } + + + exports('jmSheet', jmSheet); // 输出模块 +}).link(layui.cache.base+"jmSheet/jmSheet.css?v="+(new Date).getTime()); \ No newline at end of file diff --git a/public/js/lay-module/layarea/layarea.js b/public/js/lay-module/layarea/layarea.js new file mode 100644 index 0000000..d62e384 --- /dev/null +++ b/public/js/lay-module/layarea/layarea.js @@ -0,0 +1,4040 @@ +layui.define(['layer', 'form', 'laytpl'], function (exports) { + "use strict"; + + let $ = layui.$ + , form = layui.form + , layarea = { + _id: 0 + , config: {} + , set: function (options) { + let that = this; + that.config = $.extend({}, that.config, options); + return that; + } + , on: function (events, callback) { + return layui.onevent.call(this, 'layarea', events, callback); + } + } + , thisArea = function () { + let that = this; + return { + layarea: function (files) { + that.layarea.call(that, files); + } + , config: that.config + } + } + , Class = function (options) { + let that = this; + that.config = $.extend({}, that.config, layarea.config, options); + that.render(); + }; + + let areaList = { + province_list: { + 110000: '北京市', + 120000: '天津市', + 130000: '河北省', + 140000: '山西省', + 150000: '内蒙古自治区', + 210000: '辽宁省', + 220000: '吉林省', + 230000: '黑龙江省', + 310000: '上海市', + 320000: '江苏省', + 330000: '浙江省', + 340000: '安徽省', + 350000: '福建省', + 360000: '江西省', + 370000: '山东省', + 410000: '河南省', + 420000: '湖北省', + 430000: '湖南省', + 440000: '广东省', + 450000: '广西壮族自治区', + 460000: '海南省', + 500000: '重庆市', + 510000: '四川省', + 520000: '贵州省', + 530000: '云南省', + 540000: '西藏自治区', + 610000: '陕西省', + 620000: '甘肃省', + 630000: '青海省', + 640000: '宁夏回族自治区', + 650000: '新疆维吾尔自治区', + 710000: '台湾省', + 810000: '香港特别行政区', + 820000: '澳门特别行政区', + 900000: '海外' + }, + city_list: { + 110100: '北京市', + 120100: '天津市', + 130100: '石家庄市', + 130200: '唐山市', + 130300: '秦皇岛市', + 130400: '邯郸市', + 130500: '邢台市', + 130600: '保定市', + 130700: '张家口市', + 130800: '承德市', + 130900: '沧州市', + 131000: '廊坊市', + 131100: '衡水市', + 139000: '省直辖县', + 140100: '太原市', + 140200: '大同市', + 140300: '阳泉市', + 140400: '长治市', + 140500: '晋城市', + 140600: '朔州市', + 140700: '晋中市', + 140800: '运城市', + 140900: '忻州市', + 141000: '临汾市', + 141100: '吕梁市', + 150100: '呼和浩特市', + 150200: '包头市', + 150300: '乌海市', + 150400: '赤峰市', + 150500: '通辽市', + 150600: '鄂尔多斯市', + 150700: '呼伦贝尔市', + 150800: '巴彦淖尔市', + 150900: '乌兰察布市', + 152200: '兴安盟', + 152500: '锡林郭勒盟', + 152900: '阿拉善盟', + 210100: '沈阳市', + 210200: '大连市', + 210300: '鞍山市', + 210400: '抚顺市', + 210500: '本溪市', + 210600: '丹东市', + 210700: '锦州市', + 210800: '营口市', + 210900: '阜新市', + 211000: '辽阳市', + 211100: '盘锦市', + 211200: '铁岭市', + 211300: '朝阳市', + 211400: '葫芦岛市', + 220100: '长春市', + 220200: '吉林市', + 220300: '四平市', + 220400: '辽源市', + 220500: '通化市', + 220600: '白山市', + 220700: '松原市', + 220800: '白城市', + 222400: '延边朝鲜族自治州', + 230100: '哈尔滨市', + 230200: '齐齐哈尔市', + 230300: '鸡西市', + 230400: '鹤岗市', + 230500: '双鸭山市', + 230600: '大庆市', + 230700: '伊春市', + 230800: '佳木斯市', + 230900: '七台河市', + 231000: '牡丹江市', + 231100: '黑河市', + 231200: '绥化市', + 232700: '大兴安岭地区', + 310100: '上海市', + 320100: '南京市', + 320200: '无锡市', + 320300: '徐州市', + 320400: '常州市', + 320500: '苏州市', + 320600: '南通市', + 320700: '连云港市', + 320800: '淮安市', + 320900: '盐城市', + 321000: '扬州市', + 321100: '镇江市', + 321200: '泰州市', + 321300: '宿迁市', + 330100: '杭州市', + 330200: '宁波市', + 330300: '温州市', + 330400: '嘉兴市', + 330500: '湖州市', + 330600: '绍兴市', + 330700: '金华市', + 330800: '衢州市', + 330900: '舟山市', + 331000: '台州市', + 331100: '丽水市', + 340100: '合肥市', + 340200: '芜湖市', + 340300: '蚌埠市', + 340400: '淮南市', + 340500: '马鞍山市', + 340600: '淮北市', + 340700: '铜陵市', + 340800: '安庆市', + 341000: '黄山市', + 341100: '滁州市', + 341200: '阜阳市', + 341300: '宿州市', + 341500: '六安市', + 341600: '亳州市', + 341700: '池州市', + 341800: '宣城市', + 350100: '福州市', + 350200: '厦门市', + 350300: '莆田市', + 350400: '三明市', + 350500: '泉州市', + 350600: '漳州市', + 350700: '南平市', + 350800: '龙岩市', + 350900: '宁德市', + 360100: '南昌市', + 360200: '景德镇市', + 360300: '萍乡市', + 360400: '九江市', + 360500: '新余市', + 360600: '鹰潭市', + 360700: '赣州市', + 360800: '吉安市', + 360900: '宜春市', + 361000: '抚州市', + 361100: '上饶市', + 370100: '济南市', + 370200: '青岛市', + 370300: '淄博市', + 370400: '枣庄市', + 370500: '东营市', + 370600: '烟台市', + 370700: '潍坊市', + 370800: '济宁市', + 370900: '泰安市', + 371000: '威海市', + 371100: '日照市', + 371200: '莱芜市', + 371300: '临沂市', + 371400: '德州市', + 371500: '聊城市', + 371600: '滨州市', + 371700: '菏泽市', + 410100: '郑州市', + 410200: '开封市', + 410300: '洛阳市', + 410400: '平顶山市', + 410500: '安阳市', + 410600: '鹤壁市', + 410700: '新乡市', + 410800: '焦作市', + 410900: '濮阳市', + 411000: '许昌市', + 411100: '漯河市', + 411200: '三门峡市', + 411300: '南阳市', + 411400: '商丘市', + 411500: '信阳市', + 411600: '周口市', + 411700: '驻马店市', + 419000: '省直辖县', + 420100: '武汉市', + 420200: '黄石市', + 420300: '十堰市', + 420500: '宜昌市', + 420600: '襄阳市', + 420700: '鄂州市', + 420800: '荆门市', + 420900: '孝感市', + 421000: '荆州市', + 421100: '黄冈市', + 421200: '咸宁市', + 421300: '随州市', + 422800: '恩施土家族苗族自治州', + 429000: '省直辖县', + 430100: '长沙市', + 430200: '株洲市', + 430300: '湘潭市', + 430400: '衡阳市', + 430500: '邵阳市', + 430600: '岳阳市', + 430700: '常德市', + 430800: '张家界市', + 430900: '益阳市', + 431000: '郴州市', + 431100: '永州市', + 431200: '怀化市', + 431300: '娄底市', + 433100: '湘西土家族苗族自治州', + 440100: '广州市', + 440200: '韶关市', + 440300: '深圳市', + 440400: '珠海市', + 440500: '汕头市', + 440600: '佛山市', + 440700: '江门市', + 440800: '湛江市', + 440900: '茂名市', + 441200: '肇庆市', + 441300: '惠州市', + 441400: '梅州市', + 441500: '汕尾市', + 441600: '河源市', + 441700: '阳江市', + 441800: '清远市', + 441900: '东莞市', + 442000: '中山市', + 445100: '潮州市', + 445200: '揭阳市', + 445300: '云浮市', + 450100: '南宁市', + 450200: '柳州市', + 450300: '桂林市', + 450400: '梧州市', + 450500: '北海市', + 450600: '防城港市', + 450700: '钦州市', + 450800: '贵港市', + 450900: '玉林市', + 451000: '百色市', + 451100: '贺州市', + 451200: '河池市', + 451300: '来宾市', + 451400: '崇左市', + 460100: '海口市', + 460200: '三亚市', + 460300: '三沙市', + 460400: '儋州市', + 469000: '省直辖县', + 500100: '重庆市', + 500200: '县', + 510100: '成都市', + 510300: '自贡市', + 510400: '攀枝花市', + 510500: '泸州市', + 510600: '德阳市', + 510700: '绵阳市', + 510800: '广元市', + 510900: '遂宁市', + 511000: '内江市', + 511100: '乐山市', + 511300: '南充市', + 511400: '眉山市', + 511500: '宜宾市', + 511600: '广安市', + 511700: '达州市', + 511800: '雅安市', + 511900: '巴中市', + 512000: '资阳市', + 513200: '阿坝藏族羌族自治州', + 513300: '甘孜藏族自治州', + 513400: '凉山彝族自治州', + 520100: '贵阳市', + 520200: '六盘水市', + 520300: '遵义市', + 520400: '安顺市', + 520500: '毕节市', + 520600: '铜仁市', + 522300: '黔西南布依族苗族自治州', + 522600: '黔东南苗族侗族自治州', + 522700: '黔南布依族苗族自治州', + 530100: '昆明市', + 530300: '曲靖市', + 530400: '玉溪市', + 530500: '保山市', + 530600: '昭通市', + 530700: '丽江市', + 530800: '普洱市', + 530900: '临沧市', + 532300: '楚雄彝族自治州', + 532500: '红河哈尼族彝族自治州', + 532600: '文山壮族苗族自治州', + 532800: '西双版纳傣族自治州', + 532900: '大理白族自治州', + 533100: '德宏傣族景颇族自治州', + 533300: '怒江傈僳族自治州', + 533400: '迪庆藏族自治州', + 540100: '拉萨市', + 540200: '日喀则市', + 540300: '昌都市', + 540400: '林芝市', + 540500: '山南市', + 540600: '那曲市', + 542500: '阿里地区', + 610100: '西安市', + 610200: '铜川市', + 610300: '宝鸡市', + 610400: '咸阳市', + 610500: '渭南市', + 610600: '延安市', + 610700: '汉中市', + 610800: '榆林市', + 610900: '安康市', + 611000: '商洛市', + 620100: '兰州市', + 620200: '嘉峪关市', + 620300: '金昌市', + 620400: '白银市', + 620500: '天水市', + 620600: '武威市', + 620700: '张掖市', + 620800: '平凉市', + 620900: '酒泉市', + 621000: '庆阳市', + 621100: '定西市', + 621200: '陇南市', + 622900: '临夏回族自治州', + 623000: '甘南藏族自治州', + 630100: '西宁市', + 630200: '海东市', + 632200: '海北藏族自治州', + 632300: '黄南藏族自治州', + 632500: '海南藏族自治州', + 632600: '果洛藏族自治州', + 632700: '玉树藏族自治州', + 632800: '海西蒙古族藏族自治州', + 640100: '银川市', + 640200: '石嘴山市', + 640300: '吴忠市', + 640400: '固原市', + 640500: '中卫市', + 650100: '乌鲁木齐市', + 650200: '克拉玛依市', + 650400: '吐鲁番市', + 650500: '哈密市', + 652300: '昌吉回族自治州', + 652700: '博尔塔拉蒙古自治州', + 652800: '巴音郭楞蒙古自治州', + 652900: '阿克苏地区', + 653000: '克孜勒苏柯尔克孜自治州', + 653100: '喀什地区', + 653200: '和田地区', + 654000: '伊犁哈萨克自治州', + 654200: '塔城地区', + 654300: '阿勒泰地区', + 659000: '自治区直辖县级行政区划', + 710100: '台北市', + 710200: '高雄市', + 710300: '台南市', + 710400: '台中市', + 710500: '金门县', + 710600: '南投县', + 710700: '基隆市', + 710800: '新竹市', + 710900: '嘉义市', + 711100: '新北市', + 711200: '宜兰县', + 711300: '新竹县', + 711400: '桃园县', + 711500: '苗栗县', + 711700: '彰化县', + 711900: '嘉义县', + 712100: '云林县', + 712400: '屏东县', + 712500: '台东县', + 712600: '花莲县', + 712700: '澎湖县', + 712800: '连江县', + 810100: '香港岛', + 810200: '九龙', + 810300: '新界', + 820100: '澳门半岛', + 820200: '离岛', + 912400: '加拿大', + 941000: '韩国', + 984000: '美国' + }, + county_list: { + 110101: '东城区', + 110102: '西城区', + 110105: '朝阳区', + 110106: '丰台区', + 110107: '石景山区', + 110108: '海淀区', + 110109: '门头沟区', + 110111: '房山区', + 110112: '通州区', + 110113: '顺义区', + 110114: '昌平区', + 110115: '大兴区', + 110116: '怀柔区', + 110117: '平谷区', + 110118: '密云区', + 110119: '延庆区', + 120101: '和平区', + 120102: '河东区', + 120103: '河西区', + 120104: '南开区', + 120105: '河北区', + 120106: '红桥区', + 120110: '东丽区', + 120111: '西青区', + 120112: '津南区', + 120113: '北辰区', + 120114: '武清区', + 120115: '宝坻区', + 120116: '滨海新区', + 120117: '宁河区', + 120118: '静海区', + 120119: '蓟州区', + 130102: '长安区', + 130104: '桥西区', + 130105: '新华区', + 130107: '井陉矿区', + 130108: '裕华区', + 130109: '藁城区', + 130110: '鹿泉区', + 130111: '栾城区', + 130121: '井陉县', + 130123: '正定县', + 130125: '行唐县', + 130126: '灵寿县', + 130127: '高邑县', + 130128: '深泽县', + 130129: '赞皇县', + 130130: '无极县', + 130131: '平山县', + 130132: '元氏县', + 130133: '赵县', + 130181: '辛集市', + 130183: '晋州市', + 130184: '新乐市', + 130202: '路南区', + 130203: '路北区', + 130204: '古冶区', + 130205: '开平区', + 130207: '丰南区', + 130208: '丰润区', + 130209: '曹妃甸区', + 130223: '滦县', + 130224: '滦南县', + 130225: '乐亭县', + 130227: '迁西县', + 130229: '玉田县', + 130281: '遵化市', + 130283: '迁安市', + 130302: '海港区', + 130303: '山海关区', + 130304: '北戴河区', + 130306: '抚宁区', + 130321: '青龙满族自治县', + 130322: '昌黎县', + 130324: '卢龙县', + 130390: '经济技术开发区', + 130402: '邯山区', + 130403: '丛台区', + 130404: '复兴区', + 130406: '峰峰矿区', + 130407: '肥乡区', + 130408: '永年区', + 130423: '临漳县', + 130424: '成安县', + 130425: '大名县', + 130426: '涉县', + 130427: '磁县', + 130430: '邱县', + 130431: '鸡泽县', + 130432: '广平县', + 130433: '馆陶县', + 130434: '魏县', + 130435: '曲周县', + 130481: '武安市', + 130502: '桥东区', + 130503: '桥西区', + 130521: '邢台县', + 130522: '临城县', + 130523: '内丘县', + 130524: '柏乡县', + 130525: '隆尧县', + 130526: '任县', + 130527: '南和县', + 130528: '宁晋县', + 130529: '巨鹿县', + 130530: '新河县', + 130531: '广宗县', + 130532: '平乡县', + 130533: '威县', + 130534: '清河县', + 130535: '临西县', + 130581: '南宫市', + 130582: '沙河市', + 130602: '竞秀区', + 130606: '莲池区', + 130607: '满城区', + 130608: '清苑区', + 130609: '徐水区', + 130623: '涞水县', + 130624: '阜平县', + 130626: '定兴县', + 130627: '唐县', + 130628: '高阳县', + 130629: '容城县', + 130630: '涞源县', + 130631: '望都县', + 130632: '安新县', + 130633: '易县', + 130634: '曲阳县', + 130635: '蠡县', + 130636: '顺平县', + 130637: '博野县', + 130638: '雄县', + 130681: '涿州市', + 130682: '定州市', + 130683: '安国市', + 130684: '高碑店市', + 130702: '桥东区', + 130703: '桥西区', + 130705: '宣化区', + 130706: '下花园区', + 130708: '万全区', + 130709: '崇礼区', + 130722: '张北县', + 130723: '康保县', + 130724: '沽源县', + 130725: '尚义县', + 130726: '蔚县', + 130727: '阳原县', + 130728: '怀安县', + 130730: '怀来县', + 130731: '涿鹿县', + 130732: '赤城县', + 130802: '双桥区', + 130803: '双滦区', + 130804: '鹰手营子矿区', + 130821: '承德县', + 130822: '兴隆县', + 130824: '滦平县', + 130825: '隆化县', + 130826: '丰宁满族自治县', + 130827: '宽城满族自治县', + 130828: '围场满族蒙古族自治县', + 130881: '平泉市', + 130902: '新华区', + 130903: '运河区', + 130921: '沧县', + 130922: '青县', + 130923: '东光县', + 130924: '海兴县', + 130925: '盐山县', + 130926: '肃宁县', + 130927: '南皮县', + 130928: '吴桥县', + 130929: '献县', + 130930: '孟村回族自治县', + 130981: '泊头市', + 130982: '任丘市', + 130983: '黄骅市', + 130984: '河间市', + 131002: '安次区', + 131003: '广阳区', + 131022: '固安县', + 131023: '永清县', + 131024: '香河县', + 131025: '大城县', + 131026: '文安县', + 131028: '大厂回族自治县', + 131081: '霸州市', + 131082: '三河市', + 131090: '开发区', + 131102: '桃城区', + 131103: '冀州区', + 131121: '枣强县', + 131122: '武邑县', + 131123: '武强县', + 131124: '饶阳县', + 131125: '安平县', + 131126: '故城县', + 131127: '景县', + 131128: '阜城县', + 131182: '深州市', + 140105: '小店区', + 140106: '迎泽区', + 140107: '杏花岭区', + 140108: '尖草坪区', + 140109: '万柏林区', + 140110: '晋源区', + 140121: '清徐县', + 140122: '阳曲县', + 140123: '娄烦县', + 140181: '古交市', + 140202: '城区', + 140203: '矿区', + 140211: '南郊区', + 140212: '新荣区', + 140221: '阳高县', + 140222: '天镇县', + 140223: '广灵县', + 140224: '灵丘县', + 140225: '浑源县', + 140226: '左云县', + 140227: '大同县', + 140302: '城区', + 140303: '矿区', + 140311: '郊区', + 140321: '平定县', + 140322: '盂县', + 140402: '城区', + 140411: '郊区', + 140421: '长治县', + 140423: '襄垣县', + 140424: '屯留县', + 140425: '平顺县', + 140426: '黎城县', + 140427: '壶关县', + 140428: '长子县', + 140429: '武乡县', + 140430: '沁县', + 140431: '沁源县', + 140481: '潞城市', + 140502: '城区', + 140521: '沁水县', + 140522: '阳城县', + 140524: '陵川县', + 140525: '泽州县', + 140581: '高平市', + 140602: '朔城区', + 140603: '平鲁区', + 140621: '山阴县', + 140622: '应县', + 140623: '右玉县', + 140624: '怀仁县', + 140702: '榆次区', + 140721: '榆社县', + 140722: '左权县', + 140723: '和顺县', + 140724: '昔阳县', + 140725: '寿阳县', + 140726: '太谷县', + 140727: '祁县', + 140728: '平遥县', + 140729: '灵石县', + 140781: '介休市', + 140802: '盐湖区', + 140821: '临猗县', + 140822: '万荣县', + 140823: '闻喜县', + 140824: '稷山县', + 140825: '新绛县', + 140826: '绛县', + 140827: '垣曲县', + 140828: '夏县', + 140829: '平陆县', + 140830: '芮城县', + 140881: '永济市', + 140882: '河津市', + 140902: '忻府区', + 140921: '定襄县', + 140922: '五台县', + 140923: '代县', + 140924: '繁峙县', + 140925: '宁武县', + 140926: '静乐县', + 140927: '神池县', + 140928: '五寨县', + 140929: '岢岚县', + 140930: '河曲县', + 140931: '保德县', + 140932: '偏关县', + 140981: '原平市', + 141002: '尧都区', + 141021: '曲沃县', + 141022: '翼城县', + 141023: '襄汾县', + 141024: '洪洞县', + 141025: '古县', + 141026: '安泽县', + 141027: '浮山县', + 141028: '吉县', + 141029: '乡宁县', + 141030: '大宁县', + 141031: '隰县', + 141032: '永和县', + 141033: '蒲县', + 141034: '汾西县', + 141081: '侯马市', + 141082: '霍州市', + 141102: '离石区', + 141121: '文水县', + 141122: '交城县', + 141123: '兴县', + 141124: '临县', + 141125: '柳林县', + 141126: '石楼县', + 141127: '岚县', + 141128: '方山县', + 141129: '中阳县', + 141130: '交口县', + 141181: '孝义市', + 141182: '汾阳市', + 150102: '新城区', + 150103: '回民区', + 150104: '玉泉区', + 150105: '赛罕区', + 150121: '土默特左旗', + 150122: '托克托县', + 150123: '和林格尔县', + 150124: '清水河县', + 150125: '武川县', + 150202: '东河区', + 150203: '昆都仑区', + 150204: '青山区', + 150205: '石拐区', + 150206: '白云鄂博矿区', + 150207: '九原区', + 150221: '土默特右旗', + 150222: '固阳县', + 150223: '达尔罕茂明安联合旗', + 150302: '海勃湾区', + 150303: '海南区', + 150304: '乌达区', + 150402: '红山区', + 150403: '元宝山区', + 150404: '松山区', + 150421: '阿鲁科尔沁旗', + 150422: '巴林左旗', + 150423: '巴林右旗', + 150424: '林西县', + 150425: '克什克腾旗', + 150426: '翁牛特旗', + 150428: '喀喇沁旗', + 150429: '宁城县', + 150430: '敖汉旗', + 150502: '科尔沁区', + 150521: '科尔沁左翼中旗', + 150522: '科尔沁左翼后旗', + 150523: '开鲁县', + 150524: '库伦旗', + 150525: '奈曼旗', + 150526: '扎鲁特旗', + 150581: '霍林郭勒市', + 150602: '东胜区', + 150603: '康巴什区', + 150621: '达拉特旗', + 150622: '准格尔旗', + 150623: '鄂托克前旗', + 150624: '鄂托克旗', + 150625: '杭锦旗', + 150626: '乌审旗', + 150627: '伊金霍洛旗', + 150702: '海拉尔区', + 150703: '扎赉诺尔区', + 150721: '阿荣旗', + 150722: '莫力达瓦达斡尔族自治旗', + 150723: '鄂伦春自治旗', + 150724: '鄂温克族自治旗', + 150725: '陈巴尔虎旗', + 150726: '新巴尔虎左旗', + 150727: '新巴尔虎右旗', + 150781: '满洲里市', + 150782: '牙克石市', + 150783: '扎兰屯市', + 150784: '额尔古纳市', + 150785: '根河市', + 150802: '临河区', + 150821: '五原县', + 150822: '磴口县', + 150823: '乌拉特前旗', + 150824: '乌拉特中旗', + 150825: '乌拉特后旗', + 150826: '杭锦后旗', + 150902: '集宁区', + 150921: '卓资县', + 150922: '化德县', + 150923: '商都县', + 150924: '兴和县', + 150925: '凉城县', + 150926: '察哈尔右翼前旗', + 150927: '察哈尔右翼中旗', + 150928: '察哈尔右翼后旗', + 150929: '四子王旗', + 150981: '丰镇市', + 152201: '乌兰浩特市', + 152202: '阿尔山市', + 152221: '科尔沁右翼前旗', + 152222: '科尔沁右翼中旗', + 152223: '扎赉特旗', + 152224: '突泉县', + 152501: '二连浩特市', + 152502: '锡林浩特市', + 152522: '阿巴嘎旗', + 152523: '苏尼特左旗', + 152524: '苏尼特右旗', + 152525: '东乌珠穆沁旗', + 152526: '西乌珠穆沁旗', + 152527: '太仆寺旗', + 152528: '镶黄旗', + 152529: '正镶白旗', + 152530: '正蓝旗', + 152531: '多伦县', + 152921: '阿拉善左旗', + 152922: '阿拉善右旗', + 152923: '额济纳旗', + 210102: '和平区', + 210103: '沈河区', + 210104: '大东区', + 210105: '皇姑区', + 210106: '铁西区', + 210111: '苏家屯区', + 210112: '浑南区', + 210113: '沈北新区', + 210114: '于洪区', + 210115: '辽中区', + 210123: '康平县', + 210124: '法库县', + 210181: '新民市', + 210190: '经济技术开发区', + 210202: '中山区', + 210203: '西岗区', + 210204: '沙河口区', + 210211: '甘井子区', + 210212: '旅顺口区', + 210213: '金州区', + 210214: '普兰店区', + 210224: '长海县', + 210281: '瓦房店市', + 210283: '庄河市', + 210302: '铁东区', + 210303: '铁西区', + 210304: '立山区', + 210311: '千山区', + 210321: '台安县', + 210323: '岫岩满族自治县', + 210381: '海城市', + 210390: '高新区', + 210402: '新抚区', + 210403: '东洲区', + 210404: '望花区', + 210411: '顺城区', + 210421: '抚顺县', + 210422: '新宾满族自治县', + 210423: '清原满族自治县', + 210502: '平山区', + 210503: '溪湖区', + 210504: '明山区', + 210505: '南芬区', + 210521: '本溪满族自治县', + 210522: '桓仁满族自治县', + 210602: '元宝区', + 210603: '振兴区', + 210604: '振安区', + 210624: '宽甸满族自治县', + 210681: '东港市', + 210682: '凤城市', + 210702: '古塔区', + 210703: '凌河区', + 210711: '太和区', + 210726: '黑山县', + 210727: '义县', + 210781: '凌海市', + 210782: '北镇市', + 210793: '经济技术开发区', + 210802: '站前区', + 210803: '西市区', + 210804: '鲅鱼圈区', + 210811: '老边区', + 210881: '盖州市', + 210882: '大石桥市', + 210902: '海州区', + 210903: '新邱区', + 210904: '太平区', + 210905: '清河门区', + 210911: '细河区', + 210921: '阜新蒙古族自治县', + 210922: '彰武县', + 211002: '白塔区', + 211003: '文圣区', + 211004: '宏伟区', + 211005: '弓长岭区', + 211011: '太子河区', + 211021: '辽阳县', + 211081: '灯塔市', + 211102: '双台子区', + 211103: '兴隆台区', + 211104: '大洼区', + 211122: '盘山县', + 211202: '银州区', + 211204: '清河区', + 211221: '铁岭县', + 211223: '西丰县', + 211224: '昌图县', + 211281: '调兵山市', + 211282: '开原市', + 211302: '双塔区', + 211303: '龙城区', + 211321: '朝阳县', + 211322: '建平县', + 211324: '喀喇沁左翼蒙古族自治县', + 211381: '北票市', + 211382: '凌源市', + 211402: '连山区', + 211403: '龙港区', + 211404: '南票区', + 211421: '绥中县', + 211422: '建昌县', + 211481: '兴城市', + 215090: '工业园区', + 220102: '南关区', + 220103: '宽城区', + 220104: '朝阳区', + 220105: '二道区', + 220106: '绿园区', + 220112: '双阳区', + 220113: '九台区', + 220122: '农安县', + 220182: '榆树市', + 220183: '德惠市', + 220192: '经济技术开发区', + 220202: '昌邑区', + 220203: '龙潭区', + 220204: '船营区', + 220211: '丰满区', + 220221: '永吉县', + 220281: '蛟河市', + 220282: '桦甸市', + 220283: '舒兰市', + 220284: '磐石市', + 220302: '铁西区', + 220303: '铁东区', + 220322: '梨树县', + 220323: '伊通满族自治县', + 220381: '公主岭市', + 220382: '双辽市', + 220402: '龙山区', + 220403: '西安区', + 220421: '东丰县', + 220422: '东辽县', + 220502: '东昌区', + 220503: '二道江区', + 220521: '通化县', + 220523: '辉南县', + 220524: '柳河县', + 220581: '梅河口市', + 220582: '集安市', + 220602: '浑江区', + 220605: '江源区', + 220621: '抚松县', + 220622: '靖宇县', + 220623: '长白朝鲜族自治县', + 220681: '临江市', + 220702: '宁江区', + 220721: '前郭尔罗斯蒙古族自治县', + 220722: '长岭县', + 220723: '乾安县', + 220781: '扶余市', + 220802: '洮北区', + 220821: '镇赉县', + 220822: '通榆县', + 220881: '洮南市', + 220882: '大安市', + 221090: '工业园区', + 222401: '延吉市', + 222402: '图们市', + 222403: '敦化市', + 222404: '珲春市', + 222405: '龙井市', + 222406: '和龙市', + 222424: '汪清县', + 222426: '安图县', + 230102: '道里区', + 230103: '南岗区', + 230104: '道外区', + 230108: '平房区', + 230109: '松北区', + 230110: '香坊区', + 230111: '呼兰区', + 230112: '阿城区', + 230113: '双城区', + 230123: '依兰县', + 230124: '方正县', + 230125: '宾县', + 230126: '巴彦县', + 230127: '木兰县', + 230128: '通河县', + 230129: '延寿县', + 230183: '尚志市', + 230184: '五常市', + 230202: '龙沙区', + 230203: '建华区', + 230204: '铁锋区', + 230205: '昂昂溪区', + 230206: '富拉尔基区', + 230207: '碾子山区', + 230208: '梅里斯达斡尔族区', + 230221: '龙江县', + 230223: '依安县', + 230224: '泰来县', + 230225: '甘南县', + 230227: '富裕县', + 230229: '克山县', + 230230: '克东县', + 230231: '拜泉县', + 230281: '讷河市', + 230302: '鸡冠区', + 230303: '恒山区', + 230304: '滴道区', + 230305: '梨树区', + 230306: '城子河区', + 230307: '麻山区', + 230321: '鸡东县', + 230381: '虎林市', + 230382: '密山市', + 230402: '向阳区', + 230403: '工农区', + 230404: '南山区', + 230405: '兴安区', + 230406: '东山区', + 230407: '兴山区', + 230421: '萝北县', + 230422: '绥滨县', + 230502: '尖山区', + 230503: '岭东区', + 230505: '四方台区', + 230506: '宝山区', + 230521: '集贤县', + 230522: '友谊县', + 230523: '宝清县', + 230524: '饶河县', + 230602: '萨尔图区', + 230603: '龙凤区', + 230604: '让胡路区', + 230605: '红岗区', + 230606: '大同区', + 230621: '肇州县', + 230622: '肇源县', + 230623: '林甸县', + 230624: '杜尔伯特蒙古族自治县', + 230702: '伊春区', + 230703: '南岔区', + 230704: '友好区', + 230705: '西林区', + 230706: '翠峦区', + 230707: '新青区', + 230708: '美溪区', + 230709: '金山屯区', + 230710: '五营区', + 230711: '乌马河区', + 230712: '汤旺河区', + 230713: '带岭区', + 230714: '乌伊岭区', + 230715: '红星区', + 230716: '上甘岭区', + 230722: '嘉荫县', + 230781: '铁力市', + 230803: '向阳区', + 230804: '前进区', + 230805: '东风区', + 230811: '郊区', + 230822: '桦南县', + 230826: '桦川县', + 230828: '汤原县', + 230881: '同江市', + 230882: '富锦市', + 230883: '抚远市', + 230902: '新兴区', + 230903: '桃山区', + 230904: '茄子河区', + 230921: '勃利县', + 231002: '东安区', + 231003: '阳明区', + 231004: '爱民区', + 231005: '西安区', + 231025: '林口县', + 231081: '绥芬河市', + 231083: '海林市', + 231084: '宁安市', + 231085: '穆棱市', + 231086: '东宁市', + 231102: '爱辉区', + 231121: '嫩江县', + 231123: '逊克县', + 231124: '孙吴县', + 231181: '北安市', + 231182: '五大连池市', + 231202: '北林区', + 231221: '望奎县', + 231222: '兰西县', + 231223: '青冈县', + 231224: '庆安县', + 231225: '明水县', + 231226: '绥棱县', + 231281: '安达市', + 231282: '肇东市', + 231283: '海伦市', + 232721: '呼玛县', + 232722: '塔河县', + 232723: '漠河县', + 232790: '松岭区', + 232791: '呼中区', + 232792: '加格达奇区', + 232793: '新林区', + 310101: '黄浦区', + 310104: '徐汇区', + 310105: '长宁区', + 310106: '静安区', + 310107: '普陀区', + 310109: '虹口区', + 310110: '杨浦区', + 310112: '闵行区', + 310113: '宝山区', + 310114: '嘉定区', + 310115: '浦东新区', + 310116: '金山区', + 310117: '松江区', + 310118: '青浦区', + 310120: '奉贤区', + 310151: '崇明区', + 320102: '玄武区', + 320104: '秦淮区', + 320105: '建邺区', + 320106: '鼓楼区', + 320111: '浦口区', + 320113: '栖霞区', + 320114: '雨花台区', + 320115: '江宁区', + 320116: '六合区', + 320117: '溧水区', + 320118: '高淳区', + 320205: '锡山区', + 320206: '惠山区', + 320211: '滨湖区', + 320213: '梁溪区', + 320214: '新吴区', + 320281: '江阴市', + 320282: '宜兴市', + 320302: '鼓楼区', + 320303: '云龙区', + 320305: '贾汪区', + 320311: '泉山区', + 320312: '铜山区', + 320321: '丰县', + 320322: '沛县', + 320324: '睢宁县', + 320381: '新沂市', + 320382: '邳州市', + 320391: '工业园区', + 320402: '天宁区', + 320404: '钟楼区', + 320411: '新北区', + 320412: '武进区', + 320413: '金坛区', + 320481: '溧阳市', + 320505: '虎丘区', + 320506: '吴中区', + 320507: '相城区', + 320508: '姑苏区', + 320509: '吴江区', + 320581: '常熟市', + 320582: '张家港市', + 320583: '昆山市', + 320585: '太仓市', + 320590: '工业园区', + 320591: '高新区', + 320602: '崇川区', + 320611: '港闸区', + 320612: '通州区', + 320621: '海安县', + 320623: '如东县', + 320681: '启东市', + 320682: '如皋市', + 320684: '海门市', + 320691: '高新区', + 320703: '连云区', + 320706: '海州区', + 320707: '赣榆区', + 320722: '东海县', + 320723: '灌云县', + 320724: '灌南县', + 320803: '淮安区', + 320804: '淮阴区', + 320812: '清江浦区', + 320813: '洪泽区', + 320826: '涟水县', + 320830: '盱眙县', + 320831: '金湖县', + 320890: '经济开发区', + 320902: '亭湖区', + 320903: '盐都区', + 320904: '大丰区', + 320921: '响水县', + 320922: '滨海县', + 320923: '阜宁县', + 320924: '射阳县', + 320925: '建湖县', + 320981: '东台市', + 321002: '广陵区', + 321003: '邗江区', + 321012: '江都区', + 321023: '宝应县', + 321081: '仪征市', + 321084: '高邮市', + 321090: '经济开发区', + 321102: '京口区', + 321111: '润州区', + 321112: '丹徒区', + 321181: '丹阳市', + 321182: '扬中市', + 321183: '句容市', + 321202: '海陵区', + 321203: '高港区', + 321204: '姜堰区', + 321281: '兴化市', + 321282: '靖江市', + 321283: '泰兴市', + 321302: '宿城区', + 321311: '宿豫区', + 321322: '沭阳县', + 321323: '泗阳县', + 321324: '泗洪县', + 330102: '上城区', + 330103: '下城区', + 330104: '江干区', + 330105: '拱墅区', + 330106: '西湖区', + 330108: '滨江区', + 330109: '萧山区', + 330110: '余杭区', + 330111: '富阳区', + 330112: '临安区', + 330122: '桐庐县', + 330127: '淳安县', + 330182: '建德市', + 330203: '海曙区', + 330205: '江北区', + 330206: '北仑区', + 330211: '镇海区', + 330212: '鄞州区', + 330213: '奉化区', + 330225: '象山县', + 330226: '宁海县', + 330281: '余姚市', + 330282: '慈溪市', + 330302: '鹿城区', + 330303: '龙湾区', + 330304: '瓯海区', + 330305: '洞头区', + 330324: '永嘉县', + 330326: '平阳县', + 330327: '苍南县', + 330328: '文成县', + 330329: '泰顺县', + 330381: '瑞安市', + 330382: '乐清市', + 330402: '南湖区', + 330411: '秀洲区', + 330421: '嘉善县', + 330424: '海盐县', + 330481: '海宁市', + 330482: '平湖市', + 330483: '桐乡市', + 330502: '吴兴区', + 330503: '南浔区', + 330521: '德清县', + 330522: '长兴县', + 330523: '安吉县', + 330602: '越城区', + 330603: '柯桥区', + 330604: '上虞区', + 330624: '新昌县', + 330681: '诸暨市', + 330683: '嵊州市', + 330702: '婺城区', + 330703: '金东区', + 330723: '武义县', + 330726: '浦江县', + 330727: '磐安县', + 330781: '兰溪市', + 330782: '义乌市', + 330783: '东阳市', + 330784: '永康市', + 330802: '柯城区', + 330803: '衢江区', + 330822: '常山县', + 330824: '开化县', + 330825: '龙游县', + 330881: '江山市', + 330902: '定海区', + 330903: '普陀区', + 330921: '岱山县', + 330922: '嵊泗县', + 331002: '椒江区', + 331003: '黄岩区', + 331004: '路桥区', + 331022: '三门县', + 331023: '天台县', + 331024: '仙居县', + 331081: '温岭市', + 331082: '临海市', + 331083: '玉环市', + 331102: '莲都区', + 331121: '青田县', + 331122: '缙云县', + 331123: '遂昌县', + 331124: '松阳县', + 331125: '云和县', + 331126: '庆元县', + 331127: '景宁畲族自治县', + 331181: '龙泉市', + 340102: '瑶海区', + 340103: '庐阳区', + 340104: '蜀山区', + 340111: '包河区', + 340121: '长丰县', + 340122: '肥东县', + 340123: '肥西县', + 340124: '庐江县', + 340181: '巢湖市', + 340190: '高新技术开发区', + 340191: '经济技术开发区', + 340202: '镜湖区', + 340203: '弋江区', + 340207: '鸠江区', + 340208: '三山区', + 340221: '芜湖县', + 340222: '繁昌县', + 340223: '南陵县', + 340225: '无为县', + 340302: '龙子湖区', + 340303: '蚌山区', + 340304: '禹会区', + 340311: '淮上区', + 340321: '怀远县', + 340322: '五河县', + 340323: '固镇县', + 340402: '大通区', + 340403: '田家庵区', + 340404: '谢家集区', + 340405: '八公山区', + 340406: '潘集区', + 340421: '凤台县', + 340422: '寿县', + 340503: '花山区', + 340504: '雨山区', + 340506: '博望区', + 340521: '当涂县', + 340522: '含山县', + 340523: '和县', + 340602: '杜集区', + 340603: '相山区', + 340604: '烈山区', + 340621: '濉溪县', + 340705: '铜官区', + 340706: '义安区', + 340711: '郊区', + 340722: '枞阳县', + 340802: '迎江区', + 340803: '大观区', + 340811: '宜秀区', + 340822: '怀宁县', + 340824: '潜山县', + 340825: '太湖县', + 340826: '宿松县', + 340827: '望江县', + 340828: '岳西县', + 340881: '桐城市', + 341002: '屯溪区', + 341003: '黄山区', + 341004: '徽州区', + 341021: '歙县', + 341022: '休宁县', + 341023: '黟县', + 341024: '祁门县', + 341102: '琅琊区', + 341103: '南谯区', + 341122: '来安县', + 341124: '全椒县', + 341125: '定远县', + 341126: '凤阳县', + 341181: '天长市', + 341182: '明光市', + 341202: '颍州区', + 341203: '颍东区', + 341204: '颍泉区', + 341221: '临泉县', + 341222: '太和县', + 341225: '阜南县', + 341226: '颍上县', + 341282: '界首市', + 341302: '埇桥区', + 341321: '砀山县', + 341322: '萧县', + 341323: '灵璧县', + 341324: '泗县', + 341390: '经济开发区', + 341502: '金安区', + 341503: '裕安区', + 341504: '叶集区', + 341522: '霍邱县', + 341523: '舒城县', + 341524: '金寨县', + 341525: '霍山县', + 341602: '谯城区', + 341621: '涡阳县', + 341622: '蒙城县', + 341623: '利辛县', + 341702: '贵池区', + 341721: '东至县', + 341722: '石台县', + 341723: '青阳县', + 341802: '宣州区', + 341821: '郎溪县', + 341822: '广德县', + 341823: '泾县', + 341824: '绩溪县', + 341825: '旌德县', + 341881: '宁国市', + 350102: '鼓楼区', + 350103: '台江区', + 350104: '仓山区', + 350105: '马尾区', + 350111: '晋安区', + 350112: '长乐区', + 350121: '闽侯县', + 350122: '连江县', + 350123: '罗源县', + 350124: '闽清县', + 350125: '永泰县', + 350128: '平潭县', + 350181: '福清市', + 350203: '思明区', + 350205: '海沧区', + 350206: '湖里区', + 350211: '集美区', + 350212: '同安区', + 350213: '翔安区', + 350302: '城厢区', + 350303: '涵江区', + 350304: '荔城区', + 350305: '秀屿区', + 350322: '仙游县', + 350402: '梅列区', + 350403: '三元区', + 350421: '明溪县', + 350423: '清流县', + 350424: '宁化县', + 350425: '大田县', + 350426: '尤溪县', + 350427: '沙县', + 350428: '将乐县', + 350429: '泰宁县', + 350430: '建宁县', + 350481: '永安市', + 350502: '鲤城区', + 350503: '丰泽区', + 350504: '洛江区', + 350505: '泉港区', + 350521: '惠安县', + 350524: '安溪县', + 350525: '永春县', + 350526: '德化县', + 350527: '金门县', + 350581: '石狮市', + 350582: '晋江市', + 350583: '南安市', + 350602: '芗城区', + 350603: '龙文区', + 350622: '云霄县', + 350623: '漳浦县', + 350624: '诏安县', + 350625: '长泰县', + 350626: '东山县', + 350627: '南靖县', + 350628: '平和县', + 350629: '华安县', + 350681: '龙海市', + 350702: '延平区', + 350703: '建阳区', + 350721: '顺昌县', + 350722: '浦城县', + 350723: '光泽县', + 350724: '松溪县', + 350725: '政和县', + 350781: '邵武市', + 350782: '武夷山市', + 350783: '建瓯市', + 350802: '新罗区', + 350803: '永定区', + 350821: '长汀县', + 350823: '上杭县', + 350824: '武平县', + 350825: '连城县', + 350881: '漳平市', + 350902: '蕉城区', + 350921: '霞浦县', + 350922: '古田县', + 350923: '屏南县', + 350924: '寿宁县', + 350925: '周宁县', + 350926: '柘荣县', + 350981: '福安市', + 350982: '福鼎市', + 360102: '东湖区', + 360103: '西湖区', + 360104: '青云谱区', + 360105: '湾里区', + 360111: '青山湖区', + 360112: '新建区', + 360121: '南昌县', + 360123: '安义县', + 360124: '进贤县', + 360190: '经济技术开发区', + 360192: '高新区', + 360202: '昌江区', + 360203: '珠山区', + 360222: '浮梁县', + 360281: '乐平市', + 360302: '安源区', + 360313: '湘东区', + 360321: '莲花县', + 360322: '上栗县', + 360323: '芦溪县', + 360402: '濂溪区', + 360403: '浔阳区', + 360404: '柴桑区', + 360423: '武宁县', + 360424: '修水县', + 360425: '永修县', + 360426: '德安县', + 360428: '都昌县', + 360429: '湖口县', + 360430: '彭泽县', + 360481: '瑞昌市', + 360482: '共青城市', + 360483: '庐山市', + 360490: '经济技术开发区', + 360502: '渝水区', + 360521: '分宜县', + 360602: '月湖区', + 360622: '余江县', + 360681: '贵溪市', + 360702: '章贡区', + 360703: '南康区', + 360704: '赣县区', + 360722: '信丰县', + 360723: '大余县', + 360724: '上犹县', + 360725: '崇义县', + 360726: '安远县', + 360727: '龙南县', + 360728: '定南县', + 360729: '全南县', + 360730: '宁都县', + 360731: '于都县', + 360732: '兴国县', + 360733: '会昌县', + 360734: '寻乌县', + 360735: '石城县', + 360781: '瑞金市', + 360802: '吉州区', + 360803: '青原区', + 360821: '吉安县', + 360822: '吉水县', + 360823: '峡江县', + 360824: '新干县', + 360825: '永丰县', + 360826: '泰和县', + 360827: '遂川县', + 360828: '万安县', + 360829: '安福县', + 360830: '永新县', + 360881: '井冈山市', + 360902: '袁州区', + 360921: '奉新县', + 360922: '万载县', + 360923: '上高县', + 360924: '宜丰县', + 360925: '靖安县', + 360926: '铜鼓县', + 360981: '丰城市', + 360982: '樟树市', + 360983: '高安市', + 361002: '临川区', + 361003: '东乡区', + 361021: '南城县', + 361022: '黎川县', + 361023: '南丰县', + 361024: '崇仁县', + 361025: '乐安县', + 361026: '宜黄县', + 361027: '金溪县', + 361028: '资溪县', + 361030: '广昌县', + 361102: '信州区', + 361103: '广丰区', + 361121: '上饶县', + 361123: '玉山县', + 361124: '铅山县', + 361125: '横峰县', + 361126: '弋阳县', + 361127: '余干县', + 361128: '鄱阳县', + 361129: '万年县', + 361130: '婺源县', + 361181: '德兴市', + 370102: '历下区', + 370103: '市中区', + 370104: '槐荫区', + 370105: '天桥区', + 370112: '历城区', + 370113: '长清区', + 370114: '章丘区', + 370124: '平阴县', + 370125: '济阳县', + 370126: '商河县', + 370190: '高新区', + 370202: '市南区', + 370203: '市北区', + 370211: '黄岛区', + 370212: '崂山区', + 370213: '李沧区', + 370214: '城阳区', + 370215: '即墨区', + 370281: '胶州市', + 370283: '平度市', + 370285: '莱西市', + 370290: '开发区', + 370302: '淄川区', + 370303: '张店区', + 370304: '博山区', + 370305: '临淄区', + 370306: '周村区', + 370321: '桓台县', + 370322: '高青县', + 370323: '沂源县', + 370402: '市中区', + 370403: '薛城区', + 370404: '峄城区', + 370405: '台儿庄区', + 370406: '山亭区', + 370481: '滕州市', + 370502: '东营区', + 370503: '河口区', + 370505: '垦利区', + 370522: '利津县', + 370523: '广饶县', + 370602: '芝罘区', + 370611: '福山区', + 370612: '牟平区', + 370613: '莱山区', + 370634: '长岛县', + 370681: '龙口市', + 370682: '莱阳市', + 370683: '莱州市', + 370684: '蓬莱市', + 370685: '招远市', + 370686: '栖霞市', + 370687: '海阳市', + 370690: '开发区', + 370702: '潍城区', + 370703: '寒亭区', + 370704: '坊子区', + 370705: '奎文区', + 370724: '临朐县', + 370725: '昌乐县', + 370781: '青州市', + 370782: '诸城市', + 370783: '寿光市', + 370784: '安丘市', + 370785: '高密市', + 370786: '昌邑市', + 370790: '开发区', + 370791: '高新区', + 370811: '任城区', + 370812: '兖州区', + 370826: '微山县', + 370827: '鱼台县', + 370828: '金乡县', + 370829: '嘉祥县', + 370830: '汶上县', + 370831: '泗水县', + 370832: '梁山县', + 370881: '曲阜市', + 370883: '邹城市', + 370890: '高新区', + 370902: '泰山区', + 370911: '岱岳区', + 370921: '宁阳县', + 370923: '东平县', + 370982: '新泰市', + 370983: '肥城市', + 371002: '环翠区', + 371003: '文登区', + 371082: '荣成市', + 371083: '乳山市', + 371091: '经济技术开发区', + 371102: '东港区', + 371103: '岚山区', + 371121: '五莲县', + 371122: '莒县', + 371202: '莱城区', + 371203: '钢城区', + 371302: '兰山区', + 371311: '罗庄区', + 371312: '河东区', + 371321: '沂南县', + 371322: '郯城县', + 371323: '沂水县', + 371324: '兰陵县', + 371325: '费县', + 371326: '平邑县', + 371327: '莒南县', + 371328: '蒙阴县', + 371329: '临沭县', + 371402: '德城区', + 371403: '陵城区', + 371422: '宁津县', + 371423: '庆云县', + 371424: '临邑县', + 371425: '齐河县', + 371426: '平原县', + 371427: '夏津县', + 371428: '武城县', + 371481: '乐陵市', + 371482: '禹城市', + 371502: '东昌府区', + 371521: '阳谷县', + 371522: '莘县', + 371523: '茌平县', + 371524: '东阿县', + 371525: '冠县', + 371526: '高唐县', + 371581: '临清市', + 371602: '滨城区', + 371603: '沾化区', + 371621: '惠民县', + 371622: '阳信县', + 371623: '无棣县', + 371625: '博兴县', + 371626: '邹平县', + 371702: '牡丹区', + 371703: '定陶区', + 371721: '曹县', + 371722: '单县', + 371723: '成武县', + 371724: '巨野县', + 371725: '郓城县', + 371726: '鄄城县', + 371728: '东明县', + 410102: '中原区', + 410103: '二七区', + 410104: '管城回族区', + 410105: '金水区', + 410106: '上街区', + 410108: '惠济区', + 410122: '中牟县', + 410181: '巩义市', + 410182: '荥阳市', + 410183: '新密市', + 410184: '新郑市', + 410185: '登封市', + 410190: '高新技术开发区', + 410191: '经济技术开发区', + 410202: '龙亭区', + 410203: '顺河回族区', + 410204: '鼓楼区', + 410205: '禹王台区', + 410212: '祥符区', + 410221: '杞县', + 410222: '通许县', + 410223: '尉氏县', + 410225: '兰考县', + 410302: '老城区', + 410303: '西工区', + 410304: '瀍河回族区', + 410305: '涧西区', + 410306: '吉利区', + 410311: '洛龙区', + 410322: '孟津县', + 410323: '新安县', + 410324: '栾川县', + 410325: '嵩县', + 410326: '汝阳县', + 410327: '宜阳县', + 410328: '洛宁县', + 410329: '伊川县', + 410381: '偃师市', + 410402: '新华区', + 410403: '卫东区', + 410404: '石龙区', + 410411: '湛河区', + 410421: '宝丰县', + 410422: '叶县', + 410423: '鲁山县', + 410425: '郏县', + 410481: '舞钢市', + 410482: '汝州市', + 410502: '文峰区', + 410503: '北关区', + 410505: '殷都区', + 410506: '龙安区', + 410522: '安阳县', + 410523: '汤阴县', + 410526: '滑县', + 410527: '内黄县', + 410581: '林州市', + 410590: '开发区', + 410602: '鹤山区', + 410603: '山城区', + 410611: '淇滨区', + 410621: '浚县', + 410622: '淇县', + 410702: '红旗区', + 410703: '卫滨区', + 410704: '凤泉区', + 410711: '牧野区', + 410721: '新乡县', + 410724: '获嘉县', + 410725: '原阳县', + 410726: '延津县', + 410727: '封丘县', + 410728: '长垣县', + 410781: '卫辉市', + 410782: '辉县市', + 410802: '解放区', + 410803: '中站区', + 410804: '马村区', + 410811: '山阳区', + 410821: '修武县', + 410822: '博爱县', + 410823: '武陟县', + 410825: '温县', + 410882: '沁阳市', + 410883: '孟州市', + 410902: '华龙区', + 410922: '清丰县', + 410923: '南乐县', + 410926: '范县', + 410927: '台前县', + 410928: '濮阳县', + 411002: '魏都区', + 411003: '建安区', + 411024: '鄢陵县', + 411025: '襄城县', + 411081: '禹州市', + 411082: '长葛市', + 411102: '源汇区', + 411103: '郾城区', + 411104: '召陵区', + 411121: '舞阳县', + 411122: '临颍县', + 411202: '湖滨区', + 411203: '陕州区', + 411221: '渑池县', + 411224: '卢氏县', + 411281: '义马市', + 411282: '灵宝市', + 411302: '宛城区', + 411303: '卧龙区', + 411321: '南召县', + 411322: '方城县', + 411323: '西峡县', + 411324: '镇平县', + 411325: '内乡县', + 411326: '淅川县', + 411327: '社旗县', + 411328: '唐河县', + 411329: '新野县', + 411330: '桐柏县', + 411381: '邓州市', + 411402: '梁园区', + 411403: '睢阳区', + 411421: '民权县', + 411422: '睢县', + 411423: '宁陵县', + 411424: '柘城县', + 411425: '虞城县', + 411426: '夏邑县', + 411481: '永城市', + 411502: '浉河区', + 411503: '平桥区', + 411521: '罗山县', + 411522: '光山县', + 411523: '新县', + 411524: '商城县', + 411525: '固始县', + 411526: '潢川县', + 411527: '淮滨县', + 411528: '息县', + 411602: '川汇区', + 411621: '扶沟县', + 411622: '西华县', + 411623: '商水县', + 411624: '沈丘县', + 411625: '郸城县', + 411626: '淮阳县', + 411627: '太康县', + 411628: '鹿邑县', + 411681: '项城市', + 411690: '经济开发区', + 411702: '驿城区', + 411721: '西平县', + 411722: '上蔡县', + 411723: '平舆县', + 411724: '正阳县', + 411725: '确山县', + 411726: '泌阳县', + 411727: '汝南县', + 411728: '遂平县', + 411729: '新蔡县', + 419001: '济源市', + 420102: '江岸区', + 420103: '江汉区', + 420104: '硚口区', + 420105: '汉阳区', + 420106: '武昌区', + 420107: '青山区', + 420111: '洪山区', + 420112: '东西湖区', + 420113: '汉南区', + 420114: '蔡甸区', + 420115: '江夏区', + 420116: '黄陂区', + 420117: '新洲区', + 420202: '黄石港区', + 420203: '西塞山区', + 420204: '下陆区', + 420205: '铁山区', + 420222: '阳新县', + 420281: '大冶市', + 420302: '茅箭区', + 420303: '张湾区', + 420304: '郧阳区', + 420322: '郧西县', + 420323: '竹山县', + 420324: '竹溪县', + 420325: '房县', + 420381: '丹江口市', + 420502: '西陵区', + 420503: '伍家岗区', + 420504: '点军区', + 420505: '猇亭区', + 420506: '夷陵区', + 420525: '远安县', + 420526: '兴山县', + 420527: '秭归县', + 420528: '长阳土家族自治县', + 420529: '五峰土家族自治县', + 420581: '宜都市', + 420582: '当阳市', + 420583: '枝江市', + 420590: '经济开发区', + 420602: '襄城区', + 420606: '樊城区', + 420607: '襄州区', + 420624: '南漳县', + 420625: '谷城县', + 420626: '保康县', + 420682: '老河口市', + 420683: '枣阳市', + 420684: '宜城市', + 420702: '梁子湖区', + 420703: '华容区', + 420704: '鄂城区', + 420802: '东宝区', + 420804: '掇刀区', + 420821: '京山县', + 420822: '沙洋县', + 420881: '钟祥市', + 420902: '孝南区', + 420921: '孝昌县', + 420922: '大悟县', + 420923: '云梦县', + 420981: '应城市', + 420982: '安陆市', + 420984: '汉川市', + 421002: '沙市区', + 421003: '荆州区', + 421022: '公安县', + 421023: '监利县', + 421024: '江陵县', + 421081: '石首市', + 421083: '洪湖市', + 421087: '松滋市', + 421102: '黄州区', + 421121: '团风县', + 421122: '红安县', + 421123: '罗田县', + 421124: '英山县', + 421125: '浠水县', + 421126: '蕲春县', + 421127: '黄梅县', + 421181: '麻城市', + 421182: '武穴市', + 421202: '咸安区', + 421221: '嘉鱼县', + 421222: '通城县', + 421223: '崇阳县', + 421224: '通山县', + 421281: '赤壁市', + 421303: '曾都区', + 421321: '随县', + 421381: '广水市', + 422801: '恩施市', + 422802: '利川市', + 422822: '建始县', + 422823: '巴东县', + 422825: '宣恩县', + 422826: '咸丰县', + 422827: '来凤县', + 422828: '鹤峰县', + 429004: '仙桃市', + 429005: '潜江市', + 429006: '天门市', + 429021: '神农架林区', + 430102: '芙蓉区', + 430103: '天心区', + 430104: '岳麓区', + 430105: '开福区', + 430111: '雨花区', + 430112: '望城区', + 430121: '长沙县', + 430181: '浏阳市', + 430182: '宁乡市', + 430202: '荷塘区', + 430203: '芦淞区', + 430204: '石峰区', + 430211: '天元区', + 430221: '株洲县', + 430223: '攸县', + 430224: '茶陵县', + 430225: '炎陵县', + 430281: '醴陵市', + 430302: '雨湖区', + 430304: '岳塘区', + 430321: '湘潭县', + 430381: '湘乡市', + 430382: '韶山市', + 430405: '珠晖区', + 430406: '雁峰区', + 430407: '石鼓区', + 430408: '蒸湘区', + 430412: '南岳区', + 430421: '衡阳县', + 430422: '衡南县', + 430423: '衡山县', + 430424: '衡东县', + 430426: '祁东县', + 430481: '耒阳市', + 430482: '常宁市', + 430502: '双清区', + 430503: '大祥区', + 430511: '北塔区', + 430521: '邵东县', + 430522: '新邵县', + 430523: '邵阳县', + 430524: '隆回县', + 430525: '洞口县', + 430527: '绥宁县', + 430528: '新宁县', + 430529: '城步苗族自治县', + 430581: '武冈市', + 430602: '岳阳楼区', + 430603: '云溪区', + 430611: '君山区', + 430621: '岳阳县', + 430623: '华容县', + 430624: '湘阴县', + 430626: '平江县', + 430681: '汨罗市', + 430682: '临湘市', + 430702: '武陵区', + 430703: '鼎城区', + 430721: '安乡县', + 430722: '汉寿县', + 430723: '澧县', + 430724: '临澧县', + 430725: '桃源县', + 430726: '石门县', + 430781: '津市市', + 430802: '永定区', + 430811: '武陵源区', + 430821: '慈利县', + 430822: '桑植县', + 430902: '资阳区', + 430903: '赫山区', + 430921: '南县', + 430922: '桃江县', + 430923: '安化县', + 430981: '沅江市', + 431002: '北湖区', + 431003: '苏仙区', + 431021: '桂阳县', + 431022: '宜章县', + 431023: '永兴县', + 431024: '嘉禾县', + 431025: '临武县', + 431026: '汝城县', + 431027: '桂东县', + 431028: '安仁县', + 431081: '资兴市', + 431102: '零陵区', + 431103: '冷水滩区', + 431121: '祁阳县', + 431122: '东安县', + 431123: '双牌县', + 431124: '道县', + 431125: '江永县', + 431126: '宁远县', + 431127: '蓝山县', + 431128: '新田县', + 431129: '江华瑶族自治县', + 431202: '鹤城区', + 431221: '中方县', + 431222: '沅陵县', + 431223: '辰溪县', + 431224: '溆浦县', + 431225: '会同县', + 431226: '麻阳苗族自治县', + 431227: '新晃侗族自治县', + 431228: '芷江侗族自治县', + 431229: '靖州苗族侗族自治县', + 431230: '通道侗族自治县', + 431281: '洪江市', + 431302: '娄星区', + 431321: '双峰县', + 431322: '新化县', + 431381: '冷水江市', + 431382: '涟源市', + 433101: '吉首市', + 433122: '泸溪县', + 433123: '凤凰县', + 433124: '花垣县', + 433125: '保靖县', + 433126: '古丈县', + 433127: '永顺县', + 433130: '龙山县', + 440103: '荔湾区', + 440104: '越秀区', + 440105: '海珠区', + 440106: '天河区', + 440111: '白云区', + 440112: '黄埔区', + 440113: '番禺区', + 440114: '花都区', + 440115: '南沙区', + 440117: '从化区', + 440118: '增城区', + 440203: '武江区', + 440204: '浈江区', + 440205: '曲江区', + 440222: '始兴县', + 440224: '仁化县', + 440229: '翁源县', + 440232: '乳源瑶族自治县', + 440233: '新丰县', + 440281: '乐昌市', + 440282: '南雄市', + 440303: '罗湖区', + 440304: '福田区', + 440305: '南山区', + 440306: '宝安区', + 440307: '龙岗区', + 440308: '盐田区', + 440309: '龙华区', + 440310: '坪山区', + 440402: '香洲区', + 440403: '斗门区', + 440404: '金湾区', + 440507: '龙湖区', + 440511: '金平区', + 440512: '濠江区', + 440513: '潮阳区', + 440514: '潮南区', + 440515: '澄海区', + 440523: '南澳县', + 440604: '禅城区', + 440605: '南海区', + 440606: '顺德区', + 440607: '三水区', + 440608: '高明区', + 440703: '蓬江区', + 440704: '江海区', + 440705: '新会区', + 440781: '台山市', + 440783: '开平市', + 440784: '鹤山市', + 440785: '恩平市', + 440802: '赤坎区', + 440803: '霞山区', + 440804: '坡头区', + 440811: '麻章区', + 440823: '遂溪县', + 440825: '徐闻县', + 440881: '廉江市', + 440882: '雷州市', + 440883: '吴川市', + 440890: '经济技术开发区', + 440902: '茂南区', + 440904: '电白区', + 440981: '高州市', + 440982: '化州市', + 440983: '信宜市', + 441202: '端州区', + 441203: '鼎湖区', + 441204: '高要区', + 441223: '广宁县', + 441224: '怀集县', + 441225: '封开县', + 441226: '德庆县', + 441284: '四会市', + 441302: '惠城区', + 441303: '惠阳区', + 441322: '博罗县', + 441323: '惠东县', + 441324: '龙门县', + 441402: '梅江区', + 441403: '梅县区', + 441422: '大埔县', + 441423: '丰顺县', + 441424: '五华县', + 441426: '平远县', + 441427: '蕉岭县', + 441481: '兴宁市', + 441502: '城区', + 441521: '海丰县', + 441523: '陆河县', + 441581: '陆丰市', + 441602: '源城区', + 441621: '紫金县', + 441622: '龙川县', + 441623: '连平县', + 441624: '和平县', + 441625: '东源县', + 441702: '江城区', + 441704: '阳东区', + 441721: '阳西县', + 441781: '阳春市', + 441802: '清城区', + 441803: '清新区', + 441821: '佛冈县', + 441823: '阳山县', + 441825: '连山壮族瑶族自治县', + 441826: '连南瑶族自治县', + 441881: '英德市', + 441882: '连州市', + 441901: '中堂镇', + 441903: '南城区', + 441904: '长安镇', + 441905: '东坑镇', + 441906: '樟木头镇', + 441907: '莞城区', + 441908: '石龙镇', + 441909: '桥头镇', + 441910: '万江区', + 441911: '麻涌镇', + 441912: '虎门镇', + 441913: '谢岗镇', + 441914: '石碣镇', + 441915: '茶山镇', + 441916: '东城区', + 441917: '洪梅镇', + 441918: '道滘镇', + 441919: '高埗镇', + 441920: '企石镇', + 441921: '凤岗镇', + 441922: '大岭山镇', + 441923: '松山湖', + 441924: '清溪镇', + 441925: '望牛墩镇', + 441926: '厚街镇', + 441927: '常平镇', + 441928: '寮步镇', + 441929: '石排镇', + 441930: '横沥镇', + 441931: '塘厦镇', + 441932: '黄江镇', + 441933: '大朗镇', + 441990: '沙田镇', + 442001: '南头镇', + 442002: '神湾镇', + 442003: '东凤镇', + 442004: '五桂山镇', + 442005: '黄圃镇', + 442006: '小榄镇', + 442007: '石岐区街道', + 442008: '横栏镇', + 442009: '三角镇', + 442010: '三乡镇', + 442011: '港口镇', + 442012: '沙溪镇', + 442013: '板芙镇', + 442014: '沙朗镇', + 442015: '东升镇', + 442016: '阜沙镇', + 442017: '民众镇', + 442018: '东区街道', + 442019: '火炬开发区', + 442020: '西区街道', + 442021: '南区街道', + 442022: '古镇', + 442023: '坦洲镇', + 442024: '大涌镇', + 442025: '南朗镇', + 445102: '湘桥区', + 445103: '潮安区', + 445122: '饶平县', + 445202: '榕城区', + 445203: '揭东区', + 445222: '揭西县', + 445224: '惠来县', + 445281: '普宁市', + 445302: '云城区', + 445303: '云安区', + 445321: '新兴县', + 445322: '郁南县', + 445381: '罗定市', + 450102: '兴宁区', + 450103: '青秀区', + 450105: '江南区', + 450107: '西乡塘区', + 450108: '良庆区', + 450109: '邕宁区', + 450110: '武鸣区', + 450123: '隆安县', + 450124: '马山县', + 450125: '上林县', + 450126: '宾阳县', + 450127: '横县', + 450202: '城中区', + 450203: '鱼峰区', + 450204: '柳南区', + 450205: '柳北区', + 450206: '柳江区', + 450222: '柳城县', + 450223: '鹿寨县', + 450224: '融安县', + 450225: '融水苗族自治县', + 450226: '三江侗族自治县', + 450302: '秀峰区', + 450303: '叠彩区', + 450304: '象山区', + 450305: '七星区', + 450311: '雁山区', + 450312: '临桂区', + 450321: '阳朔县', + 450323: '灵川县', + 450324: '全州县', + 450325: '兴安县', + 450326: '永福县', + 450327: '灌阳县', + 450328: '龙胜各族自治县', + 450329: '资源县', + 450330: '平乐县', + 450331: '荔浦县', + 450332: '恭城瑶族自治县', + 450403: '万秀区', + 450405: '长洲区', + 450406: '龙圩区', + 450421: '苍梧县', + 450422: '藤县', + 450423: '蒙山县', + 450481: '岑溪市', + 450502: '海城区', + 450503: '银海区', + 450512: '铁山港区', + 450521: '合浦县', + 450602: '港口区', + 450603: '防城区', + 450621: '上思县', + 450681: '东兴市', + 450702: '钦南区', + 450703: '钦北区', + 450721: '灵山县', + 450722: '浦北县', + 450802: '港北区', + 450803: '港南区', + 450804: '覃塘区', + 450821: '平南县', + 450881: '桂平市', + 450902: '玉州区', + 450903: '福绵区', + 450921: '容县', + 450922: '陆川县', + 450923: '博白县', + 450924: '兴业县', + 450981: '北流市', + 451002: '右江区', + 451021: '田阳县', + 451022: '田东县', + 451023: '平果县', + 451024: '德保县', + 451026: '那坡县', + 451027: '凌云县', + 451028: '乐业县', + 451029: '田林县', + 451030: '西林县', + 451031: '隆林各族自治县', + 451081: '靖西市', + 451102: '八步区', + 451103: '平桂区', + 451121: '昭平县', + 451122: '钟山县', + 451123: '富川瑶族自治县', + 451202: '金城江区', + 451203: '宜州区', + 451221: '南丹县', + 451222: '天峨县', + 451223: '凤山县', + 451224: '东兰县', + 451225: '罗城仫佬族自治县', + 451226: '环江毛南族自治县', + 451227: '巴马瑶族自治县', + 451228: '都安瑶族自治县', + 451229: '大化瑶族自治县', + 451302: '兴宾区', + 451321: '忻城县', + 451322: '象州县', + 451323: '武宣县', + 451324: '金秀瑶族自治县', + 451381: '合山市', + 451402: '江州区', + 451421: '扶绥县', + 451422: '宁明县', + 451423: '龙州县', + 451424: '大新县', + 451425: '天等县', + 451481: '凭祥市', + 460105: '秀英区', + 460106: '龙华区', + 460107: '琼山区', + 460108: '美兰区', + 460202: '海棠区', + 460203: '吉阳区', + 460204: '天涯区', + 460205: '崖州区', + 460321: '西沙群岛', + 460322: '南沙群岛', + 460323: '中沙群岛的岛礁及其海域', + 460401: '那大镇', + 460402: '和庆镇', + 460403: '南丰镇', + 460404: '大成镇', + 460405: '雅星镇', + 460406: '兰洋镇', + 460407: '光村镇', + 460408: '木棠镇', + 460409: '海头镇', + 460410: '峨蔓镇', + 460411: '王五镇', + 460412: '白马井镇', + 460413: '中和镇', + 460414: '排浦镇', + 460415: '东成镇', + 460416: '新州镇', + 469001: '五指山市', + 469002: '琼海市', + 469005: '文昌市', + 469006: '万宁市', + 469007: '东方市', + 469021: '定安县', + 469022: '屯昌县', + 469023: '澄迈县', + 469024: '临高县', + 469025: '白沙黎族自治县', + 469026: '昌江黎族自治县', + 469027: '乐东黎族自治县', + 469028: '陵水黎族自治县', + 469029: '保亭黎族苗族自治县', + 469030: '琼中黎族苗族自治县', + 500101: '万州区', + 500102: '涪陵区', + 500103: '渝中区', + 500104: '大渡口区', + 500105: '江北区', + 500106: '沙坪坝区', + 500107: '九龙坡区', + 500108: '南岸区', + 500109: '北碚区', + 500110: '綦江区', + 500111: '大足区', + 500112: '渝北区', + 500113: '巴南区', + 500114: '黔江区', + 500115: '长寿区', + 500116: '江津区', + 500117: '合川区', + 500118: '永川区', + 500119: '南川区', + 500120: '璧山区', + 500151: '铜梁区', + 500152: '潼南区', + 500153: '荣昌区', + 500154: '开州区', + 500155: '梁平区', + 500156: '武隆区', + 500229: '城口县', + 500230: '丰都县', + 500231: '垫江县', + 500233: '忠县', + 500235: '云阳县', + 500236: '奉节县', + 500237: '巫山县', + 500238: '巫溪县', + 500240: '石柱土家族自治县', + 500241: '秀山土家族苗族自治县', + 500242: '酉阳土家族苗族自治县', + 500243: '彭水苗族土家族自治县', + 510104: '锦江区', + 510105: '青羊区', + 510106: '金牛区', + 510107: '武侯区', + 510108: '成华区', + 510112: '龙泉驿区', + 510113: '青白江区', + 510114: '新都区', + 510115: '温江区', + 510116: '双流区', + 510117: '郫都区', + 510121: '金堂县', + 510129: '大邑县', + 510131: '蒲江县', + 510132: '新津县', + 510181: '都江堰市', + 510182: '彭州市', + 510183: '邛崃市', + 510184: '崇州市', + 510185: '简阳市', + 510191: '高新区', + 510302: '自流井区', + 510303: '贡井区', + 510304: '大安区', + 510311: '沿滩区', + 510321: '荣县', + 510322: '富顺县', + 510402: '东区', + 510403: '西区', + 510411: '仁和区', + 510421: '米易县', + 510422: '盐边县', + 510502: '江阳区', + 510503: '纳溪区', + 510504: '龙马潭区', + 510521: '泸县', + 510522: '合江县', + 510524: '叙永县', + 510525: '古蔺县', + 510603: '旌阳区', + 510604: '罗江区', + 510623: '中江县', + 510681: '广汉市', + 510682: '什邡市', + 510683: '绵竹市', + 510703: '涪城区', + 510704: '游仙区', + 510705: '安州区', + 510722: '三台县', + 510723: '盐亭县', + 510725: '梓潼县', + 510726: '北川羌族自治县', + 510727: '平武县', + 510781: '江油市', + 510791: '高新区', + 510802: '利州区', + 510811: '昭化区', + 510812: '朝天区', + 510821: '旺苍县', + 510822: '青川县', + 510823: '剑阁县', + 510824: '苍溪县', + 510903: '船山区', + 510904: '安居区', + 510921: '蓬溪县', + 510922: '射洪县', + 510923: '大英县', + 511002: '市中区', + 511011: '东兴区', + 511024: '威远县', + 511025: '资中县', + 511083: '隆昌市', + 511102: '市中区', + 511111: '沙湾区', + 511112: '五通桥区', + 511113: '金口河区', + 511123: '犍为县', + 511124: '井研县', + 511126: '夹江县', + 511129: '沐川县', + 511132: '峨边彝族自治县', + 511133: '马边彝族自治县', + 511181: '峨眉山市', + 511302: '顺庆区', + 511303: '高坪区', + 511304: '嘉陵区', + 511321: '南部县', + 511322: '营山县', + 511323: '蓬安县', + 511324: '仪陇县', + 511325: '西充县', + 511381: '阆中市', + 511402: '东坡区', + 511403: '彭山区', + 511421: '仁寿县', + 511423: '洪雅县', + 511424: '丹棱县', + 511425: '青神县', + 511502: '翠屏区', + 511503: '南溪区', + 511521: '宜宾县', + 511523: '江安县', + 511524: '长宁县', + 511525: '高县', + 511526: '珙县', + 511527: '筠连县', + 511528: '兴文县', + 511529: '屏山县', + 511602: '广安区', + 511603: '前锋区', + 511621: '岳池县', + 511622: '武胜县', + 511623: '邻水县', + 511681: '华蓥市', + 511702: '通川区', + 511703: '达川区', + 511722: '宣汉县', + 511723: '开江县', + 511724: '大竹县', + 511725: '渠县', + 511781: '万源市', + 511802: '雨城区', + 511803: '名山区', + 511822: '荥经县', + 511823: '汉源县', + 511824: '石棉县', + 511825: '天全县', + 511826: '芦山县', + 511827: '宝兴县', + 511902: '巴州区', + 511903: '恩阳区', + 511921: '通江县', + 511922: '南江县', + 511923: '平昌县', + 512002: '雁江区', + 512021: '安岳县', + 512022: '乐至县', + 513201: '马尔康市', + 513221: '汶川县', + 513222: '理县', + 513223: '茂县', + 513224: '松潘县', + 513225: '九寨沟县', + 513226: '金川县', + 513227: '小金县', + 513228: '黑水县', + 513230: '壤塘县', + 513231: '阿坝县', + 513232: '若尔盖县', + 513233: '红原县', + 513301: '康定市', + 513322: '泸定县', + 513323: '丹巴县', + 513324: '九龙县', + 513325: '雅江县', + 513326: '道孚县', + 513327: '炉霍县', + 513328: '甘孜县', + 513329: '新龙县', + 513330: '德格县', + 513331: '白玉县', + 513332: '石渠县', + 513333: '色达县', + 513334: '理塘县', + 513335: '巴塘县', + 513336: '乡城县', + 513337: '稻城县', + 513338: '得荣县', + 513401: '西昌市', + 513422: '木里藏族自治县', + 513423: '盐源县', + 513424: '德昌县', + 513425: '会理县', + 513426: '会东县', + 513427: '宁南县', + 513428: '普格县', + 513429: '布拖县', + 513430: '金阳县', + 513431: '昭觉县', + 513432: '喜德县', + 513433: '冕宁县', + 513434: '越西县', + 513435: '甘洛县', + 513436: '美姑县', + 513437: '雷波县', + 520102: '南明区', + 520103: '云岩区', + 520111: '花溪区', + 520112: '乌当区', + 520113: '白云区', + 520115: '观山湖区', + 520121: '开阳县', + 520122: '息烽县', + 520123: '修文县', + 520181: '清镇市', + 520201: '钟山区', + 520203: '六枝特区', + 520221: '水城县', + 520281: '盘州市', + 520302: '红花岗区', + 520303: '汇川区', + 520304: '播州区', + 520322: '桐梓县', + 520323: '绥阳县', + 520324: '正安县', + 520325: '道真仡佬族苗族自治县', + 520326: '务川仡佬族苗族自治县', + 520327: '凤冈县', + 520328: '湄潭县', + 520329: '余庆县', + 520330: '习水县', + 520381: '赤水市', + 520382: '仁怀市', + 520402: '西秀区', + 520403: '平坝区', + 520422: '普定县', + 520423: '镇宁布依族苗族自治县', + 520424: '关岭布依族苗族自治县', + 520425: '紫云苗族布依族自治县', + 520502: '七星关区', + 520521: '大方县', + 520522: '黔西县', + 520523: '金沙县', + 520524: '织金县', + 520525: '纳雍县', + 520526: '威宁彝族回族苗族自治县', + 520527: '赫章县', + 520602: '碧江区', + 520603: '万山区', + 520621: '江口县', + 520622: '玉屏侗族自治县', + 520623: '石阡县', + 520624: '思南县', + 520625: '印江土家族苗族自治县', + 520626: '德江县', + 520627: '沿河土家族自治县', + 520628: '松桃苗族自治县', + 522301: '兴义市', + 522322: '兴仁县', + 522323: '普安县', + 522324: '晴隆县', + 522325: '贞丰县', + 522326: '望谟县', + 522327: '册亨县', + 522328: '安龙县', + 522601: '凯里市', + 522622: '黄平县', + 522623: '施秉县', + 522624: '三穗县', + 522625: '镇远县', + 522626: '岑巩县', + 522627: '天柱县', + 522628: '锦屏县', + 522629: '剑河县', + 522630: '台江县', + 522631: '黎平县', + 522632: '榕江县', + 522633: '从江县', + 522634: '雷山县', + 522635: '麻江县', + 522636: '丹寨县', + 522701: '都匀市', + 522702: '福泉市', + 522722: '荔波县', + 522723: '贵定县', + 522725: '瓮安县', + 522726: '独山县', + 522727: '平塘县', + 522728: '罗甸县', + 522729: '长顺县', + 522730: '龙里县', + 522731: '惠水县', + 522732: '三都水族自治县', + 530102: '五华区', + 530103: '盘龙区', + 530111: '官渡区', + 530112: '西山区', + 530113: '东川区', + 530114: '呈贡区', + 530115: '晋宁区', + 530124: '富民县', + 530125: '宜良县', + 530126: '石林彝族自治县', + 530127: '嵩明县', + 530128: '禄劝彝族苗族自治县', + 530129: '寻甸回族彝族自治县', + 530181: '安宁市', + 530302: '麒麟区', + 530303: '沾益区', + 530321: '马龙县', + 530322: '陆良县', + 530323: '师宗县', + 530324: '罗平县', + 530325: '富源县', + 530326: '会泽县', + 530381: '宣威市', + 530402: '红塔区', + 530403: '江川区', + 530422: '澄江县', + 530423: '通海县', + 530424: '华宁县', + 530425: '易门县', + 530426: '峨山彝族自治县', + 530427: '新平彝族傣族自治县', + 530428: '元江哈尼族彝族傣族自治县', + 530502: '隆阳区', + 530521: '施甸县', + 530523: '龙陵县', + 530524: '昌宁县', + 530581: '腾冲市', + 530602: '昭阳区', + 530621: '鲁甸县', + 530622: '巧家县', + 530623: '盐津县', + 530624: '大关县', + 530625: '永善县', + 530626: '绥江县', + 530627: '镇雄县', + 530628: '彝良县', + 530629: '威信县', + 530630: '水富县', + 530702: '古城区', + 530721: '玉龙纳西族自治县', + 530722: '永胜县', + 530723: '华坪县', + 530724: '宁蒗彝族自治县', + 530802: '思茅区', + 530821: '宁洱哈尼族彝族自治县', + 530822: '墨江哈尼族自治县', + 530823: '景东彝族自治县', + 530824: '景谷傣族彝族自治县', + 530825: '镇沅彝族哈尼族拉祜族自治县', + 530826: '江城哈尼族彝族自治县', + 530827: '孟连傣族拉祜族佤族自治县', + 530828: '澜沧拉祜族自治县', + 530829: '西盟佤族自治县', + 530902: '临翔区', + 530921: '凤庆县', + 530922: '云县', + 530923: '永德县', + 530924: '镇康县', + 530925: '双江拉祜族佤族布朗族傣族自治县', + 530926: '耿马傣族佤族自治县', + 530927: '沧源佤族自治县', + 532301: '楚雄市', + 532322: '双柏县', + 532323: '牟定县', + 532324: '南华县', + 532325: '姚安县', + 532326: '大姚县', + 532327: '永仁县', + 532328: '元谋县', + 532329: '武定县', + 532331: '禄丰县', + 532501: '个旧市', + 532502: '开远市', + 532503: '蒙自市', + 532504: '弥勒市', + 532523: '屏边苗族自治县', + 532524: '建水县', + 532525: '石屏县', + 532527: '泸西县', + 532528: '元阳县', + 532529: '红河县', + 532530: '金平苗族瑶族傣族自治县', + 532531: '绿春县', + 532532: '河口瑶族自治县', + 532601: '文山市', + 532622: '砚山县', + 532623: '西畴县', + 532624: '麻栗坡县', + 532625: '马关县', + 532626: '丘北县', + 532627: '广南县', + 532628: '富宁县', + 532801: '景洪市', + 532822: '勐海县', + 532823: '勐腊县', + 532901: '大理市', + 532922: '漾濞彝族自治县', + 532923: '祥云县', + 532924: '宾川县', + 532925: '弥渡县', + 532926: '南涧彝族自治县', + 532927: '巍山彝族回族自治县', + 532928: '永平县', + 532929: '云龙县', + 532930: '洱源县', + 532931: '剑川县', + 532932: '鹤庆县', + 533102: '瑞丽市', + 533103: '芒市', + 533122: '梁河县', + 533123: '盈江县', + 533124: '陇川县', + 533301: '泸水市', + 533323: '福贡县', + 533324: '贡山独龙族怒族自治县', + 533325: '兰坪白族普米族自治县', + 533401: '香格里拉市', + 533422: '德钦县', + 533423: '维西傈僳族自治县', + 540102: '城关区', + 540103: '堆龙德庆区', + 540104: '达孜区', + 540121: '林周县', + 540122: '当雄县', + 540123: '尼木县', + 540124: '曲水县', + 540127: '墨竹工卡县', + 540202: '桑珠孜区', + 540221: '南木林县', + 540222: '江孜县', + 540223: '定日县', + 540224: '萨迦县', + 540225: '拉孜县', + 540226: '昂仁县', + 540227: '谢通门县', + 540228: '白朗县', + 540229: '仁布县', + 540230: '康马县', + 540231: '定结县', + 540232: '仲巴县', + 540233: '亚东县', + 540234: '吉隆县', + 540235: '聂拉木县', + 540236: '萨嘎县', + 540237: '岗巴县', + 540302: '卡若区', + 540321: '江达县', + 540322: '贡觉县', + 540323: '类乌齐县', + 540324: '丁青县', + 540325: '察雅县', + 540326: '八宿县', + 540327: '左贡县', + 540328: '芒康县', + 540329: '洛隆县', + 540330: '边坝县', + 540402: '巴宜区', + 540421: '工布江达县', + 540422: '米林县', + 540423: '墨脱县', + 540424: '波密县', + 540425: '察隅县', + 540426: '朗县', + 540502: '乃东区', + 540521: '扎囊县', + 540522: '贡嘎县', + 540523: '桑日县', + 540524: '琼结县', + 540525: '曲松县', + 540526: '措美县', + 540527: '洛扎县', + 540528: '加查县', + 540529: '隆子县', + 540530: '错那县', + 540531: '浪卡子县', + 540602: '色尼区', + 542421: '那曲县', + 542422: '嘉黎县', + 542423: '比如县', + 542424: '聂荣县', + 542425: '安多县', + 542426: '申扎县', + 542427: '索县', + 542428: '班戈县', + 542429: '巴青县', + 542430: '尼玛县', + 542431: '双湖县', + 542521: '普兰县', + 542522: '札达县', + 542523: '噶尔县', + 542524: '日土县', + 542525: '革吉县', + 542526: '改则县', + 542527: '措勤县', + 610102: '新城区', + 610103: '碑林区', + 610104: '莲湖区', + 610111: '灞桥区', + 610112: '未央区', + 610113: '雁塔区', + 610114: '阎良区', + 610115: '临潼区', + 610116: '长安区', + 610117: '高陵区', + 610118: '鄠邑区', + 610122: '蓝田县', + 610124: '周至县', + 610202: '王益区', + 610203: '印台区', + 610204: '耀州区', + 610222: '宜君县', + 610302: '渭滨区', + 610303: '金台区', + 610304: '陈仓区', + 610322: '凤翔县', + 610323: '岐山县', + 610324: '扶风县', + 610326: '眉县', + 610327: '陇县', + 610328: '千阳县', + 610329: '麟游县', + 610330: '凤县', + 610331: '太白县', + 610402: '秦都区', + 610403: '杨陵区', + 610404: '渭城区', + 610422: '三原县', + 610423: '泾阳县', + 610424: '乾县', + 610425: '礼泉县', + 610426: '永寿县', + 610427: '彬县', + 610428: '长武县', + 610429: '旬邑县', + 610430: '淳化县', + 610431: '武功县', + 610481: '兴平市', + 610502: '临渭区', + 610503: '华州区', + 610522: '潼关县', + 610523: '大荔县', + 610524: '合阳县', + 610525: '澄城县', + 610526: '蒲城县', + 610527: '白水县', + 610528: '富平县', + 610581: '韩城市', + 610582: '华阴市', + 610602: '宝塔区', + 610603: '安塞区', + 610621: '延长县', + 610622: '延川县', + 610623: '子长县', + 610625: '志丹县', + 610626: '吴起县', + 610627: '甘泉县', + 610628: '富县', + 610629: '洛川县', + 610630: '宜川县', + 610631: '黄龙县', + 610632: '黄陵县', + 610702: '汉台区', + 610703: '南郑区', + 610722: '城固县', + 610723: '洋县', + 610724: '西乡县', + 610725: '勉县', + 610726: '宁强县', + 610727: '略阳县', + 610728: '镇巴县', + 610729: '留坝县', + 610730: '佛坪县', + 610802: '榆阳区', + 610803: '横山区', + 610822: '府谷县', + 610824: '靖边县', + 610825: '定边县', + 610826: '绥德县', + 610827: '米脂县', + 610828: '佳县', + 610829: '吴堡县', + 610830: '清涧县', + 610831: '子洲县', + 610881: '神木市', + 610902: '汉滨区', + 610921: '汉阴县', + 610922: '石泉县', + 610923: '宁陕县', + 610924: '紫阳县', + 610925: '岚皋县', + 610926: '平利县', + 610927: '镇坪县', + 610928: '旬阳县', + 610929: '白河县', + 611002: '商州区', + 611021: '洛南县', + 611022: '丹凤县', + 611023: '商南县', + 611024: '山阳县', + 611025: '镇安县', + 611026: '柞水县', + 620102: '城关区', + 620103: '七里河区', + 620104: '西固区', + 620105: '安宁区', + 620111: '红古区', + 620121: '永登县', + 620122: '皋兰县', + 620123: '榆中县', + 620201: '市辖区', + 620290: '雄关区', + 620291: '长城区', + 620292: '镜铁区', + 620293: '新城镇', + 620294: '峪泉镇', + 620295: '文殊镇', + 620302: '金川区', + 620321: '永昌县', + 620402: '白银区', + 620403: '平川区', + 620421: '靖远县', + 620422: '会宁县', + 620423: '景泰县', + 620502: '秦州区', + 620503: '麦积区', + 620521: '清水县', + 620522: '秦安县', + 620523: '甘谷县', + 620524: '武山县', + 620525: '张家川回族自治县', + 620602: '凉州区', + 620621: '民勤县', + 620622: '古浪县', + 620623: '天祝藏族自治县', + 620702: '甘州区', + 620721: '肃南裕固族自治县', + 620722: '民乐县', + 620723: '临泽县', + 620724: '高台县', + 620725: '山丹县', + 620802: '崆峒区', + 620821: '泾川县', + 620822: '灵台县', + 620823: '崇信县', + 620824: '华亭县', + 620825: '庄浪县', + 620826: '静宁县', + 620902: '肃州区', + 620921: '金塔县', + 620922: '瓜州县', + 620923: '肃北蒙古族自治县', + 620924: '阿克塞哈萨克族自治县', + 620981: '玉门市', + 620982: '敦煌市', + 621002: '西峰区', + 621021: '庆城县', + 621022: '环县', + 621023: '华池县', + 621024: '合水县', + 621025: '正宁县', + 621026: '宁县', + 621027: '镇原县', + 621102: '安定区', + 621121: '通渭县', + 621122: '陇西县', + 621123: '渭源县', + 621124: '临洮县', + 621125: '漳县', + 621126: '岷县', + 621202: '武都区', + 621221: '成县', + 621222: '文县', + 621223: '宕昌县', + 621224: '康县', + 621225: '西和县', + 621226: '礼县', + 621227: '徽县', + 621228: '两当县', + 622901: '临夏市', + 622921: '临夏县', + 622922: '康乐县', + 622923: '永靖县', + 622924: '广河县', + 622925: '和政县', + 622926: '东乡族自治县', + 622927: '积石山保安族东乡族撒拉族自治县', + 623001: '合作市', + 623021: '临潭县', + 623022: '卓尼县', + 623023: '舟曲县', + 623024: '迭部县', + 623025: '玛曲县', + 623026: '碌曲县', + 623027: '夏河县', + 630102: '城东区', + 630103: '城中区', + 630104: '城西区', + 630105: '城北区', + 630121: '大通回族土族自治县', + 630122: '湟中县', + 630123: '湟源县', + 630202: '乐都区', + 630203: '平安区', + 630222: '民和回族土族自治县', + 630223: '互助土族自治县', + 630224: '化隆回族自治县', + 630225: '循化撒拉族自治县', + 632221: '门源回族自治县', + 632222: '祁连县', + 632223: '海晏县', + 632224: '刚察县', + 632321: '同仁县', + 632322: '尖扎县', + 632323: '泽库县', + 632324: '河南蒙古族自治县', + 632521: '共和县', + 632522: '同德县', + 632523: '贵德县', + 632524: '兴海县', + 632525: '贵南县', + 632621: '玛沁县', + 632622: '班玛县', + 632623: '甘德县', + 632624: '达日县', + 632625: '久治县', + 632626: '玛多县', + 632701: '玉树市', + 632722: '杂多县', + 632723: '称多县', + 632724: '治多县', + 632725: '囊谦县', + 632726: '曲麻莱县', + 632801: '格尔木市', + 632802: '德令哈市', + 632821: '乌兰县', + 632822: '都兰县', + 632823: '天峻县', + 640104: '兴庆区', + 640105: '西夏区', + 640106: '金凤区', + 640121: '永宁县', + 640122: '贺兰县', + 640181: '灵武市', + 640202: '大武口区', + 640205: '惠农区', + 640221: '平罗县', + 640302: '利通区', + 640303: '红寺堡区', + 640323: '盐池县', + 640324: '同心县', + 640381: '青铜峡市', + 640402: '原州区', + 640422: '西吉县', + 640423: '隆德县', + 640424: '泾源县', + 640425: '彭阳县', + 640502: '沙坡头区', + 640521: '中宁县', + 640522: '海原县', + 650102: '天山区', + 650103: '沙依巴克区', + 650104: '新市区', + 650105: '水磨沟区', + 650106: '头屯河区', + 650107: '达坂城区', + 650109: '米东区', + 650121: '乌鲁木齐县', + 650202: '独山子区', + 650203: '克拉玛依区', + 650204: '白碱滩区', + 650205: '乌尔禾区', + 650402: '高昌区', + 650421: '鄯善县', + 650422: '托克逊县', + 650502: '伊州区', + 650521: '巴里坤哈萨克自治县', + 650522: '伊吾县', + 652301: '昌吉市', + 652302: '阜康市', + 652323: '呼图壁县', + 652324: '玛纳斯县', + 652325: '奇台县', + 652327: '吉木萨尔县', + 652328: '木垒哈萨克自治县', + 652701: '博乐市', + 652702: '阿拉山口市', + 652722: '精河县', + 652723: '温泉县', + 652801: '库尔勒市', + 652822: '轮台县', + 652823: '尉犁县', + 652824: '若羌县', + 652825: '且末县', + 652826: '焉耆回族自治县', + 652827: '和静县', + 652828: '和硕县', + 652829: '博湖县', + 652901: '阿克苏市', + 652922: '温宿县', + 652923: '库车县', + 652924: '沙雅县', + 652925: '新和县', + 652926: '拜城县', + 652927: '乌什县', + 652928: '阿瓦提县', + 652929: '柯坪县', + 653001: '阿图什市', + 653022: '阿克陶县', + 653023: '阿合奇县', + 653024: '乌恰县', + 653101: '喀什市', + 653121: '疏附县', + 653122: '疏勒县', + 653123: '英吉沙县', + 653124: '泽普县', + 653125: '莎车县', + 653126: '叶城县', + 653127: '麦盖提县', + 653128: '岳普湖县', + 653129: '伽师县', + 653130: '巴楚县', + 653131: '塔什库尔干塔吉克自治县', + 653201: '和田市', + 653221: '和田县', + 653222: '墨玉县', + 653223: '皮山县', + 653224: '洛浦县', + 653225: '策勒县', + 653226: '于田县', + 653227: '民丰县', + 654002: '伊宁市', + 654003: '奎屯市', + 654004: '霍尔果斯市', + 654021: '伊宁县', + 654022: '察布查尔锡伯自治县', + 654023: '霍城县', + 654024: '巩留县', + 654025: '新源县', + 654026: '昭苏县', + 654027: '特克斯县', + 654028: '尼勒克县', + 654201: '塔城市', + 654202: '乌苏市', + 654221: '额敏县', + 654223: '沙湾县', + 654224: '托里县', + 654225: '裕民县', + 654226: '和布克赛尔蒙古自治县', + 654301: '阿勒泰市', + 654321: '布尔津县', + 654322: '富蕴县', + 654323: '福海县', + 654324: '哈巴河县', + 654325: '青河县', + 654326: '吉木乃县', + 659001: '石河子市', + 659002: '阿拉尔市', + 659003: '图木舒克市', + 659004: '五家渠市', + 659005: '北屯市', + 659006: '铁门关市', + 659007: '双河市', + 659008: '可克达拉市', + 659009: '昆玉市', + 710101: '中正区', + 710102: '大同区', + 710103: '中山区', + 710104: '松山区', + 710105: '大安区', + 710106: '万华区', + 710107: '信义区', + 710108: '士林区', + 710109: '北投区', + 710110: '内湖区', + 710111: '南港区', + 710112: '文山区', + 710199: '其它区', + 710201: '新兴区', + 710202: '前金区', + 710203: '芩雅区', + 710204: '盐埕区', + 710205: '鼓山区', + 710206: '旗津区', + 710207: '前镇区', + 710208: '三民区', + 710209: '左营区', + 710210: '楠梓区', + 710211: '小港区', + 710241: '苓雅区', + 710242: '仁武区', + 710243: '大社区', + 710244: '冈山区', + 710245: '路竹区', + 710246: '阿莲区', + 710247: '田寮区', + 710248: '燕巢区', + 710249: '桥头区', + 710250: '梓官区', + 710251: '弥陀区', + 710252: '永安区', + 710253: '湖内区', + 710254: '凤山区', + 710255: '大寮区', + 710256: '林园区', + 710257: '鸟松区', + 710258: '大树区', + 710259: '旗山区', + 710260: '美浓区', + 710261: '六龟区', + 710262: '内门区', + 710263: '杉林区', + 710264: '甲仙区', + 710265: '桃源区', + 710266: '那玛夏区', + 710267: '茂林区', + 710268: '茄萣区', + 710299: '其它区', + 710301: '中西区', + 710302: '东区', + 710303: '南区', + 710304: '北区', + 710305: '安平区', + 710306: '安南区', + 710339: '永康区', + 710340: '归仁区', + 710341: '新化区', + 710342: '左镇区', + 710343: '玉井区', + 710344: '楠西区', + 710345: '南化区', + 710346: '仁德区', + 710347: '关庙区', + 710348: '龙崎区', + 710349: '官田区', + 710350: '麻豆区', + 710351: '佳里区', + 710352: '西港区', + 710353: '七股区', + 710354: '将军区', + 710355: '学甲区', + 710356: '北门区', + 710357: '新营区', + 710358: '后壁区', + 710359: '白河区', + 710360: '东山区', + 710361: '六甲区', + 710362: '下营区', + 710363: '柳营区', + 710364: '盐水区', + 710365: '善化区', + 710366: '大内区', + 710367: '山上区', + 710368: '新市区', + 710369: '安定区', + 710399: '其它区', + 710401: '中区', + 710402: '东区', + 710403: '南区', + 710404: '西区', + 710405: '北区', + 710406: '北屯区', + 710407: '西屯区', + 710408: '南屯区', + 710431: '太平区', + 710432: '大里区', + 710433: '雾峰区', + 710434: '乌日区', + 710435: '丰原区', + 710436: '后里区', + 710437: '石冈区', + 710438: '东势区', + 710439: '和平区', + 710440: '新社区', + 710441: '潭子区', + 710442: '大雅区', + 710443: '神冈区', + 710444: '大肚区', + 710445: '沙鹿区', + 710446: '龙井区', + 710447: '梧栖区', + 710448: '清水区', + 710449: '大甲区', + 710450: '外埔区', + 710451: '大安区', + 710499: '其它区', + 710507: '金沙镇', + 710508: '金湖镇', + 710509: '金宁乡', + 710510: '金城镇', + 710511: '烈屿乡', + 710512: '乌坵乡', + 710614: '南投市', + 710615: '中寮乡', + 710616: '草屯镇', + 710617: '国姓乡', + 710618: '埔里镇', + 710619: '仁爱乡', + 710620: '名间乡', + 710621: '集集镇', + 710622: '水里乡', + 710623: '鱼池乡', + 710624: '信义乡', + 710625: '竹山镇', + 710626: '鹿谷乡', + 710701: '仁爱区', + 710702: '信义区', + 710703: '中正区', + 710704: '中山区', + 710705: '安乐区', + 710706: '暖暖区', + 710707: '七堵区', + 710799: '其它区', + 710801: '东区', + 710802: '北区', + 710803: '香山区', + 710899: '其它区', + 710901: '东区', + 710902: '西区', + 710999: '其它区', + 711130: '万里区', + 711132: '板桥区', + 711133: '汐止区', + 711134: '深坑区', + 711136: '瑞芳区', + 711137: '平溪区', + 711138: '双溪区', + 711140: '新店区', + 711141: '坪林区', + 711142: '乌来区', + 711143: '永和区', + 711144: '中和区', + 711145: '土城区', + 711146: '三峡区', + 711147: '树林区', + 711149: '三重区', + 711150: '新庄区', + 711151: '泰山区', + 711152: '林口区', + 711154: '五股区', + 711155: '八里区', + 711156: '淡水区', + 711157: '三芝区', + 711287: '宜兰市', + 711288: '头城镇', + 711289: '礁溪乡', + 711290: '壮围乡', + 711291: '员山乡', + 711292: '罗东镇', + 711293: '三星乡', + 711294: '大同乡', + 711295: '五结乡', + 711296: '冬山乡', + 711297: '苏澳镇', + 711298: '南澳乡', + 711299: '钓鱼台', + 711387: '竹北市', + 711388: '湖口乡', + 711389: '新丰乡', + 711390: '新埔镇', + 711391: '关西镇', + 711392: '芎林乡', + 711393: '宝山乡', + 711394: '竹东镇', + 711395: '五峰乡', + 711396: '横山乡', + 711397: '尖石乡', + 711398: '北埔乡', + 711399: '峨眉乡', + 711487: '中坜市', + 711488: '平镇市', + 711489: '龙潭乡', + 711490: '杨梅市', + 711491: '新屋乡', + 711492: '观音乡', + 711493: '桃园市', + 711494: '龟山乡', + 711495: '八德市', + 711496: '大溪镇', + 711497: '复兴乡', + 711498: '大园乡', + 711499: '芦竹乡', + 711582: '竹南镇', + 711583: '头份镇', + 711584: '三湾乡', + 711585: '南庄乡', + 711586: '狮潭乡', + 711587: '后龙镇', + 711588: '通霄镇', + 711589: '苑里镇', + 711590: '苗栗市', + 711591: '造桥乡', + 711592: '头屋乡', + 711593: '公馆乡', + 711594: '大湖乡', + 711595: '泰安乡', + 711596: '铜锣乡', + 711597: '三义乡', + 711598: '西湖乡', + 711599: '卓兰镇', + 711774: '彰化市', + 711775: '芬园乡', + 711776: '花坛乡', + 711777: '秀水乡', + 711778: '鹿港镇', + 711779: '福兴乡', + 711780: '线西乡', + 711781: '和美镇', + 711782: '伸港乡', + 711783: '员林镇', + 711784: '社头乡', + 711785: '永靖乡', + 711786: '埔心乡', + 711787: '溪湖镇', + 711788: '大村乡', + 711789: '埔盐乡', + 711790: '田中镇', + 711791: '北斗镇', + 711792: '田尾乡', + 711793: '埤头乡', + 711794: '溪州乡', + 711795: '竹塘乡', + 711796: '二林镇', + 711797: '大城乡', + 711798: '芳苑乡', + 711799: '二水乡', + 711982: '番路乡', + 711983: '梅山乡', + 711984: '竹崎乡', + 711985: '阿里山乡', + 711986: '中埔乡', + 711987: '大埔乡', + 711988: '水上乡', + 711989: '鹿草乡', + 711990: '太保市', + 711991: '朴子市', + 711992: '东石乡', + 711993: '六脚乡', + 711994: '新港乡', + 711995: '民雄乡', + 711996: '大林镇', + 711997: '溪口乡', + 711998: '义竹乡', + 711999: '布袋镇', + 712180: '斗南镇', + 712181: '大埤乡', + 712182: '虎尾镇', + 712183: '土库镇', + 712184: '褒忠乡', + 712185: '东势乡', + 712186: '台西乡', + 712187: '仑背乡', + 712188: '麦寮乡', + 712189: '斗六市', + 712190: '林内乡', + 712191: '古坑乡', + 712192: '莿桐乡', + 712193: '西螺镇', + 712194: '二仑乡', + 712195: '北港镇', + 712196: '水林乡', + 712197: '口湖乡', + 712198: '四湖乡', + 712199: '元长乡', + 712467: '屏东市', + 712468: '三地门乡', + 712469: '雾台乡', + 712470: '玛家乡', + 712471: '九如乡', + 712472: '里港乡', + 712473: '高树乡', + 712474: '盐埔乡', + 712475: '长治乡', + 712476: '麟洛乡', + 712477: '竹田乡', + 712478: '内埔乡', + 712479: '万丹乡', + 712480: '潮州镇', + 712481: '泰武乡', + 712482: '来义乡', + 712483: '万峦乡', + 712484: '莰顶乡', + 712485: '新埤乡', + 712486: '南州乡', + 712487: '林边乡', + 712488: '东港镇', + 712489: '琉球乡', + 712490: '佳冬乡', + 712491: '新园乡', + 712492: '枋寮乡', + 712493: '枋山乡', + 712494: '春日乡', + 712495: '狮子乡', + 712496: '车城乡', + 712497: '牡丹乡', + 712498: '恒春镇', + 712499: '满州乡', + 712584: '台东市', + 712585: '绿岛乡', + 712586: '兰屿乡', + 712587: '延平乡', + 712588: '卑南乡', + 712589: '鹿野乡', + 712590: '关山镇', + 712591: '海端乡', + 712592: '池上乡', + 712593: '东河乡', + 712594: '成功镇', + 712595: '长滨乡', + 712596: '金峰乡', + 712597: '大武乡', + 712598: '达仁乡', + 712599: '太麻里乡', + 712686: '花莲市', + 712687: '新城乡', + 712688: '太鲁阁', + 712689: '秀林乡', + 712690: '吉安乡', + 712691: '寿丰乡', + 712692: '凤林镇', + 712693: '光复乡', + 712694: '丰滨乡', + 712695: '瑞穗乡', + 712696: '万荣乡', + 712697: '玉里镇', + 712698: '卓溪乡', + 712699: '富里乡', + 712794: '马公市', + 712795: '西屿乡', + 712796: '望安乡', + 712797: '七美乡', + 712798: '白沙乡', + 712799: '湖西乡', + 712896: '南竿乡', + 712897: '北竿乡', + 712898: '东引乡', + 712899: '莒光乡', + 810101: '中西区', + 810102: '湾仔', + 810103: '东区', + 810104: '南区', + 810201: '九龙城区', + 810202: '油尖旺区', + 810203: '深水埗区', + 810204: '黄大仙区', + 810205: '观塘区', + 810301: '北区', + 810302: '大埔区', + 810303: '沙田区', + 810304: '西贡区', + 810305: '元朗区', + 810306: '屯门区', + 810307: '荃湾区', + 810308: '葵青区', + 810309: '离岛区', + 820101: '澳门半岛', + 820201: '离岛' + } + }; + + + Class.prototype.config = { + elem: '', + data: { + province: '', + city: '', + county: '', + provinceCode: 0, + cityCode: 0, + countyCode: 0, + }, + change: function(result){} + }; + + Class.prototype.index = 0; + + Class.prototype.render = function () { + let that = this, options = that.config; + options.elem = $(options.elem); + options.bindAction = $(options.bindAction); + + that.events(); + }; + + Class.prototype.events = function () { + let that = this, options = that.config, index; + let provinceFilter = 'province-' + layarea._id; + let cityFilter = 'city-' + layarea._id; + let countyFilter = 'county-' + layarea._id; + + let provinceEl = options.elem.find('.province-selector'); + let cityEl = options.elem.find('.city-selector'); + let countyEl = options.elem.find('.county-selector'); + + //filter + if(provinceEl.attr('lay-filter')){ + provinceFilter = provinceEl.attr('lay-filter'); + } + if(cityEl.attr('lay-filter')){ + cityFilter = cityEl.attr('lay-filter'); + } + if(countyEl.attr('lay-filter')){ + countyFilter = countyEl.attr('lay-filter'); + } + provinceEl.attr('lay-filter', provinceFilter); + cityEl.attr('lay-filter', cityFilter); + countyEl.attr('lay-filter', countyFilter); + + //获取默认值 + if(provinceEl.data('value')){ + options.data.province = provinceEl.data('value'); + options.data.provinceCode = getCode('province', options.data.province); + } + if(cityEl.data('value')){ + options.data.city = cityEl.data('value'); + let code = getCode('city', options.data.city, options.data.provinceCode.slice(0, 2)); + options.data.cityCode = code; + } + if(countyEl.data('value')){ + options.data.county = countyEl.data('value'); + options.data.countyCode = getCode('county', options.data.county, options.data.cityCode.slice(0, 4)); + } + provinceEl.attr('lay-filter', provinceFilter); + cityEl.attr('lay-filter', cityFilter); + countyEl.attr('lay-filter', countyFilter); + + //监听结果 + form.on('select('+provinceFilter+')', function(data){ + options.data.province = data.value; + options.data.provinceCode = getCode('province', data.value); + renderCity(options.data.provinceCode); + + options.change(options.data); + }); + form.on('select('+cityFilter+')', function(data){ + options.data.city = data.value; + if(options.data.provinceCode){ + options.data.cityCode = getCode('city', data.value, options.data.provinceCode.slice(0, 2)); + renderCounty(options.data.cityCode); + } + + options.change(options.data); + }); + form.on('select('+countyFilter+')', function(data){ + options.data.county = data.value; + if(options.data.cityCode){ + options.data.countyCode = getCode('county', data.value, options.data.cityCode.slice(0, 4)); + } + options.change(options.data); + }); + + renderProvince(); + + //查找province + function renderProvince(){ + let tpl = ''; + let provinceList = getList("province"); + let currentCode = ''; + let currentName = ''; + provinceList.forEach(function(_item){ + // if (!currentCode){ + // currentCode = _item.code; + // currentName = _item.name; + // } + if(_item.name === options.data.province){ + currentCode = _item.code; + currentName = _item.name; + } + tpl += ''; + }); + provinceEl.html(tpl); + provinceEl.val(options.data.province); + form.render('select'); + renderCity(currentCode); + } + + function renderCity(provinceCode){ + let tpl = ''; + let cityList = getList('city', provinceCode.slice(0, 2)); + let currentCode = ''; + let currentName = ''; + cityList.forEach(function(_item){ + // if (!currentCode){ + // currentCode = _item.code; + // currentName = _item.name; + // } + if(_item.name === options.data.city){ + currentCode = _item.code; + currentName = _item.name; + } + tpl += ''; + }); + options.data.city = currentName; + cityEl.html(tpl); + cityEl.val(options.data.city); + form.render('select'); + renderCounty(currentCode); + } + + function renderCounty(cityCode){ + let tpl = ''; + let countyList = getList('county', cityCode.slice(0, 4)); + let currentCode = ''; + let currentName = ''; + countyList.forEach(function(_item){ + // if (!currentCode){ + // currentCode = _item.code; + // currentName = _item.name; + // } + if(_item.name === options.data.county){ + currentCode = _item.code; + currentName = _item.name; + } + tpl += ''; + }); + options.data.county = currentName; + countyEl.html(tpl); + countyEl.val(options.data.county); + + form.render('select'); + } + + function getList(type, code) { + let result = []; + + if (type !== 'province' && !code) { + return result; + } + + let list = areaList[type + "_list"] || {}; + result = Object.keys(list).map(function (code) { + return { + code: code, + name: list[code] + }; + }); + + if (code) { + // oversea code + if (code[0] === '9' && type === 'city') { + code = '9'; + } + + result = result.filter(function (item) { + return item.code.indexOf(code) === 0; + }); + } + + return result; + } + + function getCode(type, name, parentCode = 0){ + let code = ''; + let list = areaList[type + "_list"] || {}; + let result = {}; + Object.keys(list).map(function (_code) { + if(parentCode){ + if(_code.indexOf(parentCode) === 0){ + result[_code] = list[_code]; + } + }else{ + result[_code] = list[_code]; + } + }); + layui.each(result, function(_code, _name){ + if(_name === name){ + code = _code; + } + }); + + return code; + } + }; + + layarea.render = function (options) { + let inst = new Class(options); + layarea._id++; + return thisArea.call(inst); + }; + + //暴露接口 + exports('layarea', layarea); +}); \ No newline at end of file diff --git a/public/js/lay-module/layselect/layselect.js b/public/js/lay-module/layselect/layselect.js new file mode 100644 index 0000000..90f8deb --- /dev/null +++ b/public/js/lay-module/layselect/layselect.js @@ -0,0 +1,177 @@ +/** + * layselect 下拉框插件,只支持单选 + * @author:Darker.Wang + * @version:1.1(优化增加数据内部控制元素默认选择) + * @version:1.2(优化支持指定请求头认证,修复元素默认选择优先级错乱) + * render参数{ + * 属性: + * elem:元素ID,带#号必传 + * url:请求路径的URL,必传 + * data:请求url所携带的参数,可选 + * type:请求方式,默认为get,可选 + * option:元素数据,数组,用于不通过请求url获取数据,本地自动赋值,可选 + * select:指定选中的索引项,可选,分组时索引为:groupNum-itemNum,如:1-2 表示第一组里的第二个元素 + * 方法: + * format:格式化方法映射,将返回的Data元素映射乘标准格式 + * success:成功回调,返回加载后的对象数组 + * fail:失败回调,加载失败的处理 + * onselect:点击选择时事件响应(如事件无响应,记得加lay-filter属性=id) + * headers:可选,用于传递请求头认证,默认为:{Accept: "application/json; charset=utf-8"} + * contentType:可选,用于指定请求接口的数据类型,默认为:application/json + * } + * 请求返回需对象格式:rtvObj=option={status,code,codeName,select,...},不满足的通过format映射处理 status=0 时表示禁用 + * 其他说明: + * 1、分组展示按照:groupName,groupChildren 数组 [rtvObj] + * 2、暂不支持多选(后期版本规划:支持多选,在select标签上设置属性:multiple="true") + * 3、获取原始数据,可通过调用success得到,返回原始数据的集合 + * 4、点击事件回调,可通过实现onselect 触发,参数为当前点击的值 + * @param exports + * @returns 返回一个对象 + * 码云地址:https://gitee.com/godbirds/layselect + */ +layui.define(['element','form','jquery'],function(exports){ + var element = layui.element; + var form = layui.form; + var $ = layui.jquery; + var obj={ + //{elem,url,data,type,format} + render:function(param){ + var that = this; + var eid = param.elem; + that.selectValues=new Array(); + if(param.type == null || param.type==undefined){ + param.type = 'get';//默认get请求 + } + if(param.data){ + param.data = JSON.stringify(data); + }else{ + param.data = {} + } + if(param.url == null || param.url == '' || param.url==undefined){ + if(param.option == null || param.option == undefined){ + param.option = new Array();//重新定义为了正常显示下拉框 + } + that._init(eid,param,param.option); + }else{ + $.ajax({ + url: param.url, + type: param.type, + data: param.data, + async: 'false', + dataType: 'json', + headers: param.headers||{Accept: "application/json; charset=utf-8"}, + contentType : param.contentType||'application/json',//指定json头 + success: function(data){ + that._init(eid,param,data) + }, + error: function (e) { + //加载失败执行回调函数 + console.log("Ajax request "+param.url+" error! "); + if(param.fail){ + param.fail(e);//加载失败回调函数,请求URL时才会触发 + } + } + }); + } + /*** + * 初始化:data=[{code,status,codeName,select,groupName,groupChildren}] + */ + that._init=function(eid,param,data){ + $(eid).empty();//请求成功时清空 + $(eid).prepend("");//添加第一个option值 + var option = new Array(); + if(param.format){//格式化 + for (var i = 0; i < data.length; i++) { + var tdata = param.format(data[i]); + option.push({ + code:tdata.code, + status:tdata.status||'1', + select:tdata.select||'false', + codeName:tdata.codeName, + groupName:tdata.groupName, + groupChildren:tdata.groupChildren + }); + } + }else{//不格式化 + for (var i = 0; i < data.length; i++) { + option.push({ + code:data[i].code, + status:data[i].status||'1', + select:data[i].select||'false', + codeName:data[i].codeName, + groupName:data[i].groupName, + groupChildren:data[i].groupChildren + }); + } + } + var isgroup = false; + for (var i = 0; i < option.length; i++) { + //分组 + if(option[i].groupChildren != null && option[i].groupChildren != undefined && + option[i].groupChildren != '' && option[i].groupChildren.length > 0){ + isgroup = true;//分组标识 + $(eid).append(""); + option[i].groupChildren.forEach(function(item,index){ + var status = "";var topborder ="",buttomborder="",checkoff=""; + if(item.status && item.status == '0'){status = "disabled='disabled'";}//是否有效 + $(eid).append(""); + //优先级:内部数据option中的选中标识优先级高于外部指定的优先级 + if(item.select != undefined && (item.select == 'true' || item.select == true) && item.status != '0'){ + that.selectValues.push(item); + } + }); + $(eid).append(""); + }else{//不分组 + isgroup = false;//分组标识 + var status = "";var topborder ="",buttomborder="",checkoff=""; + if(option[i].status && option[i].status == '0'){status = "disabled='disabled'";}//是否有效 + $(eid).append(""); + //内部数据option中的选中标识优先级高于外部指定的优先级 + if(option[i].select != undefined && (option[i].select == 'true' || option[i].select == true) && option[i].status != '0'){ + that.selectValues.push(option[i]); + } + } + } + //内部无选中则判断外部是否指定,遵循内部优先级高于外部 + if(that.selectValues.length == 0 && param.select != undefined){ + var item; + var si = param.select+""; + if(isgroup && si.indexOf("-") > 0){//分组时索引为指定为 group-item + var s = si.split("-");//拆分组 + item = option[s[0]].groupChildren[s[1]]; + }else{ + item = option[si]; + } + if(item && item.status != '0'){ + that.selectValues.push(item); + } + }//选择指定选中项 + + //var k = eid.replace('#',''); + var multiple = $(eid).attr('multiple');//多选控制,暂时无用 + //console.log(eid,that.selectValues); + if(that.selectValues.length>0){//默认选中数据在循环外部,提高渲染效率 + that.selectValues.forEach(function(item,index){ + $(eid).find("option[value = '"+item.code+"']").attr("selected","selected"); + }); + }else{ + //$(eid).find("option[value = '']").attr("selected","selected");//默认选中请选择 + } + //加载成功执行回调函数 + if(param.success){ + //console.log(k+" call success",option); + param.success(option); + option = null; + } + //监听事件:这里做自己想做的事情 + form.render('select');//select是固定写法 不是选择器 + form.on('select('+eid.replace('#','')+')', function(data){ + if(param.onselect){//点击选择事件 + param.onselect(data.value); + } + }); + } + } + }; + exports('layselect',obj); +}); \ No newline at end of file diff --git a/public/js/lay-module/layuimini/miniAdmin.js b/public/js/lay-module/layuimini/miniAdmin.js new file mode 100644 index 0000000..8bda5bc --- /dev/null +++ b/public/js/lay-module/layuimini/miniAdmin.js @@ -0,0 +1,368 @@ +/** + * date:2020/02/27 + * author:Mr.Chung + * version:2.0 + * description:layuimini 主体框架扩展 + */ +layui.define(["jquery", "miniMenu", "element", "miniTab", "miniTheme"], function (exports) { + var $ = layui.$, + layer = layui.layer, + miniMenu = layui.miniMenu, + miniTheme = layui.miniTheme, + element = layui.element , + miniTab = layui.miniTab; + + if (!/http(s*):\/\//.test(location.href)) { + var tips = "请先将项目部署至web容器(Apache/Tomcat/Nginx/IIS/等),否则部分数据将无法显示"; + return layer.alert(tips); + } + + var miniAdmin = { + + /** + * 后台框架初始化 + * @param options.iniUrl 后台初始化接口地址 + * @param options.clearUrl 后台清理缓存接口 + * @param options.urlHashLocation URL地址hash定位 + * @param options.bgColorDefault 默认皮肤 + * @param options.multiModule 是否开启多模块 + * @param options.menuChildOpen 是否展开子菜单 + * @param options.loadingTime 初始化加载时间 + * @param options.pageAnim iframe窗口动画 + * @param options.maxTabNum 最大的tab打开数量 + */ + render: function (options) { + options.iniUrl = options.iniUrl || null; + options.clearUrl = options.clearUrl || null; + options.urlHashLocation = options.urlHashLocation || false; + options.bgColorDefault = options.bgColorDefault || 0; + options.multiModule = options.multiModule || false; + options.menuChildOpen = options.menuChildOpen || false; + options.loadingTime = options.loadingTime || 0; + options.pageAnim = options.pageAnim || false; + options.maxTabNum = options.maxTabNum || 200; + options.isHideOpenMenu = !options.isHideOpenMenu ? options.isHideOpenMenu : true; + $.getJSON(options.iniUrl, function (data) { + if (data == null) { + miniAdmin.error('暂无菜单信息') + } else { + miniAdmin.renderLogo(data.logoInfo); + miniAdmin.renderClear(options.clearUrl); + miniAdmin.renderHome(data.homeInfo); + miniAdmin.renderAnim(options.pageAnim); + miniAdmin.listen(); + miniMenu.render({ + menuList: data.menuInfo, + multiModule: options.multiModule, + menuChildOpen: options.menuChildOpen + }); + miniTab.render({ + filter: 'layuiminiTab', + urlHashLocation: options.urlHashLocation, + multiModule: options.multiModule, + menuChildOpen: options.menuChildOpen, + maxTabNum: options.maxTabNum, + menuList: data.menuInfo, + homeInfo: data.homeInfo, + listenSwichCallback: function () { + miniAdmin.renderDevice(); + } + }); + miniTheme.render({ + bgColorDefault: options.bgColorDefault, + listen: true, + }); + miniAdmin.deleteLoader(options.loadingTime); + miniAdmin.isHideOpenMenu(options.isHideOpenMenu); + } + }).fail(function () { + miniAdmin.error('菜单接口有误'); + }); + }, + + /** + * 点击左侧下拉菜单,关闭其他下拉菜单 + * @param data + */ + isHideOpenMenu: function (data) { + $(".layui-side").on("click", ".layui-nav-item", function () { + if (data) { + $(this).siblings('li').attr('class', 'layui-nav-item'); + } + }); + }, + + /** + * 初始化logo + * @param data + */ + renderLogo: function (data) { + var html = 'logo

          ' + data.title + '

          '; + $('.layuimini-logo').html(html); + }, + + /** + * 初始化首页 + * @param data + */ + renderHome: function (data) { + sessionStorage.setItem('layuiminiHomeHref', data.href); + $('#layuiminiHomeTabId').html('' + data.title + ''); + $('#layuiminiHomeTabId').attr('lay-id', data.href); + $('#layuiminiHomeTabIframe').html(''); + }, + + /** + * 初始化缓存地址 + * @param clearUrl + */ + renderClear: function (clearUrl) { + $('.layuimini-clear').attr('data-href',clearUrl); + }, + + /** + * 初始化iframe窗口动画 + * @param anim + */ + renderAnim: function (anim) { + if (anim) { + $('#layuimini-bg-color').after(''); + } + }, + + fullScreen: function () { + var el = document.documentElement; + var rfs = el.requestFullScreen || el.webkitRequestFullScreen; + if (typeof rfs != "undefined" && rfs) { + rfs.call(el); + } else if (typeof window.ActiveXObject != "undefined") { + var wscript = new ActiveXObject("WScript.Shell"); + if (wscript != null) { + wscript.SendKeys("{F11}"); + } + } else if (el.msRequestFullscreen) { + el.msRequestFullscreen(); + } else if (el.oRequestFullscreen) { + el.oRequestFullscreen(); + } else if (el.webkitRequestFullscreen) { + el.webkitRequestFullscreen(); + } else if (el.mozRequestFullScreen) { + el.mozRequestFullScreen(); + } else { + miniAdmin.error('浏览器不支持全屏调用!'); + } + }, + + /** + * 退出全屏 + */ + exitFullScreen: function () { + var el = document; + var cfs = el.cancelFullScreen || el.webkitCancelFullScreen || el.exitFullScreen; + if (typeof cfs != "undefined" && cfs) { + cfs.call(el); + } else if (typeof window.ActiveXObject != "undefined") { + var wscript = new ActiveXObject("WScript.Shell"); + if (wscript != null) { + wscript.SendKeys("{F11}"); + } + } else if (el.msExitFullscreen) { + el.msExitFullscreen(); + } else if (el.oRequestFullscreen) { + el.oCancelFullScreen(); + }else if (el.mozCancelFullScreen) { + el.mozCancelFullScreen(); + } else if (el.webkitCancelFullScreen) { + el.webkitCancelFullScreen(); + } else { + miniAdmin.error('浏览器不支持全屏调用!'); + } + }, + + /** + * 初始化设备端 + */ + renderDevice: function () { + if (miniAdmin.checkMobile()) { + $('.layuimini-tool i').attr('data-side-fold', 1); + $('.layuimini-tool i').attr('class', 'layui-icon layui-icon-list'); + $('.layui-layout-body').removeClass('layuimini-mini'); + $('.layui-layout-body').addClass('layuimini-all'); + } + }, + + + /** + * 初始化加载时间 + * @param loadingTime + */ + deleteLoader: function (loadingTime) { + if (loadingTime) { + setTimeout(function () { + $('.layuimini-loader').fadeOut(); + }, loadingTime * 1000) + } else { + $('.layuimini-loader').fadeOut(); + } + }, + + /** + * 成功 + * @param title + * @returns {*} + */ + success: function (title) { + return layer.msg(title, {icon: 1, shade: this.shade, scrollbar: false, time: 2000, shadeClose: true}); + }, + + /** + * 失败 + * @param title + * @returns {*} + */ + error: function (title) { + return layer.msg(title, {icon: 2, shade: this.shade, scrollbar: false, time: 3000, shadeClose: true}); + }, + + /** + * 判断是否为手机 + * @returns {boolean} + */ + checkMobile: function () { + var ua = navigator.userAgent.toLocaleLowerCase(); + var pf = navigator.platform.toLocaleLowerCase(); + var isAndroid = (/android/i).test(ua) || ((/iPhone|iPod|iPad/i).test(ua) && (/linux/i).test(pf)) + || (/ucweb.*linux/i.test(ua)); + var isIOS = (/iPhone|iPod|iPad/i).test(ua) && !isAndroid; + var isWinPhone = (/Windows Phone|ZuneWP7/i).test(ua); + var clientWidth = document.documentElement.clientWidth; + if (!isAndroid && !isIOS && !isWinPhone && clientWidth > 1024) { + return false; + } else { + return true; + } + }, + + /** + * 监听 + */ + listen: function () { + + /** + * 清理 + */ + $('body').on('click', '[data-clear]', function () { + var loading = layer.load(0, {shade: false, time: 2 * 1000}); + sessionStorage.clear(); + + // 判断是否清理服务端 + var clearUrl = $(this).attr('data-href'); + if (clearUrl != undefined && clearUrl != '' && clearUrl != null) { + $.getJSON(clearUrl, function (data, status) { + layer.close(loading); + if (data.code != 0) { + return miniAdmin.error(data.msg); + } else { + return miniAdmin.success(data.msg); + } + }).fail(function () { + layer.close(loading); + return miniAdmin.error('清理缓存接口有误'); + }); + } else { + layer.close(loading); + return miniAdmin.success('清除缓存成功'); + } + }); + + /** + * 刷新 + */ + $('body').on('click', '[data-refresh]', function () { + $(".layui-tab-item.layui-show").find("iframe")[0].contentWindow.location.reload(); + miniAdmin.success('刷新成功'); + }); + + /** + * 监听提示信息 + */ + $("body").on("mouseenter", ".layui-nav-tree .menu-li", function () { + if (miniAdmin.checkMobile()) { + return false; + } + var classInfo = $(this).attr('class'), + tips = $(this).prop("innerHTML"), + isShow = $('.layuimini-tool i').attr('data-side-fold'); + if (isShow == 0 && tips) { + tips = "
          • "+tips+"
          " ; + window.openTips = layer.tips(tips, $(this), { + tips: [2, '#2f4056'], + time: 300000, + skin:"popup-tips", + success:function (el) { + var left = $(el).position().left - 10 ; + $(el).css({ left:left }); + element.render(); + } + }); + } + }); + + $("body").on("mouseleave", ".popup-tips", function () { + if (miniAdmin.checkMobile()) { + return false; + } + var isShow = $('.layuimini-tool i').attr('data-side-fold'); + if (isShow == 0) { + try { + layer.close(window.openTips); + } catch (e) { + console.log(e.message); + } + } + }); + + + /** + * 全屏 + */ + $('body').on('click', '[data-check-screen]', function () { + var check = $(this).attr('data-check-screen'); + if (check == 'full') { + miniAdmin.fullScreen(); + $(this).attr('data-check-screen', 'exit'); + $(this).html(''); + } else { + miniAdmin.exitFullScreen(); + $(this).attr('data-check-screen', 'full'); + $(this).html(''); + } + }); + + /** + * 点击遮罩层 + */ + $('body').on('click', '.layuimini-make', function () { + miniAdmin.renderDevice(); + }); + + } + }; + + + exports("miniAdmin", miniAdmin); +}); diff --git a/public/js/lay-module/layuimini/miniMenu.js b/public/js/lay-module/layuimini/miniMenu.js new file mode 100644 index 0000000..e1ca743 --- /dev/null +++ b/public/js/lay-module/layuimini/miniMenu.js @@ -0,0 +1,253 @@ +/** + * date:2020/02/27 + * author:Mr.Chung + * version:2.0 + * description:layuimini 菜单框架扩展 + */ +layui.define(["element","laytpl" ,"jquery"], function (exports) { + var element = layui.element, + $ = layui.$, + laytpl = layui.laytpl, + layer = layui.layer; + + var miniMenu = { + + /** + * 菜单初始化 + * @param options.menuList 菜单数据信息 + * @param options.multiModule 是否开启多模块 + * @param options.menuChildOpen 是否展开子菜单 + */ + render: function (options) { + options.menuList = options.menuList || []; + options.multiModule = options.multiModule || false; + options.menuChildOpen = options.menuChildOpen || false; + if (options.multiModule) { + miniMenu.renderMultiModule(options.menuList, options.menuChildOpen); + } else { + miniMenu.renderSingleModule(options.menuList, options.menuChildOpen); + } + miniMenu.listen(); + }, + + /** + * 单模块 + * @param menuList 菜单数据 + * @param menuChildOpen 是否默认展开 + */ + renderSingleModule: function (menuList, menuChildOpen) { + menuList = menuList || []; + var leftMenuHtml = '', + childOpenClass = '', + leftMenuCheckDefault = 'layui-this'; + var me = this ; + if (menuChildOpen) childOpenClass = ' layui-nav-itemed'; + leftMenuHtml = this.renderLeftMenu(menuList,{ childOpenClass:childOpenClass }) ; + $('.layui-layout-body').addClass('layuimini-single-module'); //单模块标识 + $('.layuimini-header-menu').remove(); + $('.layuimini-menu-left').html(leftMenuHtml); + + element.init(); + }, + + /** + * 渲染一级菜单 + */ + compileMenu: function(menu,isSub){ + var menuHtml = '' ; + if(isSub){ + menuHtml = '' + } + return laytpl(menuHtml).render(menu); + }, + compileMenuContainer :function(menu,isSub){ + var wrapperHtml = '
            {{-d.children}}
          ' ; + if(isSub){ + wrapperHtml = '
          {{-d.children}}
          ' ; + } + if(!menu.children){ + return ""; + } + return laytpl(wrapperHtml).render(menu); + }, + + each:function(list,callback){ + var _list = []; + for(var i = 0 ,length = list.length ; i= options.maxTabNum) { + layer.msg('Tab窗口已达到限定数量,请先关闭部分Tab'); + return false; + } + var ele = element; + if (options.isIframe) ele = parent.layui.element; + // + console.log('aaa'); + ele.tabAdd('layuiminiTab', { + title: '' + options.title + '' //用于演示 + , content: '' + , id: options.tabId + }); + console.log('bbb'); + // + $('.layuimini-menu-left').attr('layuimini-tab-tag', 'add'); + sessionStorage.setItem('layuiminimenu_' + options.tabId, options.title); + }, + + + /** + * 切换选项卡 + * @param tabId + */ + change: function (tabId) { + element.tabChange('layuiminiTab', tabId); + }, + + /** + * 删除tab窗口 + * @param tabId + * @param isParent + */ + delete: function (tabId, isParent) { + // todo 未知BUG,不知道是不是layui问题,必须先删除元素 + $(".layuimini-tab .layui-tab-title .layui-unselect.layui-tab-bar").remove(); + + if (isParent === true) { + parent.layui.element.tabDelete('layuiminiTab', tabId); + } else { + element.tabDelete('layuiminiTab', tabId); + } + }, + + /** + * 在iframe层打开新tab方法 + */ + openNewTabByIframe: function (options) { + options.href = options.href || null; + options.id = options.id || options.href; + options.title = options.title || null; + var loading = parent.layer.load(0, {shade: false, time: 2 * 1000}); + if (options.href === null || options.href === undefined) options.href = new Date().getTime(); + var checkTab = miniTab.check(options.id, true); + if (!checkTab) { + parent.layui.element.tabChange('layuiminiTab','0'); + setTimeout(function(){ + miniTab.create({ + tabId: options.id, + href: options.href, + title: options.title, + isIframe: true, + }); + parent.layui.element.tabChange('layuiminiTab', options.id); + parent.layer.close(loading); + },100); + }else{ + parent.layui.element.tabChange('layuiminiTab', options.id); + parent.layer.close(loading); + } + }, + openNewTab: function (options) { + // console.log(options,'o'); + options.href = options.href || null; + options.id = options.id || options.href; + options.title = options.title || null; + // console.log(options,'p'); + var loading = layer.load(0, {shade: false, time: 2 * 1000}); + if (options.href === null || options.href === undefined) options.href = new Date().getTime(); + var checkTab = miniTab.check(options.id, true); + // console.log(checkTab,'po',options.id); + if (!checkTab) { + layui.element.tabChange('layuiminiTab','0'); + setTimeout(() => { + miniTab.create({ + tabId: options.id, + href: options.href, + title: options.title, + isIframe: true, + }); + layui.element.tabChange('layuiminiTab', options.id); + layer.close(loading); + }, 100); + }else{ + layui.element.tabChange('layuiminiTab', options.id); + layer.close(loading); + } + }, + + /** + * 在iframe层关闭当前tab方法 + */ + deleteCurrentByIframe: function () { + var ele = $(".layuimini-tab .layui-tab-title li.layui-this", parent.document); + if (ele.length > 0) { + var layId = $(ele[0]).attr('lay-id'); + miniTab.delete(layId, true); + } + }, + + /** + * 判断tab窗口 + */ + check: function (tabId, isIframe) { + // 判断选项卡上是否有 + tabId=tabId+""; + var checkTab = false; + if (isIframe === undefined || isIframe === false) { + $(".layui-tab-title li").each(function () { + var checkTabId = $(this).attr('lay-id'); + if (checkTabId != null && checkTabId === tabId) { + checkTab = true; + } + }); + } else { + parent.layui.$(".layui-tab-title li").each(function () { + var checkTabId = $(this).attr('lay-id'); + if (checkTabId != null && checkTabId === tabId) { + checkTab = true; + } + }); + } + return checkTab; + }, + + /** + * 开启tab右键菜单 + * @param tabId + * @param left + */ + openTabRignMenu: function (tabId, left) { + miniTab.closeTabRignMenu(); + var menuHtml = '
          \n' + + '
          \n' + + '
          关 闭 当 前
          \n' + + '
          关 闭 其 他
          \n' + + '
          关 闭 全 部
          \n' + + '
          脱 离 标 签
          \n' + + '
          \n' + + '
          '; + var makeHtml = '
          '; + $('.layuimini-tab .layui-tab-title').after(menuHtml); + $('.layuimini-tab .layui-tab-content').after(makeHtml); + }, + + /** + * 关闭tab右键菜单 + */ + closeTabRignMenu: function () { + $('.layuimini-tab-mousedown').remove(); + $('.layuimini-tab-make').remove(); + }, + + /** + * 查询菜单信息 + * @param href + * @param menuList + */ + searchMenu: function (href, menuList) { + var menu; + for (key in menuList) { + var item = menuList[key]; + if (item.href === href) { + menu = item; + break; + } + if (item.child) { + newMenu = miniTab.searchMenu(href, item.child); + if (newMenu) { + menu = newMenu; + break; + } + } + } + return menu; + }, + + /** + * 监听 + * @param options + */ + listen: function (options) { + options = options || {}; + options.maxTabNum = options.maxTabNum || 200; + + /** + * 打开新窗口 + */ + $('body').on('click', '[layuimini-href]', function () { + var loading = layer.load(0, {shade: false, time: 2 * 1000}); + var tabId = $(this).attr('layuimini-href'), + href = $(this).attr('layuimini-href'), + title = $(this).text(), + target = $(this).attr('target'); + + var el = $("[layuimini-href='" + href + "']", ".layuimini-menu-left"); + layer.close(window.openTips); + if (el.length) { + $(el).closest(".layui-nav-tree").find(".layui-this").removeClass("layui-this"); + $(el).parent().addClass("layui-this"); + } + + if (target === '_blank') { + layer.close(loading); + window.open(href, "_blank"); + return false; + } + + if (tabId === null || tabId === undefined) tabId = new Date().getTime(); + var checkTab = miniTab.check(tabId); + if(checkTab){ + miniTab.change(tabId); + } + if (!checkTab) { + miniTab.create({ + tabId: tabId, + href: href, + title: title, + isIframe: false, + maxTabNum: options.maxTabNum, + }); + } + element.tabChange('layuiminiTab', tabId); + layer.close(loading); + }); + + /** + * 在iframe子菜单上打开新窗口 + */ + $('body').on('click', '[layuimini-content-href]', function () { + var loading = parent.layer.load(0, {shade: false, time: 2 * 1000}); + var tabId = $(this).attr('layuimini-content-href'), + href = $(this).attr('layuimini-content-href'), + title = $(this).attr('data-title'), + target = $(this).attr('target'); + if (target === '_blank') { + parent.layer.close(loading); + window.open(href, "_blank"); + return false; + } + if (tabId === null || tabId === undefined) tabId = new Date().getTime(); + var checkTab = miniTab.check(tabId, true); + if (!checkTab) { + miniTab.create({ + tabId: tabId, + href: href, + title: title, + isIframe: true, + maxTabNum: options.maxTabNum, + }); + } + parent.layui.element.tabChange('layuiminiTab', tabId); + parent.layer.close(loading); + }); + + /** + * 关闭选项卡 + **/ + $('body').on('click', '.layuimini-tab .layui-tab-title .layui-tab-close', function () { + var loading = layer.load(0, {shade: false, time: 2 * 1000}); + var $parent = $(this).parent(); + var tabId = $parent.attr('lay-id'); + if (tabId !== undefined || tabId !== null) { + miniTab.delete(tabId); + } + layer.close(loading); + }); + + /** + * 选项卡操作 + */ + $('body').on('click', '[layuimini-tab-close]', function () { + var loading = layer.load(0, {shade: false, time: 2 * 1000}); + var closeType = $(this).attr('layuimini-tab-close'); + $(".layuimini-tab .layui-tab-title li").each(function () { + var tabId = $(this).attr('lay-id'); + var id = $(this).attr('id'); + var isCurrent = $(this).hasClass('layui-this'); + if (id !== 'layuiminiHomeTabId') { + if (closeType === 'all') { + miniTab.delete(tabId); + } else { + if (closeType === 'current' && isCurrent) { + miniTab.delete(tabId); + } else if (closeType === 'other' && !isCurrent) { + miniTab.delete(tabId); + } + } + } + }); + layer.close(loading); + }); + + /** + * 禁用网页右键 + */ + $(".layuimini-tab .layui-tab-title").unbind("mousedown").bind("contextmenu", function (e) { + e.preventDefault(); + return false; + }); + + /** + * 注册鼠标右键 + */ + $('body').on('mousedown', '.layuimini-tab .layui-tab-title li', function (e) { + var left = $(this).offset().left - $('.layuimini-tab ').offset().left + ($(this).width() / 2), + tabId = $(this).attr('lay-id'); + if (e.which === 3) { + miniTab.openTabRignMenu(tabId, left); + } + }); + + /** + * 关闭tab右键菜单 + */ + $('body').on('click', '.layui-body,.layui-header,.layuimini-menu-left,.layuimini-tab-make', function () { + miniTab.closeTabRignMenu(); + }); + + /** + * tab右键选项卡操作 + */ + $('body').on('click', '[layuimini-tab-menu-close]', function () { + var loading = layer.load(0, {shade: false, time: 2 * 1000}); + var closeType = $(this).attr('layuimini-tab-menu-close'), + currentTabId = $('.layuimini-tab-mousedown').attr('data-tab-id'); + $(".layuimini-tab .layui-tab-title li").each(function () { + var tabId = $(this).attr('lay-id'); + var id = $(this).attr('id'); + if (id !== 'layuiminiHomeTabId') { + if (closeType === 'all') { + miniTab.delete(tabId); + } else { + if (closeType === 'current' && currentTabId === tabId) { + miniTab.delete(tabId); + return false; + } else if (closeType === 'other' && currentTabId !== tabId) { + miniTab.delete(tabId); + } else if (closeType === 'divorced' && currentTabId === tabId) { + miniTab.divorced(tabId); + } + } + } + }); + miniTab.closeTabRignMenu(); + layer.close(loading); + }); + }, + + /** + * 监听tab切换 + * @param options + */ + listenSwitch: function (options) { + options.filter = options.filter || null; + options.multiModule = options.multiModule || false; + options.urlHashLocation = options.urlHashLocation || false; + options.listenSwichCallback = options.listenSwichCallback || function () { + + }; + element.on('tab(' + options.filter + ')', function (data) { + var tabId = $(this).attr('lay-id'); + if (options.urlHashLocation) { + location.hash = '/' + tabId; + } + if (typeof options.listenSwichCallback === 'function') { + options.listenSwichCallback(); + } + if (typeof options.switchtoindex === 'function') { + options.switchtoindex(); + } + // 判断是否为新增窗口 + if ($('.layuimini-menu-left').attr('layuimini-tab-tag') === 'add') { + $('.layuimini-menu-left').attr('layuimini-tab-tag', 'no') + } + + $("div.layui-side ul li.layui-nav-itemed").removeClass("layui-nav-itemed"); + + $("[layuimini-href]").parent().removeClass('layui-this'); + if (options.multiModule) { + miniTab.listenSwitchMultiModule(tabId); + } else { + miniTab.listenSwitchSingleModule(tabId); + } + + miniTab.rollPosition(); + }); + }, + + /** + * 监听hash变化 + * @param options + * @returns {boolean} + */ + listenHash: function (options) { + options.urlHashLocation = options.urlHashLocation || false; + options.maxTabNum = options.maxTabNum || 200; + options.homeInfo = options.homeInfo || {}; + options.menuList = options.menuList || []; + if (!options.urlHashLocation) return false; + var tabId = location.hash.replace(/^#\//, ''); + if (tabId === null || tabId === undefined || tabId ==='') return false; + + // 判断是否为首页 + if(tabId ===options.homeInfo.href) return false; + + // 判断是否为右侧菜单 + var menu = miniTab.searchMenu(tabId, options.menuList); + if (menu !== undefined) { + miniTab.create({ + tabId: tabId, + href: tabId, + title: menu.title, + isIframe: false, + maxTabNum: options.maxTabNum, + }); + $('.layuimini-menu-left').attr('layuimini-tab-tag', 'no'); + element.tabChange('layuiminiTab', tabId); + return false; + } + + // 判断是否为快捷菜单 + var isSearchMenu = false; + $("[layuimini-content-href]").each(function () { + if ($(this).attr("layuimini-content-href") === tabId) { + var title = $(this).attr("data-title"); + miniTab.create({ + tabId: tabId, + href: tabId, + title: title, + isIframe: false, + maxTabNum: options.maxTabNum, + }); + $('.layuimini-menu-left').attr('layuimini-tab-tag', 'no'); + element.tabChange('layuiminiTab', tabId); + isSearchMenu = true; + return false; + } + }); + if (isSearchMenu) return false; + + // 既不是右侧菜单、快捷菜单,就直接打开 + var title = sessionStorage.getItem('layuiminimenu_' + tabId) === null ? tabId : sessionStorage.getItem('layuiminimenu_' + tabId); + miniTab.create({ + tabId: tabId, + href: tabId, + title: title, + isIframe: false, + maxTabNum: options.maxTabNum, + }); + element.tabChange('layuiminiTab', tabId); + return false; + }, + + /** + * 监听滚动 + */ + listenRoll: function () { + $(".layuimini-tab-roll-left").click(function () { + miniTab.rollClick("left"); + }); + $(".layuimini-tab-roll-right").click(function () { + miniTab.rollClick("right"); + }); + }, + + /** + * 单模块切换 + * @param tabId + */ + listenSwitchSingleModule: function (tabId) { + $("[layuimini-href]").each(function () { + if ($(this).attr("layuimini-href") === tabId) { + // 自动展开菜单栏 + var addMenuClass = function ($element, type) { + if (type === 1) { + $element.addClass('layui-this'); + if ($element.hasClass('layui-nav-item') && $element.hasClass('layui-this')) { + $(".layuimini-header-menu li").attr('class', 'layui-nav-item'); + } else { + addMenuClass($element.parent().parent(), 2); + } + } else { + $element.addClass('layui-nav-itemed'); + if ($element.hasClass('layui-nav-item') && $element.hasClass('layui-nav-itemed')) { + $(".layuimini-header-menu li").attr('class', 'layui-nav-item'); + } else { + addMenuClass($element.parent().parent(), 2); + } + } + }; + addMenuClass($(this).parent(), 1); + return false; + } + }); + }, + + /** + * 多模块切换 + * @param tabId + */ + listenSwitchMultiModule: function (tabId) { + $("[layuimini-href]").each(function () { + if ($(this).attr("layuimini-href") === tabId) { + + // 自动展开菜单栏 + var addMenuClass = function ($element, type) { + if (type === 1) { + $element.addClass('layui-this'); + if ($element.hasClass('layui-nav-item') && $element.hasClass('layui-this')) { + var moduleId = $element.parent().attr('id'); + $(".layuimini-header-menu li").attr('class', 'layui-nav-item'); + $("#" + moduleId + "HeaderId").addClass("layui-this"); + $(".layuimini-menu-left .layui-nav.layui-nav-tree").attr('class', 'layui-nav layui-nav-tree layui-hide'); + $("#" + moduleId).attr('class', 'layui-nav layui-nav-tree layui-this'); + } else { + addMenuClass($element.parent().parent(), 2); + } + } else { + $element.addClass('layui-nav-itemed'); + if ($element.hasClass('layui-nav-item') && $element.hasClass('layui-nav-itemed')) { + var moduleId = $element.parent().attr('id'); + $(".layuimini-header-menu li").attr('class', 'layui-nav-item'); + $("#" + moduleId + "HeaderId").addClass("layui-this"); + $(".layuimini-menu-left .layui-nav.layui-nav-tree").attr('class', 'layui-nav layui-nav-tree layui-hide'); + $("#" + moduleId).attr('class', 'layui-nav layui-nav-tree layui-this'); + } else { + addMenuClass($element.parent().parent(), 2); + } + } + }; + addMenuClass($(this).parent(), 1); + return false; + } + }); + }, + + /** + * 自动定位 + */ + rollPosition: function () { + var $tabTitle = $('.layuimini-tab .layui-tab-title'); + var autoLeft = 0; + $tabTitle.children("li").each(function () { + if ($(this).hasClass('layui-this')) { + return false; + } else { + autoLeft += $(this).outerWidth(); + } + }); + $tabTitle.animate({ + scrollLeft: autoLeft - $tabTitle.width() / 3 + }, 200); + }, + + /** + * 点击滚动 + * @param direction + */ + rollClick: function (direction) { + var $tabTitle = $('.layuimini-tab .layui-tab-title'); + var left = $tabTitle.scrollLeft(); + if ('left' === direction) { + $tabTitle.animate({ + scrollLeft: left - 450 + }, 200); + } else { + $tabTitle.animate({ + scrollLeft: left + 450 + }, 200); + } + }, + + /** + * 脱离标签栏 + * @param tabId + */ + divorced: function (tabId) { + let tabtitle = $("ul.layui-tab-title").children('li[lay-id="' + tabId + '"]'); + let title = tabtitle.children("span").text(); + let tab = $("div.layui-tab-item").children("iframe[id='" + tabId + "']"); + let id = tabId.replace(/[^\u4e00-\u9fa5\w]/g, ""); + layer.open({ + id: id, + title: title, + type: 1, + content: "", + shadeClose: false, + shade: 0, + maxmin: true, + area: ['50%', '80%'], + success: function (layero, index) { + //layero tab + tabtitle.hide(); + tabtitle.removeClass("layui-this"); + tab.parent("div.layui-tab-item").attr("layui-id", index); + tab.appendTo($(layero).children("div#" + id)); + //$(layero).children("div#" + id)[0].addendChild(tab[0]); + }, + cancel: function (index, layero) { + let iframe = $(layero).children("div#" + id).children("iframe"); + iframe.appendTo($("div.layui-tab-item[layui-id=" + index + "]")); + //$("div.layui-tab-item.layui-show")[0].addendChild(iframe[0]); + tabtitle.addClass("layui-this"); + tabtitle.show(); + }, + end: function () { + layer.msg("已关闭"); + } + }); + } + + }; + + exports("miniTab", miniTab); +}); \ No newline at end of file diff --git a/public/js/lay-module/layuimini/miniTheme.js b/public/js/lay-module/layuimini/miniTheme.js new file mode 100644 index 0000000..011fbc4 --- /dev/null +++ b/public/js/lay-module/layuimini/miniTheme.js @@ -0,0 +1,469 @@ +/** + * date:2020/02/28 + * author:Mr.Chung + * version:2.0 + * description:layuimini tab框架扩展 + */ +layui.define(["jquery", "layer"], function (exports) { + var $ = layui.$, + layer = layui.layer; + + var miniTheme = { + + /** + * 主题配置项 + * @param bgcolorId + * @returns {{headerLogo, menuLeftHover, headerRight, menuLeft, headerRightThis, menuLeftThis}|*|*[]} + */ + config: function (bgcolorId) { + var bgColorConfig = [ + { + headerRightBg: '#ffffff', //头部右侧背景色 + headerRightBgThis: '#e4e4e4', //头部右侧选中背景色, + headerRightColor: 'rgba(107, 107, 107, 0.7)', //头部右侧字体颜色, + headerRightChildColor: 'rgba(107, 107, 107, 0.7)', //头部右侧下拉字体颜色, + headerRightColorThis: '#565656', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(160, 160, 160, 0.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#247AE0', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#565656', //头部缩放按钮样式, + headerLogoBg: '#192027', //logo背景颜色, + headerLogoColor: 'rgb(191, 187, 187)', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#28333E', //左侧菜单背景, + leftMenuBgThis: '#247AE0', //左侧菜单选中背景, + leftMenuChildBg: '#0c0f13', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#247AE0', //tab选项卡选中颜色, + }, + { + headerRightBg: '#23262e', //头部右侧背景色 + headerRightBgThis: '#0c0c0c', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#1aa094', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#0c0c0c', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#23262e', //左侧菜单背景, + leftMenuBgThis: '#737373', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#23262e', //tab选项卡选中颜色, + }, + { + headerRightBg: '#ffa4d1', //头部右侧背景色 + headerRightBgThis: '#bf7b9d', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#ffa4d1', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#e694bd', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#1f1f1f', //左侧菜单背景, + leftMenuBgThis: '#737373', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#ffa4d1', //tab选项卡选中颜色, + }, + { + headerRightBg: '#1aa094', //头部右侧背景色 + headerRightBgThis: '#197971', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#1aa094', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#0c0c0c', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#23262e', //左侧菜单背景, + leftMenuBgThis: '#1aa094', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#1aa094', //tab选项卡选中颜色, + }, + { + headerRightBg: '#1e9fff', //头部右侧背景色 + headerRightBgThis: '#0069b7', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#1e9fff', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#0c0c0c', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#1f1f1f', //左侧菜单背景, + leftMenuBgThis: '#1e9fff', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#1e9fff', //tab选项卡选中颜色, + }, + { + headerRightBg: '#ffb800', //头部右侧背景色 + headerRightBgThis: '#d09600', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#d09600', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#243346', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#2f4056', //左侧菜单背景, + leftMenuBgThis: '#8593a7', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#ffb800', //tab选项卡选中颜色, + }, + { + headerRightBg: '#e82121', //头部右侧背景色 + headerRightBgThis: '#ae1919', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#ae1919', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#0c0c0c', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#1f1f1f', //左侧菜单背景, + leftMenuBgThis: '#3b3f4b', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#e82121', //tab选项卡选中颜色, + }, + { + headerRightBg: '#963885', //头部右侧背景色 + headerRightBgThis: '#772c6a', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#772c6a', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#243346', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#2f4056', //左侧菜单背景, + leftMenuBgThis: '#586473', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#963885', //tab选项卡选中颜色, + }, + { + headerRightBg: '#2D8CF0', //头部右侧背景色 + headerRightBgThis: '#0069b7', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#0069b7', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#0069b7', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#1f1f1f', //左侧菜单背景, + leftMenuBgThis: '#2D8CF0', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#2d8cf0', //tab选项卡选中颜色, + }, + { + headerRightBg: '#ffb800', //头部右侧背景色 + headerRightBgThis: '#d09600', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#d09600', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#d09600', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#2f4056', //左侧菜单背景, + leftMenuBgThis: '#3b3f4b', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#ffb800', //tab选项卡选中颜色, + }, + { + headerRightBg: '#e82121', //头部右侧背景色 + headerRightBgThis: '#ae1919', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#ae1919', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#d91f1f', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#1f1f1f', //左侧菜单背景, + leftMenuBgThis: '#3b3f4b', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#e82121', //tab选项卡选中颜色, + }, + { + headerRightBg: '#963885', //头部右侧背景色 + headerRightBgThis: '#772c6a', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#772c6a', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#772c6a', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#2f4056', //左侧菜单背景, + leftMenuBgThis: '#626f7f', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#963885', //tab选项卡选中颜色, + } + ]; + if (bgcolorId === undefined) { + return bgColorConfig; + } else { + return bgColorConfig[bgcolorId]; + } + }, + + /** + * 初始化 + * @param options + */ + render: function (options) { + options.bgColorDefault = options.bgColorDefault || false; + options.listen = options.listen || false; + var bgcolorId = sessionStorage.getItem('layuiminiBgcolorId'); + if (bgcolorId === null || bgcolorId === undefined || bgcolorId === '') { + bgcolorId = options.bgColorDefault; + } + miniTheme.buildThemeCss(bgcolorId); + if (options.listen) miniTheme.listen(options); + }, + + /** + * 构建主题样式 + * @param bgcolorId + * @returns {boolean} + */ + buildThemeCss: function (bgcolorId) { + if (!bgcolorId) { + return false; + } + var bgcolorData = miniTheme.config(bgcolorId); + var styleHtml = '/*头部右侧背景色 headerRightBg */\n' + + '.layui-layout-admin .layui-header {\n' + + ' background-color: ' + bgcolorData.headerRightBg + ' !important;\n' + + '}\n' + + '\n' + + '/*头部右侧选中背景色 headerRightBgThis */\n' + + '.layui-layout-admin .layui-header .layuimini-header-content > ul > .layui-nav-item.layui-this, .layuimini-tool i:hover {\n' + + ' background-color: ' + bgcolorData.headerRightBgThis + ' !important;\n' + + '}\n' + + '\n' + + '/*头部右侧字体颜色 headerRightColor */\n' + + '.layui-layout-admin .layui-header .layui-nav .layui-nav-item a {\n' + + ' color: ' + bgcolorData.headerRightColor + ';\n' + + '}\n' + + '/**头部右侧下拉字体颜色 headerRightChildColor */\n' + + '.layui-layout-admin .layui-header .layui-nav .layui-nav-item .layui-nav-child a {\n' + + ' color: ' + bgcolorData.headerRightChildColor + '!important;\n' + + '}\n'+ + '\n' + + '/*头部右侧鼠标选中 headerRightColorThis */\n' + + '.layui-header .layuimini-menu-header-pc.layui-nav .layui-nav-item a:hover, .layui-header .layuimini-header-menu.layuimini-pc-show.layui-nav .layui-this a {\n' + + ' color: ' + bgcolorData.headerRightColorThis + ' !important;\n' + + '}\n' + + '\n' + + '/*头部右侧更多下拉颜色 headerRightNavMore */\n' + + '.layui-header .layui-nav .layui-nav-more {\n' + + ' border-top-color: ' + bgcolorData.headerRightNavMore + ' !important;\n' + + '}\n' + + '\n' + + '/*头部右侧更多下拉颜色 headerRightNavMore */\n' + + '.layui-header .layui-nav .layui-nav-mored, .layui-header .layui-nav-itemed > a .layui-nav-more {\n' + + ' border-color: transparent transparent ' + bgcolorData.headerRightNavMore + ' !important;\n' + + '}\n' + + '\n' + + '/**头部右侧更多下拉配置色 headerRightNavMoreBg headerRightNavMoreColor */\n' + + '.layui-header .layui-nav .layui-nav-child dd.layui-this a, .layui-header .layui-nav-child dd.layui-this, .layui-layout-admin .layui-header .layui-nav .layui-nav-item .layui-nav-child .layui-this a {\n' + + ' background-color: ' + bgcolorData.headerRightNavMoreBg + ' !important;\n' + + ' color:' + bgcolorData.headerRightNavMoreColor + ' !important;\n' + + '}\n' + + '\n' + + '/*头部缩放按钮样式 headerRightToolColor */\n' + + '.layui-layout-admin .layui-header .layuimini-tool i {\n' + + ' color: ' + bgcolorData.headerRightToolColor + ';\n' + + '}\n' + + '\n' + + '/*logo背景颜色 headerLogoBg */\n' + + '.layui-layout-admin .layuimini-logo {\n' + + ' background-color: ' + bgcolorData.headerLogoBg + ' !important;\n' + + '}\n' + + '\n' + + '/*logo字体颜色 headerLogoColor */\n' + + '.layui-layout-admin .layuimini-logo h1 {\n' + + ' color: ' + bgcolorData.headerLogoColor + ';\n' + + '}\n' + + '\n' + + '/*左侧菜单更多下拉样式 leftMenuNavMore */\n' + + '.layuimini-menu-left .layui-nav .layui-nav-more,.layuimini-menu-left-zoom.layui-nav .layui-nav-more {\n' + + ' border-top-color: ' + bgcolorData.leftMenuNavMore + ';\n' + + '}\n' + + '\n' + + '/*左侧菜单更多下拉样式 leftMenuNavMore */\n' + + '.layuimini-menu-left .layui-nav .layui-nav-mored, .layuimini-menu-left .layui-nav-itemed > a .layui-nav-more, .layuimini-menu-left-zoom.layui-nav .layui-nav-mored, .layuimini-menu-left-zoom.layui-nav-itemed > a .layui-nav-more {\n' + + ' border-color: transparent transparent ' + bgcolorData.leftMenuNavMore + ' !important;\n' + + '}\n' + + '\n' + + '/*左侧菜单背景 leftMenuBg */\n' + + '.layui-side.layui-bg-black, .layui-side.layui-bg-black > .layuimini-menu-left > ul, .layuimini-menu-left-zoom > ul {\n' + + ' background-color: ' + bgcolorData.leftMenuBg + ' !important;\n' + + '}\n' + + '\n' + + '/*左侧菜单选中背景 leftMenuBgThis */\n' + + '.layuimini-menu-left .layui-nav-tree .layui-this, .layuimini-menu-left .layui-nav-tree .layui-this > a, .layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this, .layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this a, .layuimini-menu-left-zoom.layui-nav-tree .layui-this, .layuimini-menu-left-zoom.layui-nav-tree .layui-this > a, .layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this, .layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this a {\n' + + ' background-color: ' + bgcolorData.leftMenuBgThis + ' !important\n' + + '}\n' + + '\n' + + '/*左侧菜单子菜单背景 leftMenuChildBg */\n' + + '.layuimini-menu-left .layui-nav-itemed > .layui-nav-child{\n' + + ' background-color: ' + bgcolorData.leftMenuChildBg + ' !important;\n' + + '}\n' + + '\n' + + '/*左侧菜单字体颜色 leftMenuColor */\n' + + '.layuimini-menu-left .layui-nav .layui-nav-item a, .layuimini-menu-left-zoom.layui-nav .layui-nav-item a {\n' + + ' color: ' + bgcolorData.leftMenuColor + ' !important;\n' + + '}\n' + + '\n' + + '/*左侧菜单选中字体颜色 leftMenuColorThis */\n' + + '.layuimini-menu-left .layui-nav .layui-nav-item a:hover, .layuimini-menu-left .layui-nav .layui-this a, .layuimini-menu-left-zoom.layui-nav .layui-nav-item a:hover, .layuimini-menu-left-zoom.layui-nav .layui-this a {\n' + + ' color:' + bgcolorData.leftMenuColorThis + ' !important;\n' + + '}\n' + + '\n' + + '/**tab选项卡选中颜色 tabActiveColor */\n' + + '.layuimini-tab .layui-tab-title .layui-this .layuimini-tab-active {\n' + + ' background-color: ' + bgcolorData.tabActiveColor + ';\n' + + '}\n'; + $('#layuimini-bg-color').html(styleHtml); + }, + + /** + * 构建主题选择html + * @param options + * @returns {string} + */ + buildBgColorHtml: function (options) { + options.bgColorDefault = options.bgColorDefault || 0; + var bgcolorId = parseInt(sessionStorage.getItem('layuiminiBgcolorId')); + if (isNaN(bgcolorId)) bgcolorId = options.bgColorDefault; + var bgColorConfig = miniTheme.config(); + var html = ''; + $.each(bgColorConfig, function (key, val) { + if (key === bgcolorId) { + html += '
        • \n'; + } else { + html += '
        • \n'; + } + html += '\n' + + '
          \n' + + '
          \n' + + '
          \n' + + '
        • '; + }); + return html; + }, + + /** + * 监听 + * @param options + */ + listen: function (options) { + $('body').on('click', '[data-bgcolor]', function () { + var loading = layer.load(0, {shade: false, time: 2 * 1000}); + var clientHeight = (document.documentElement.clientHeight) - 60; + var bgColorHtml = miniTheme.buildBgColorHtml(options); + var html = '
          \n' + + '
          \n' + + '配色方案\n' + + '
          \n' + + '
          \n' + + '
            \n' + bgColorHtml + '
          \n' + + '
          \n' + + '
          '; + layer.open({ + type: 1, + title: false, + closeBtn: 0, + shade: 0.2, + anim: 2, + shadeClose: true, + id: 'layuiminiBgColor', + area: ['340px', clientHeight + 'px'], + offset: 'rb', + content: html, + success: function (index, layero) { + }, + end: function () { + $('.layuimini-select-bgcolor').removeClass('layui-this'); + } + }); + layer.close(loading); + }); + + $('body').on('click', '[data-select-bgcolor]', function () { + var bgcolorId = $(this).attr('data-select-bgcolor'); + $('.layuimini-color .color-content ul .layui-this').attr('class', ''); + $(this).attr('class', 'layui-this'); + sessionStorage.setItem('layuiminiBgcolorId', bgcolorId); + miniTheme.render({ + bgColorDefault: bgcolorId, + listen: false, + }); + }); + } + }; + + exports("miniTheme", miniTheme); + +}) +; \ No newline at end of file diff --git a/public/js/lay-module/layuimini/xphp.js b/public/js/lay-module/layuimini/xphp.js new file mode 100644 index 0000000..b6f060e --- /dev/null +++ b/public/js/lay-module/layuimini/xphp.js @@ -0,0 +1,248 @@ +/** + * date:2020/02/27 + * author:Mr.Chung + * version:2.0 + * description:layuimini 主体框架扩展 + */ +layui.define(["jquery","element", "miniTab"], function (exports) { + var $ = layui.$, + layer = layui.layer, + dropdown = layui.dropdown, + element = layui.element , + miniTab = layui.miniTab; + + if (!/http(s*):\/\//.test(location.href)) { + var tips = "请先将项目部署至web容器(Apache/Tomcat/Nginx/IIS/等),否则部分数据将无法显示"; + return layer.alert(tips); + } + + var xphp = { + + /** + * 后台框架初始化 + * @param options.iniUrl 后台初始化接口地址 + * @param options.urlHashLocation URL地址hash定位 + * @param options.bgColorDefault 默认皮肤 + * @param options.multiModule 是否开启多模块 + * @param options.menuChildOpen 是否展开子菜单 + * @param options.loadingTime 初始化加载时间 + * @param options.pageAnim iframe窗口动画 + * @param options.maxTabNum 最大的tab打开数量 + */ + render: function (options) { + options.iniUrl = options.iniUrl || null; + options.urlHashLocation = options.urlHashLocation || false; + options.bgColorDefault = options.bgColorDefault || 0; + options.multiModule = options.multiModule || false; + options.menuChildOpen = options.menuChildOpen || false; + options.loadingTime = options.loadingTime || 0; + options.pageAnim = options.pageAnim || false; + options.maxTabNum = options.maxTabNum || 200; + options.isHideOpenMenu = !options.isHideOpenMenu ? options.isHideOpenMenu : true; + $.getJSON(options.iniUrl, function (data) { + if (data == null) { + xphp.error('暂无菜单信息') + } else { + xphp.renderHome(data.homeInfo); + xphp.renderAnim(options.pageAnim); + xphp.listen(); + dropdown.render({ + elem: '.menuitemon', + data:data.menuInfo, + customName: { + id: 'id', + title: 'title', + children: 'children' + }, + trigger: 'hover', + click: function(obj){ + miniTab.openNewTab({...obj,href:obj.dizhi}); + } + }); + //tab页 + miniTab.render({ + filter: 'layuiminiTab', + urlHashLocation: options.urlHashLocation, + multiModule: options.multiModule, + menuChildOpen: options.menuChildOpen, + maxTabNum: options.maxTabNum, + menuList: data.menuInfo, + homeInfo: data.homeInfo, + switchtoindex:options.switchtoindex||false, + listenSwichCallback: function () { + xphp.renderDevice(); + } + }); + xphp.deleteLoader(options.loadingTime); + xphp.isHideOpenMenu(options.isHideOpenMenu); + } + }).fail(function () { + xphp.error('菜单接口有误'); + }); + }, + + /** + * 点击左侧下拉菜单,关闭其他下拉菜单 + * @param data + */ + isHideOpenMenu: function (data) { + $(".layui-side").on("click", ".layui-nav-item", function () { + if (data) { + $(this).siblings('li').attr('class', 'layui-nav-item'); + } + }); + }, + + /** + * 初始化首页 + * @param data + */ + renderHome: function (data) { + sessionStorage.setItem('layuiminiHomeHref', data.href); + $('#layuiminiHomeTabId').html('' + data.title + ''); + $('#layuiminiHomeTabId').attr('lay-id', data.id); + $('#layuiminiHomeTabIframe').html(''); + }, + + /** + * 初始化iframe窗口动画 + * @param anim + */ + renderAnim: function (anim) { + if (anim) { + $('#layuimini-bg-color').after(''); + } + }, + /** + * 初始化设备端 + */ + renderDevice: function () { + if (xphp.checkMobile()) { + $('.layuimini-tool i').attr('data-side-fold', 1); + $('.layuimini-tool i').attr('class', 'layui-icon layui-icon-list'); + $('.layui-layout-body').removeClass('layuimini-mini'); + $('.layui-layout-body').addClass('layuimini-all'); + } + }, + + + /** + * 初始化加载时间 + * @param loadingTime + */ + deleteLoader: function (loadingTime) { + if (loadingTime) { + setTimeout(function () { + $('.layuimini-loader').fadeOut(); + }, loadingTime * 1000) + } else { + $('.layuimini-loader').fadeOut(); + } + }, + + /** + * 成功 + * @param title + * @returns {*} + */ + success: function (title) { + return layer.msg(title, {icon: 1, shade: this.shade, scrollbar: false, time: 2000, shadeClose: true}); + }, + + /** + * 失败 + * @param title + * @returns {*} + */ + error: function (title) { + return layer.msg(title, {icon: 2, shade: this.shade, scrollbar: false, time: 3000, shadeClose: true}); + }, + + /** + * 判断是否为手机 + * @returns {boolean} + */ + checkMobile: function () { + var ua = navigator.userAgent.toLocaleLowerCase(); + var pf = navigator.platform.toLocaleLowerCase(); + var isAndroid = (/android/i).test(ua) || ((/iPhone|iPod|iPad/i).test(ua) && (/linux/i).test(pf)) + || (/ucweb.*linux/i.test(ua)); + var isIOS = (/iPhone|iPod|iPad/i).test(ua) && !isAndroid; + var isWinPhone = (/Windows Phone|ZuneWP7/i).test(ua); + var clientWidth = document.documentElement.clientWidth; + if (!isAndroid && !isIOS && !isWinPhone && clientWidth > 1024) { + return false; + } else { + return true; + } + }, + + /** + * 监听 + */ + listen: function () { + + /** + * 监听提示信息 + */ + $("body").on("mouseenter", ".layui-nav-tree .menu-li", function () { + if (xphp.checkMobile()) { + return false; + } + var classInfo = $(this).attr('class'), + tips = $(this).prop("innerHTML"), + isShow = $('.layuimini-tool i').attr('data-side-fold'); + if (isShow == 0 && tips) { + tips = "
          • "+tips+"
          " ; + window.openTips = layer.tips(tips, $(this), { + tips: [2, '#2f4056'], + time: 300000, + skin:"popup-tips", + success:function (el) { + var left = $(el).position().left - 10 ; + $(el).css({ left:left }); + element.render(); + } + }); + } + }); + + $("body").on("mouseleave", ".popup-tips", function () { + if (xphp.checkMobile()) { + return false; + } + var isShow = $('.layuimini-tool i').attr('data-side-fold'); + if (isShow == 0) { + try { + layer.close(window.openTips); + } catch (e) { + console.log(e.message); + } + } + }); + /** + * 点击遮罩层 + */ + $('body').on('click', '.layuimini-make', function () { + xphp.renderDevice(); + }); + } + }; + + + exports("xphp", xphp); +}); diff --git a/public/js/lay-module/location/img/location.cur b/public/js/lay-module/location/img/location.cur new file mode 100644 index 0000000..e2aadfd Binary files /dev/null and b/public/js/lay-module/location/img/location.cur differ diff --git a/public/js/lay-module/location/img/location_blue.cur b/public/js/lay-module/location/img/location_blue.cur new file mode 100644 index 0000000..61f103b Binary files /dev/null and b/public/js/lay-module/location/img/location_blue.cur differ diff --git a/public/js/lay-module/location/location.css b/public/js/lay-module/location/location.css new file mode 100644 index 0000000..4c4101d --- /dev/null +++ b/public/js/lay-module/location/location.css @@ -0,0 +1,101 @@ +.ew-map-select-tool { + padding: 5px 15px; + box-shadow: 0 1px 0 0 rgba(0, 0, 0, .05); +} +.inline-block { + display: inline-block; +} +.layui-btn.icon-btn { + padding: 0 10px; +} + +.pull-right { + float: right; +} + +.map-select:after, .map-select:before { + content: ''; + display: block; + clear: both; +} + +.ew-map-select-poi { + height: 505px; + width: 250px; + overflow-x: hidden; + overflow-y: auto; + float: left; + position: relative; + display: block; + box-sizing: border-box; +} + +.ew-map-select-search-list-item { + padding: 10px 30px 10px 15px; + border-bottom: 1px solid #e8e8e8; + cursor: pointer; + position: relative; +} + +.ew-map-select-search-list-item .ew-map-select-search-list-item-title { + font-size: 14px; + color: #262626; +} + +.ew-map-select-search-list-item .ew-map-select-search-list-item-address { + font-size: 12px; + color: #595959; + padding-top: 5px; +} + +.ew-map-select-search-list-item-icon-ok { + position: absolute; + right: 15px; + top: 50%; + transform: translateY(-50%); +} + +#ew-map-select-tips { + position: absolute; + z-index: 999; + background: #fff; + max-height: 430px; + overflow: auto; + top: 48px; + left: 56px; + width: 280px; + box-shadow: 0 2px 4px rgba(0,0,0,.12); + border: 1px solid #d2d2d2; +} + +#ew-map-select-tips .ew-map-select-search-list-item { + padding: 10px 15px 10px 35px; +} + +.ew-map-select-search-list-item-icon-search { + position: absolute; + left: 10px; + top: 50%; + transform: translateY(-50%); +} + +.ew-map-select-search-list-item .ew-map-select-search-list-item-title { + font-size: 14px; + color: #262626; +} + +.ew-map-select-search-list-item .ew-map-select-search-list-item-address { + font-size: 12px; + color: #595959; + padding-top: 5px; +} + +.cur-load0 { + display: none; + background: url('./img/location.cur'); +} + +.cur-load1 { + display: none; + background: url('./img/location_blue.cur'); +} diff --git a/public/js/lay-module/location/location.js b/public/js/lay-module/location/location.js new file mode 100644 index 0000000..85c1966 --- /dev/null +++ b/public/js/lay-module/location/location.js @@ -0,0 +1,759 @@ +layui.define(['layer', 'locationX'], function (exports) { + var $ = layui.jquery, + layer = layui.layer, + MOD_NAME = "location", + GPS = layui.locationX; + + + var tpl0 = '
          \n' + + '
          \n' + + '
          \n' + + ' 经度:\n' + + '    纬度:\n' + + ' \n' + + '
          \n' + + '
          '; + + var tpl1 ='
          \n' + + '
          \n' + + '
          \n' + + ' 搜索:\n' + + '
          \n' + + '   经度:\n' + + '    纬度:\n' + + ' \n' + + '
          \n' + + '
          \n' + + '\n' + + '
          \n' + + '
          \n' + + '
          \n' + + '\n' + + '
          '; + + + var obj = function (config) { + + this.config = { + // 默认中心点位置是北京天安门,所有坐标系都用此坐标,偏的不大 + type: 0, // 0 : 仅定位 1: 带有搜索的定位 + longitude: 116.404, + latitude: 39.915, + title: '定位', + zoom: 18, + apiType: "baiduMap", + coordinate: "baiduMap", + mapType: 0, + searchKey: '村', + init: function () { + return {longitude: 116.404, latitude: 39.915}; + }, + success: function () { + + }, + onClickTip: function (data) { + console.log(data); + } + + } + + this.config = $.extend(this.config, config); + + // 初始化经纬度信息 + var initData = this.config.init(); + this.config.longitude = initData.longitude; + this.config.latitude = initData.latitude; + + this.lng = this.config.longitude; + this.lat = this.config.latitude; + // 转换初始坐标 + this.initCoordinate = function (lng, lat) { + var o = this; + if (o.config.apiType == o.config.coordinate) { + return {lng: lng, lat: lat}; + } else if (o.config.apiType == 'baiduMap' && o.config.coordinate == 'tiandiMap') { + var res = GPS.WGS84_bd(lat, lng); + return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)}; + } else if (o.config.apiType == 'tiandiMap' && o.config.coordinate == 'baiduMap') { + var res = GPS.bd_WGS84(lat, lng); + return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)}; + } else if (o.config.apiType == 'gaodeMap' && o.config.coordinate == 'baiduMap') { + var res = GPS.bd_decrypt(lat, lng); + return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)}; + } else if (o.config.apiType == 'baiduMap' && o.config.coordinate == 'gaodeMap') { + var res = GPS.bd_encrypt(lat, lng); + return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)}; + } else if (o.config.apiType == 'gaodeMap' && o.config.coordinate == 'tiandiMap') { + var res = GPS.gcj_encrypt(lat, lng); + return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)}; + } else if (o.config.apiType == 'tiandiMap' && o.config.coordinate == 'gaodeMap') { + var res = GPS.gcj_decrypt(lat, lng); + return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)}; + } + } + + if (this.config.longitude && this.config.latitude && this.config.mapType != this.config.coordinate) { + var tbd = this.initCoordinate(this.config.longitude, this.config.latitude); + this.config.longitude = tbd.lng; + this.config.latitude = tbd.lat; + } + + + this.transformCoordinate = function (lng, lat) { + var o = this; + if (o.config.apiType == o.config.coordinate) { + return {lng: lng, lat: lat}; + } else if (o.config.apiType == 'baiduMap' && o.config.coordinate == 'tiandiMap') { + var res = GPS.bd_WGS84(lat, lng); + return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)}; + } else if (o.config.apiType == 'tiandiMap' && o.config.coordinate == 'baiduMap') { + var res = GPS.WGS84_bd(lat, lng); + return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)}; + } else if (o.config.apiType == 'gaodeMap' && o.config.coordinate == 'baiduMap') { + var res = GPS.bd_encrypt(lat, lng); + return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)}; + } else if (o.config.apiType == 'baiduMap' && o.config.coordinate == 'gaodeMap') { + var res = GPS.bd_decrypt(lat, lng); + return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)}; + } else if (o.config.apiType == 'gaodeMap' && o.config.coordinate == 'tiandiMap') { + var res = GPS.gcj_decrypt(lat, lng); + return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)}; + } else if (o.config.apiType == 'tiandiMap' && o.config.coordinate == 'gaodeMap') { + var res = GPS.gcj_encrypt(lat, lng); + return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)}; + } + } + + this.openBaiduMap = function () { + var o = this; + var map; // 创建地图实例 + if (o.config.mapType == 1) { + map = new BMap.Map("map", {enableMapClick: false, mapType: BMAP_SATELLITE_MAP}); + } else if (o.config.mapType == 2) { + map = new BMap.Map("map", {enableMapClick: false, mapType: BMAP_HYBRID_MAP}); + } else { + map = new BMap.Map("map", {enableMapClick: false, mapType: BMAP_NORMAL_MAP}); + } + map.enableScrollWheelZoom(); //启用滚轮放大缩小,默认禁用 + var point = new BMap.Point(o.config.longitude ? o.config.longitude : 116.404, o.config.latitude ? o.config.latitude : 39.915); // 创建点坐标 + map.centerAndZoom(point, o.config.zoom); + map.setDefaultCursor("url('" + layui.cache.base + "location/img/location.cur') 17 35,auto"); //设置地图默认的鼠标指针样式 + var marker = new BMap.Marker(map.getCenter()); // 创建标注 + map.addOverlay(marker); // 将标注添加到地图中 + map.addEventListener("click", function (e) { + var tbd = o.transformCoordinate(e.point.lng, e.point.lat); + //显示经纬度 + $("#lng").val(tbd.lng); + $("#lat").val(tbd.lat); + o.lng = tbd.lng; + o.lat = tbd.lat; + var point = new BMap.Point(e.point.lng, e.point.lat); + map.removeOverlay(marker); + marker = new BMap.Marker(point); + map.addOverlay(marker); + + if (o.config.type==1){ + searchNearBy(e.point.lng, e.point.lat); + } + }); + + // 标记中心点 + var markCenter = function (lng, lat){ + var tbd = o.transformCoordinate(lng, lat); + //显示经纬度 + $("#lng").val(tbd.lng); + $("#lat").val(tbd.lat); + o.lng = tbd.lng; + o.lat = tbd.lat; + var point = new BMap.Point(lng, lat); + map.removeOverlay(marker); + marker = new BMap.Marker(point); + map.addOverlay(marker); + if (o.config.type==1){ + searchNearBy(lng, lat); + } + + } + + // 搜索附近方法 + var searchNearBy = function (lng, lat){ + var point = new BMap.Point(lng, lat); + var localSearch = new BMap.LocalSearch(point, { + pageCapacity: 10, + onSearchComplete: function (result){ + var htmlList = ''; + $.each(result,function (i,val){ + $.each(val.Hr,function (i,ad){ + htmlList += '
          '; + htmlList += '
          ' + ad.title + '
          '; + htmlList += '
          ' + ad.address + '
          '; + htmlList += '
          '; + htmlList += '
          '; + }); + }); + $('#ew-map-select-poi').html(htmlList); + } + }); + localSearch.searchNearby([o.config.searchKey,'镇','街道','店'],point,1000); + } + + // 初始化搜索 + if (o.config.type==1){ + o.initBaiduSearch(map,searchNearBy,markCenter); + } + + } + + this.initBaiduSearch = function (map,searchNearBy,markCenter){ + var o = this; + + searchNearBy(o.config.longitude ? o.config.longitude : 116.404, o.config.latitude ? o.config.latitude : 39.915); + + // poi列表点击事件 + $('#ew-map-select-poi').off('click').on('click', '.ew-map-select-search-list-item', function () { + $('#ew-map-select-tips').addClass('layui-hide'); + $('#ew-map-select-poi .ew-map-select-search-list-item-icon-ok').addClass('layui-hide'); + $(this).find('.ew-map-select-search-list-item-icon-ok').removeClass('layui-hide'); + $('#ew-map-select-center-img').removeClass('bounceInDown'); + setTimeout(function () { + $('#ew-map-select-center-img').addClass('bounceInDown'); + }); + var lng = $(this).data('lng'); + var lat = $(this).data('lat'); + + // + var point = new BMap.Point(lng, lat); + map.centerAndZoom(point, map.getZoom()); + + markCenter(lng, lat); + var title = $(this).data('title'); + var address = $(this).data('address'); + o.config.onClickTip({title:title,address:address,lng:lng,lat:lat}); + }); + + // 搜索提示 + var $inputSearch = $('#ew-map-select-input-search'); + $inputSearch.off('input').on('input', function () { + var keywords = $(this).val(); + var $selectTips = $('#ew-map-select-tips'); + if (!keywords) { + $selectTips.html(''); + $selectTips.addClass('layui-hide'); + } + + var autoComplete = new BMap.LocalSearch('全国', { + pageCapacity: 10, + onSearchComplete: function (result){ + if (undefined == result){ + return ; + } + var htmlList = ''; + $.each(result.Hr,function (i,ad){ + htmlList += '
          '; + htmlList += ' '; + htmlList += '
          ' + ad.title + '
          '; + htmlList += '
          ' + ad.address + '
          '; + htmlList += '
          '; + }); + $selectTips.html(htmlList); + if (result.Hr.length === 0) $('#ew-map-select-tips').addClass('layui-hide'); + else $('#ew-map-select-tips').removeClass('layui-hide'); + } + }); + autoComplete.search(keywords); + + }); + $inputSearch.off('blur').on('blur', function () { + var keywords = $(this).val(); + var $selectTips = $('#ew-map-select-tips'); + if (!keywords) { + $selectTips.html(''); + $selectTips.addClass('layui-hide'); + } + }); + $inputSearch.off('focus').on('focus', function () { + var keywords = $(this).val(); + if (keywords) $('#ew-map-select-tips').removeClass('layui-hide'); + }); + // tips列表点击事件 + $('#ew-map-select-tips').off('click').on('click', '.ew-map-select-search-list-item', function () { + $('#ew-map-select-tips').addClass('layui-hide'); + var lng = $(this).data('lng'); + var lat = $(this).data('lat'); + var point = new BMap.Point(lng, lat); + map.centerAndZoom(point, map.getZoom()); + markCenter(lng, lat); + var title = $(this).data('title'); + var address = $(this).data('address'); + o.config.onClickTip({title:title,address:address,lng:lng,lat:lat}); + }); + } + + this.openTiandiMap = function () { + var o = this; + var map = new T.Map("map"); // 创建地图实例 + if (o.config.mapType == 1) { + map.setMapType(TMAP_SATELLITE_MAP); + } else if (o.config.mapType == 2) { + map.setMapType(TMAP_HYBRID_MAP); + } else { + map.setMapType(TMAP_NORMAL_MAP); + } + map.enableScrollWheelZoom(true); // 开启鼠标滚轮缩放 + var latLng = new T.LngLat(o.config.longitude ? o.config.longitude : 116.404, o.config.latitude ? o.config.latitude : 39.915); + + map.centerAndZoom(latLng, o.config.zoom); + + var marker = new T.Marker(latLng); // 创建标注 + map.addOverLay(marker);// 将标注添加到地图中 + + if (undefined === window.T.MarkTool) { + setTimeout(function () { + initMarkerTool(); + }, 200); + } else { + initMarkerTool(); + } + + function initMarkerTool() { + var markerTool = new T.MarkTool(map, {follow: true}); + markerTool.open(); + /*标注事件*/ + var mark = function (e) { + $.each(map.getOverlays(), function (i, marker) { + if (marker != e.currentMarker) { + map.removeOverLay(marker); + } + }) + //显示经纬度 + var tbd = o.transformCoordinate(e.currentLnglat.getLng(), e.currentLnglat.getLat()); + $("#lng").val(tbd.lng); + $("#lat").val(tbd.lat); + o.lng = tbd.lng; + o.lat = tbd.lat; + markerTool = new T.MarkTool(map, {follow: true}); + markerTool.open(); + markerTool.addEventListener("mouseup", mark); + + if (o.config.type==1){ + searchNearBy(e.currentLnglat.getLng(), e.currentLnglat.getLat()); + } + } + //绑定mouseup事件 在用户每完成一次标注时触发事件。 + markerTool.addEventListener("mouseup", mark); + } + + // 标记中心点 + var markCenter = function (lng, lat) { + $.each(map.getOverlays(), function (i, marker) { + map.removeOverLay(marker); + }) + //显示经纬度 + var tbd = o.transformCoordinate(lng, lat); + $("#lng").val(tbd.lng); + $("#lat").val(tbd.lat); + o.lng = tbd.lng; + o.lat = tbd.lat; + var latLng = new T.LngLat(lng, lat); + var marker = new T.Marker(latLng); // 创建标注 + map.addOverLay(marker);// 将标注添加到地图中 + if (o.config.type==1){ + searchNearBy(lng, lat); + } + + } + + // 搜索附近方法 + var searchNearBy = function (lng, lat){ + var point = new T.LngLat(lng,lat); + var localSearch = new T.LocalSearch(map, { + pageCapacity: 10, + onSearchComplete: function (result){ + var htmlList = ''; + $.each(result.getPois(),function (i,ad){ + var lnglat = ad.lonlat.split(" "); + htmlList += '
          '; + htmlList += '
          ' + ad.name + '
          '; + htmlList += '
          ' + ad.address + '
          '; + htmlList += '
          '; + htmlList += '
          '; + }); + $('#ew-map-select-poi').html(htmlList); + } + }); + localSearch.setQueryType(1); + localSearch.searchNearby(o.config.searchKey,point,1000); + } + + if (o.config.type==1){ + o.initTiandiSearch(map,searchNearBy,markCenter); + } + + } + + this.initTiandiSearch = function (map,searchNearBy,markCenter){ + var o = this; + searchNearBy(o.config.longitude ? o.config.longitude : 116.404, o.config.latitude ? o.config.latitude : 39.915); + + // poi列表点击事件 + $('#ew-map-select-poi').off('click').on('click', '.ew-map-select-search-list-item', function () { + $('#ew-map-select-tips').addClass('layui-hide'); + $('#ew-map-select-poi .ew-map-select-search-list-item-icon-ok').addClass('layui-hide'); + $(this).find('.ew-map-select-search-list-item-icon-ok').removeClass('layui-hide'); + $('#ew-map-select-center-img').removeClass('bounceInDown'); + setTimeout(function () { + $('#ew-map-select-center-img').addClass('bounceInDown'); + }); + var lng = $(this).data('lng'); + var lat = $(this).data('lat'); + + // + var point = new T.LngLat(lng, lat); + map.centerAndZoom(point, map.getZoom()); + + markCenter(lng, lat); + var title = $(this).data('title'); + var address = $(this).data('address'); + o.config.onClickTip({title:title,address:address,lng:lng,lat:lat}); + }); + + // 搜索提示 + var $inputSearch = $('#ew-map-select-input-search'); + $inputSearch.off('input').on('input', function () { + var keywords = $(this).val(); + var $selectTips = $('#ew-map-select-tips'); + if (!keywords) { + $selectTips.html(''); + $selectTips.addClass('layui-hide'); + } + + var autoComplete = new T.LocalSearch(map, { + pageCapacity: 10, + onSearchComplete: function (result){ + if (undefined == result){ + return ; + } + var htmlList = ''; + $.each(result.getPois(),function (i,ad){ + var lnglat = ad.lonlat.split(" "); + htmlList += '
          '; + htmlList += '
          ' + ad.name + '
          '; + htmlList += '
          ' + ad.address + '
          '; + htmlList += '
          '; + htmlList += '
          '; + }); + $selectTips.html(htmlList); + if (result.getPois().length === 0) $('#ew-map-select-tips').addClass('layui-hide'); + else $('#ew-map-select-tips').removeClass('layui-hide'); + } + }); + autoComplete.setQueryType(1); + autoComplete.search(keywords); + + }); + $inputSearch.off('blur').on('blur', function () { + var keywords = $(this).val(); + var $selectTips = $('#ew-map-select-tips'); + if (!keywords) { + $selectTips.html(''); + $selectTips.addClass('layui-hide'); + } + }); + $inputSearch.off('focus').on('focus', function () { + var keywords = $(this).val(); + if (keywords) $('#ew-map-select-tips').removeClass('layui-hide'); + }); + // tips列表点击事件 + $('#ew-map-select-tips').off('click').on('click', '.ew-map-select-search-list-item', function () { + $('#ew-map-select-tips').addClass('layui-hide'); + var lng = $(this).data('lng'); + var lat = $(this).data('lat'); + var point = new T.LngLat(lng, lat); + map.centerAndZoom(point, map.getZoom()); + markCenter(lng, lat); + var title = $(this).data('title'); + var address = $(this).data('address'); + o.config.onClickTip({title:title,address:address,lng:lng,lat:lat}); + }); + } + + this.openGaodeMap = function () { + var o = this; + // 创建地图实例 + var layers = []; + if (o.config.mapType == '1') { + var satellite = new AMap.TileLayer.Satellite(); + layers.push(satellite); + } else if (o.config.mapType == '2') { + var satellite = new AMap.TileLayer.Satellite(); + var roadNet = new AMap.TileLayer.RoadNet(); + layers.push(satellite); + layers.push(roadNet); + } else { + var layer = new AMap.TileLayer(); + layers.push(layer); + } + var map = new AMap.Map("map", + { + resizeEnable: true, + zoom: o.config.zoom, + center: [o.config.longitude ? o.config.longitude : 116.404, o.config.latitude ? o.config.latitude : 39.915], + layers: layers + }); + map.setDefaultCursor("url('" + layui.cache.base + "location/img/location_blue.cur') 17 35,auto"); + + // 初始化中间点标记 + var marker = new AMap.Marker({ + icon: "https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png", + position: [o.config.longitude ? o.config.longitude : 116.404, o.config.latitude ? o.config.latitude : 39.915] + }); + map.add(marker); + var markCenter = function (e) { + // 标记中心点 + map.clearMap(); + // alert('您在[ '+e.lnglat.getLng()+','+e.lnglat.getLat()+' ]的位置点击了地图!'); + //显示经纬度 + var tbd = o.transformCoordinate(e.lng, e.lat); + $("#lng").val(tbd.lng); + $("#lat").val(tbd.lat); + o.lng = tbd.lng; + o.lat = tbd.lat; + var marker = new AMap.Marker({ + icon: "https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png", + position: [e.lng, e.lat] + }); + map.add(marker); + if (o.config.type == 1) { + searchNearBy(e.lng, e.lat); + } + } + + var clickHandler = function (e) { + markCenter({lng: e.lnglat.getLng(), lat: e.lnglat.getLat()}); + }; + + // 绑定事件 + map.on('click', clickHandler); + + // 附近搜索方法 + var searchNearBy = function (lng, lat) { + AMap.service(['AMap.PlaceSearch'], function () { + var placeSearch = new AMap.PlaceSearch({ + type: '', pageSize: 10, pageIndex: 1 + }); + var cpoint = [lng, lat]; + placeSearch.searchNearBy('', cpoint, 1000, function (status, result) { + if (status === 'complete') { + var pois = result.poiList.pois; + var htmlList = ''; + for (var i = 0; i < pois.length; i++) { + var poiItem = pois[i]; + if (poiItem.location !== undefined) { + htmlList += '
          '; + htmlList += '
          ' + poiItem.name + '
          '; + htmlList += '
          ' + poiItem.address + '
          '; + htmlList += '
          '; + htmlList += '
          '; + } + } + $('#ew-map-select-poi').html(htmlList); + } + }); + }); + }; + + // 初始化search + if (o.config.type == 1) { + o.initGaodeSearch(map, markCenter, searchNearBy); + } + + } + + this.initGaodeSearch = function (map, markCenter, searchNearBy) { + var o = this; + // poi列表点击事件 + $('#ew-map-select-poi').off('click').on('click', '.ew-map-select-search-list-item', function () { + $('#ew-map-select-tips').addClass('layui-hide'); + $('#ew-map-select-poi .ew-map-select-search-list-item-icon-ok').addClass('layui-hide'); + $(this).find('.ew-map-select-search-list-item-icon-ok').removeClass('layui-hide'); + $('#ew-map-select-center-img').removeClass('bounceInDown'); + setTimeout(function () { + $('#ew-map-select-center-img').addClass('bounceInDown'); + }); + var lng = $(this).data('lng'); + var lat = $(this).data('lat'); + var name = $(this).find('.ew-map-select-search-list-item-title').text(); + var address = $(this).find('.ew-map-select-search-list-item-address').text(); + // + map.setZoomAndCenter(map.getZoom(), [lng, lat]); + + markCenter({lng: lng, lat: lat}); + var title = $(this).data('title'); + var address = $(this).data('address'); + o.config.onClickTip({title:title,address:address,lng:lng,lat:lat}); + }); + + searchNearBy(o.config.longitude ? o.config.longitude : 116.404, o.config.latitude ? o.config.latitude : 39.915); + + // 搜索提示 + var $inputSearch = $('#ew-map-select-input-search'); + $inputSearch.off('input').on('input', function () { + var keywords = $(this).val(); + var $selectTips = $('#ew-map-select-tips'); + if (!keywords) { + $selectTips.html(''); + $selectTips.addClass('layui-hide'); + } + AMap.plugin('AMap.Autocomplete', function () { + var autoComplete = new AMap.Autocomplete({city: '全国'}); + autoComplete.search(keywords, function (status, result) { + if (result.tips) { + var tips = result.tips; + var htmlList = ''; + for (var i = 0; i < tips.length; i++) { + var tipItem = tips[i]; + if (tipItem.location !== undefined) { + htmlList += '
          '; + htmlList += ' '; + htmlList += '
          ' + tipItem.name + '
          '; + htmlList += '
          ' + tipItem.address + '
          '; + htmlList += '
          '; + } + } + $selectTips.html(htmlList); + if (tips.length === 0) $('#ew-map-select-tips').addClass('layui-hide'); + else $('#ew-map-select-tips').removeClass('layui-hide'); + } else { + $selectTips.html(''); + $selectTips.addClass('layui-hide'); + } + }); + }); + }); + $inputSearch.off('blur').on('blur', function () { + var keywords = $(this).val(); + var $selectTips = $('#ew-map-select-tips'); + if (!keywords) { + $selectTips.html(''); + $selectTips.addClass('layui-hide'); + } + }); + $inputSearch.off('focus').on('focus', function () { + var keywords = $(this).val(); + if (keywords) $('#ew-map-select-tips').removeClass('layui-hide'); + }); + // tips列表点击事件 + $('#ew-map-select-tips').off('click').on('click', '.ew-map-select-search-list-item', function () { + $('#ew-map-select-tips').addClass('layui-hide'); + var lng = $(this).data('lng'); + var lat = $(this).data('lat'); + map.setZoomAndCenter(map.getZoom(), [lng, lat]); + markCenter({lng: lng, lat: lat}); + var title = $(this).data('title'); + var address = $(this).data('address'); + o.config.onClickTip({title:title,address:address,lng:lng,lat:lat}); + }); + } + + this.openMap = function () { + var o = this; + + if (o.config.apiType == "baiduMap") { + var index = layer.open({ + type: 1, + area: ["850px", "600px"], + title: o.config.title, + content: o.config.type == 0 ? tpl0:tpl1, + success: function () { + // 回显数据 中心标记经纬度 + $("#lng").val(o.lng); + $("#lat").val(o.lat); + // 渲染地图 + if (undefined === window.BMap) { + $.getScript("http://api.map.baidu.com/getscript?v=2.0&ak=tCNPmUfNmy4nTR3VYW71a6IgyWMaOSUb&services=&t=20200824135534", function () { + o.openBaiduMap(); + }); + } else { + o.openBaiduMap(); + } + // 绑定事件 + $("#ew-map-select-btn-ok").on("click", function () { + o.config.success({lng: o.lng ? o.lng : 116.404, lat: o.lat ? o.lat : 39.915}); + layer.close(index); + }) + } + }); + } else if (o.config.apiType == "tiandiMap") { + var index = layer.open({ + type: 1, + area: ["850px", "600px"], + title: o.config.title, + content: o.config.type == 0 ? tpl0:tpl1, + success: function () { + // 回显数据 中心标记经纬度 + $("#lng").val(o.lng); + $("#lat").val(o.lat); + // 渲染地图 + if (undefined === window.T) { + $.getScript("http://api.tianditu.gov.cn/api?v=4.0&tk=a8718394c98e9ae85b0d7af352653ce2&callback=init", function () { + o.openTiandiMap(); + }) + } else { + o.openTiandiMap(); + } + // 绑定事件 + $("#ew-map-select-btn-ok").on("click", function () { + o.config.success({lng: o.lng ? o.lng : 116.404, lat: o.lat ? o.lat : 39.915}); + layer.close(index); + }) + } + }); + + } else if (o.config.apiType == "gaodeMap") { + var index = layer.open({ + type: 1, + area: ["850px", "600px"], + title: o.config.title, + content: o.config.type == 0 ? tpl0:tpl1, + success: function () { + // 回显数据 中心标记经纬度 + $("#lng").val(o.lng); + $("#lat").val(o.lat); + // 渲染地图 + if (undefined === window.AMap) { + $.getScript("https://webapi.amap.com/maps?v=1.4.14&key=006d995d433058322319fa797f2876f5", function () { + o.openGaodeMap(); + }); + } else { + o.openGaodeMap(); + } + // 绑定事件 + $("#ew-map-select-btn-ok").on("click", function () { + o.config.success({lng: o.lng ? o.lng : 116.404, lat: o.lat ? o.lat : 39.915}); + layer.close(index); + }) + } + }); + } + + } + + }; + + + layui.link(layui.cache.base + "location/location.css?v="+(new Date).getTime()); // 加载css + + /*导出模块,用一个location对象来管理obj,不需要外部new obj*/ + var location = function () { + } + location.prototype.render = function (elem, config) { + $(elem).on("click", function () { + var _this = new obj(config); + _this.openMap(); + }) + } + var locationObj = new location(); + exports(MOD_NAME, locationObj); +}) \ No newline at end of file diff --git a/public/js/lay-module/location/locationX.js b/public/js/lay-module/location/locationX.js new file mode 100644 index 0000000..68f6782 --- /dev/null +++ b/public/js/lay-module/location/locationX.js @@ -0,0 +1,168 @@ +layui.define(['layer'],function (exports) { + var $ = layui.jquery, + layer=layui.layer, + MOD_NAME = "locationX"; + + var GPS = { + PI : 3.14159265358979324, + x_pi : 3.14159265358979324 * 3000.0 / 180.0, + delta : function (lat, lon) { + // Krasovsky 1940 + // + // a = 6378245.0, 1/f = 298.3 + // b = a * (1 - f) + // ee = (a^2 - b^2) / a^2; + var a = 6378245.0; // a: 卫星椭球坐标投影到平面地图坐标系的投影因子。 + var ee = 0.00669342162296594323; // ee: 椭球的偏心率。 + var dLat = this.transformLat(lon - 105.0, lat - 35.0); + var dLon = this.transformLon(lon - 105.0, lat - 35.0); + var radLat = lat / 180.0 * this.PI; + var magic = Math.sin(radLat); + magic = 1 - ee * magic * magic; + var sqrtMagic = Math.sqrt(magic); + dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * this.PI); + dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * this.PI); + return {'lat': dLat, 'lon': dLon}; + }, + + //WGS-84 to GCJ-02 + gcj_encrypt : function (wgsLat, wgsLon) { + if (this.outOfChina(wgsLat, wgsLon)) + return {'lat': wgsLat, 'lon': wgsLon}; + + var d = this.delta(wgsLat, wgsLon); + return {'lat' : wgsLat + d.lat,'lon' : wgsLon + d.lon}; + }, + //GCJ-02 to WGS-84 + gcj_decrypt : function (gcjLat, gcjLon) { + if (this.outOfChina(gcjLat, gcjLon)) + return {'lat': gcjLat, 'lon': gcjLon}; + + var d = this.delta(gcjLat, gcjLon); + return {'lat': gcjLat - d.lat, 'lon': gcjLon - d.lon}; + }, + //GCJ-02 to WGS-84 exactly + gcj_decrypt_exact : function (gcjLat, gcjLon) { + var initDelta = 0.01; + var threshold = 0.000000001; + var dLat = initDelta, dLon = initDelta; + var mLat = gcjLat - dLat, mLon = gcjLon - dLon; + var pLat = gcjLat + dLat, pLon = gcjLon + dLon; + var wgsLat, wgsLon, i = 0; + while (1) { + wgsLat = (mLat + pLat) / 2; + wgsLon = (mLon + pLon) / 2; + var tmp = this.gcj_encrypt(wgsLat, wgsLon) + dLat = tmp.lat - gcjLat; + dLon = tmp.lon - gcjLon; + if ((Math.abs(dLat) < threshold) && (Math.abs(dLon) < threshold)) + break; + + if (dLat > 0) pLat = wgsLat; else mLat = wgsLat; + if (dLon > 0) pLon = wgsLon; else mLon = wgsLon; + + if (++i > 10000) break; + } + //console.log(i); + return {'lat': wgsLat, 'lon': wgsLon}; + }, + //GCJ-02 to BD-09 + bd_encrypt : function (gcjLat, gcjLon) { + var x = gcjLon, y = gcjLat; + var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * this.x_pi); + var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * this.x_pi); + bdLon = z * Math.cos(theta) + 0.0065; + bdLat = z * Math.sin(theta) + 0.006; + return {'lat' : bdLat,'lon' : bdLon}; + }, + //BD-09 to GCJ-02 + bd_decrypt : function (bdLat, bdLon) { + var x = bdLon - 0.0065, y = bdLat - 0.006; + var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * this.x_pi); + var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * this.x_pi); + var gcjLon = z * Math.cos(theta); + var gcjLat = z * Math.sin(theta); + return {'lat' : gcjLat, 'lon' : gcjLon}; + }, + // + bd_WGS84:function(bdLat, bdLon){ + var gcj=GPS.bd_decrypt(bdLat, bdLon); + return GPS.gcj_decrypt(gcj.lat,gcj.lon); + }, + // 天地图坐标->百度坐标 + WGS84_bd:function(bdLat, bdLon){ + var gcj=GPS.gcj_encrypt(bdLat, bdLon); + return GPS.bd_encrypt(gcj.lat,gcj.lon); + }, + //WGS-84 to Web mercator + //mercatorLat -> y mercatorLon -> x + mercator_encrypt : function(wgsLat, wgsLon) { + var x = wgsLon * 20037508.34 / 180.; + var y = Math.log(Math.tan((90. + wgsLat) * this.PI / 360.)) / (this.PI / 180.); + y = y * 20037508.34 / 180.; + return {'lat' : y, 'lon' : x}; + /* + if ((Math.abs(wgsLon) > 180 || Math.abs(wgsLat) > 90)) + return null; + var x = 6378137.0 * wgsLon * 0.017453292519943295; + var a = wgsLat * 0.017453292519943295; + var y = 3189068.5 * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a))); + return {'lat' : y, 'lon' : x}; + //*/ + }, + // Web mercator to WGS-84 + // mercatorLat -> y mercatorLon -> x + mercator_decrypt : function(mercatorLat, mercatorLon) { + var x = mercatorLon / 20037508.34 * 180.; + var y = mercatorLat / 20037508.34 * 180.; + y = 180 / this.PI * (2 * Math.atan(Math.exp(y * this.PI / 180.)) - this.PI / 2); + return {'lat' : y, 'lon' : x}; + /* + if (Math.abs(mercatorLon) < 180 && Math.abs(mercatorLat) < 90) + return null; + if ((Math.abs(mercatorLon) > 20037508.3427892) || (Math.abs(mercatorLat) > 20037508.3427892)) + return null; + var a = mercatorLon / 6378137.0 * 57.295779513082323; + var x = a - (Math.floor(((a + 180.0) / 360.0)) * 360.0); + var y = (1.5707963267948966 - (2.0 * Math.atan(Math.exp((-1.0 * mercatorLat) / 6378137.0)))) * 57.295779513082323; + return {'lat' : y, 'lon' : x}; + //*/ + }, + // two point's distance + distance : function (latA, lonA, latB, lonB) { + var earthR = 6371000.; + var x = Math.cos(latA * this.PI / 180.) * Math.cos(latB * this.PI / 180.) * Math.cos((lonA - lonB) * this.PI / 180); + var y = Math.sin(latA * this.PI / 180.) * Math.sin(latB * this.PI / 180.); + var s = x + y; + if (s > 1) s = 1; + if (s < -1) s = -1; + var alpha = Math.acos(s); + var distance = alpha * earthR; + return distance; + }, + outOfChina : function (lat, lon) { + if (lon < 72.004 || lon > 137.8347) + return true; + if (lat < 0.8293 || lat > 55.8271) + return true; + return false; + }, + transformLat : function (x, y) { + var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x)); + ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin(y / 3.0 * this.PI)) * 2.0 / 3.0; + ret += (160.0 * Math.sin(y / 12.0 * this.PI) + 320 * Math.sin(y * this.PI / 30.0)) * 2.0 / 3.0; + return ret; + }, + transformLon : function (x, y) { + var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)); + ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin(x / 3.0 * this.PI)) * 2.0 / 3.0; + ret += (150.0 * Math.sin(x / 12.0 * this.PI) + 300.0 * Math.sin(x / 30.0 * this.PI)) * 2.0 / 3.0; + return ret; + } + }; + + exports(MOD_NAME,GPS); + +}) diff --git a/public/js/lay-module/mods/extend/jsan-notice.css b/public/js/lay-module/mods/extend/jsan-notice.css new file mode 100644 index 0000000..5816cab --- /dev/null +++ b/public/js/lay-module/mods/extend/jsan-notice.css @@ -0,0 +1,96 @@ +/** +消息盒子容器 + */ +.lay-jsan-notice-marker { + position: absolute; +} + +/** +消息盒子基础样式 + */ +.lay-jsan-notice-marker-box { + /*position: absolute;*/ + width: 35px; + height: 35px; + border: 1px solid #e2e2e2; + text-align: center; + line-height: 35px; + color: #e2e2e2; + float: left; + background-color: #ffffff; +} + +/** +消息盒子鼠标悬停样式 + */ +.lay-jsan-notice-marker-box:hover { + background-color: #f2f2f2; + color: #1E9FFF; +} + +/** +消息盒子中图标样式 + */ +.lay-jsan-notice-marker-icon { + font-size: 26px!important; +} + +/** +最新消息,消息盒子样式 + */ +.lay-jsan-notice-marker-news { + color: #FF5722!important; +} + +/** +消息盒子操作按钮 + */ +.lay-jsan-notice-marker-btn { + /*position: absolute;*/ + width: 15px; + height: 35px; + border: 1px solid #e2e2e2; + text-align: center; + line-height: 35px; + color: #e2e2e2; + float: left; + background-color: #ffffff; + font-size: 16px; +} + +.lay-jsan-notice-marker-btn:hover { + background-color: #f2f2f2; + color: #1E9FFF; +} + +.lay-jsan-notice-marker-item { + padding: 5px 0 5px 10px; + background-color: #f2f2f2; + border-radius: 5px; + margin-bottom: 12px; +} + +.lay-jsan-notice-marker-item:hover { + background-color: #dddddd; + cursor: pointer; +} + +.lay-jsan-notice-marker-item-title-new { + color: #FF5722; +} + +.lay-jsan-notice-marker-item-title { + font-size: 14px; + font-weight: bold; +} + +.lay-jsan-notice-marker-item-date { + font-size: 10px; + color: #d2d2d2; + padding: 2px 0 2px 0; +} + +.lay-jsan-notice-marker-item-content { + font-size: 12px; + color: #d2d2d2; +} \ No newline at end of file diff --git a/public/js/lay-module/mods/extend/jsan-notice.js b/public/js/lay-module/mods/extend/jsan-notice.js new file mode 100644 index 0000000..697cd6b --- /dev/null +++ b/public/js/lay-module/mods/extend/jsan-notice.js @@ -0,0 +1,243 @@ +// noinspection ThisExpressionReferencesGlobalObjectJS +/** + * 基于layui v-2.5.4 版本,封装的消息组件 + * 作者:jsan + * 日期:2019-07-28 + */ +(function (root, factroy) { + typeof root.layui === "object" && layui.define ? layui.define(["layer"], function(mods){mods("jsanNotice", factroy(layui.layer))}) : factroy(root.layer); +}(this, function (layer) { + // //引入css + layui.link(layui.cache.base+"mods/extend/jsan-notice.css?v="+(new Date).getTime()); + + /** + * 消息盒子方法 + * @param option 参数对象: + * elem 元素选择器,如:#test + * positionX 盒子左右定位位置[right,left],默认right + * positionY 盒子相对位置,可以选择不同的单位长度,如:100px + * lowKey true隐藏,false显示 + * noticeWindow 详细消息窗口属性 + */ + layer.noticeMarker = function (option) { + const $ = layui.$, + that = {}, + POSITION_X_STYLE = typeof option["positionX"] === "undefined" || option["positionX"] === "right" ? "right: 1px;" : option["positionX"]+" 1px;", + POSITION_Y_STYPE = typeof option["positionY"] === "undefined" ? "top: 0;" : "top: "+option["positionY"]+";", + MARKER = "lay-jsan-notice-marker", + MARKER_BOX = "lay-jsan-notice-marker-box", + MARKER_BOX_NEWS = "lay-jsan-notice-marker-news", + MARKER_BOX_ICON = "layui-icon layui-icon-speaker lay-jsan-notice-marker-icon", + MARKER_BOX_BTN = "lay-jsan-notice-marker-btn", + MARKER_BOX_HIDE_BTN_ICON = "layui-icon layui-icon-"+(typeof option["positionX"] === "undefined" || option["positionX"] === "right" ? "right" : "left"), + MARKER_BOX_SHOW_BTN_ICON = "layui-icon layui-icon-"+(typeof option["positionX"] === "undefined" || option["positionX"] === "right" ? "left" : "right"); + + that.properties = {}; //初始化属性 + + $(option.elem).hide(); //隐藏初始化元素 + that.properties.index = NOTICE_MARKER_INDEX++; //消息框唯一标识 + that.properties.isOpen = option["lowKey"]; //初始化是否显示 + that.properties.option = option; //录入初始化配置 + that.properties.option.positionX = typeof option["positionX"] === "undefined" ? "right" : option["positionX"]; //初始化定位 + that.properties.option.positionY = typeof option["positionY"] === "undefined" ? "right" : option["positionY"]; //初始化定位 + that.properties.marker = $("
          "); //容器 + that.properties.markerBoxBtn = $("
          "); //提示显示角标 + that.properties.markerBoxBtnIcon = $(""); //提示显示角标 + that.properties.markerBox = $("
          "); //消息盒子 + that.properties.markerBoxIcon = $(""); //消息盒子图标 + //初始化默认方法 + /** + * 消息提醒方法 + * @param option 参数对象 + * lowKey true隐藏,false显示 + */ + that.remind = function(option) { + if(option["lowKey"] && that.properties.isOpen) { + //提醒时不弹出 + this.hideBox(); + }else if(!option["lowKey"] && !that.properties.isOpen) { + this.showBox(); + } + this.properties.markerBox.addClass(MARKER_BOX_NEWS); + this.properties.markerBoxBtn.addClass(MARKER_BOX_NEWS); + }; + + /** + * 隐藏消息盒子方法 + */ + that.hideBox = function() { + if(this.properties.isOpen) { + this.properties.markerBox.hide(); + this.properties.markerBoxBtnIcon.removeClass(MARKER_BOX_HIDE_BTN_ICON); + this.properties.markerBoxBtnIcon.addClass(MARKER_BOX_SHOW_BTN_ICON); + this.properties.isOpen = false; + } + }; + + /** + * 显示消息盒子方法 + */ + that.showBox = function() { + if(!this.properties.isOpen) { + this.properties.markerBox.show(); + this.properties.markerBoxBtnIcon.removeClass(MARKER_BOX_SHOW_BTN_ICON); + this.properties.markerBoxBtnIcon.addClass(MARKER_BOX_HIDE_BTN_ICON); + this.properties.isOpen = true; + } + }; + + //封装渲染 + that.properties.markerBoxBtn.html(that.properties.markerBoxBtnIcon); + that.properties.markerBox.html(that.properties.markerBoxIcon); + that.properties.marker.html(that.properties.option["positionX"] === "left" ? that.properties.markerBox : that.properties.markerBoxBtn); + that.properties.marker.append(that.properties.option["positionX"] === "left" ? that.properties.markerBoxBtn : that.properties.markerBox); + + $("body").append(that.properties.marker); + + //隐藏/显示事件 + that.properties.markerBoxBtn.unbind().on("click", that, function (event) { + event.data.properties.isOpen ? event.data.hideBox() : event.data.showBox(); + }); + + //初始化详细消息窗口 + /** + * type 1:组件自带消息窗口,2:打开用户自定义窗口,默认是1 + * title 消息窗口标题 + * classType 消息类型(type=1时生效) 属于Object类型 {"id": "name"} 如:{"notice": "通知", "alerted": "预警", "other": "其他"} + * url 自定义消息窗口时打开的链接 + * width 消息窗口宽度 可以选择不同的单位长度,如:100px + * height 消息窗口高度 可以选择不同的单位长度,如:100px + * contentWidth 消息内容窗口宽度 + * contentHeight 消息内容窗口高度 + */ + if(typeof that.properties.option.noticeWindow === "object" && that.properties.option.noticeWindow["type"] !== 2) { + + that.noticeWindow = {}; + that.noticeWindow.index = that.properties.index; + that.noticeWindow.width = typeof that.properties.option.noticeWindow["width"] === "string" ? that.properties.option.noticeWindow["width"] : "150px"; + that.noticeWindow.height = typeof that.properties.option.noticeWindow["height"] === "string" ? that.properties.option.noticeWindow["height"] : "560px"; + that.noticeWindow.contentWidth = typeof that.properties.option.noticeWindow["contentWidth"] === "string" ? that.properties.option.noticeWindow["contentWidth"] : "650px"; + that.noticeWindow.contentHeight = typeof that.properties.option.noticeWindow["contentHeight"] === "string" ? that.properties.option.noticeWindow["contentHeight"] : "560px"; + that.noticeWindow.window = $("
          "); + that.noticeWindow.tabTitle = $("
            "); + that.noticeWindow.tabContent = $("
            "); + + const classType = {}; + if(typeof that.properties.option.noticeWindow["classType"] === "object") { + for(let tab in that.properties.option.noticeWindow["classType"]) { + classType[tab] = ["
          • "+that.properties.option.noticeWindow["classType"][tab]+"
          • ", "
            "]; + } + }else { + classType['notice'] = ["
          • 消息
          • ", "
            "]; + } + + that.noticeWindow.classType = classType; + + //详细消息窗口渲染 + that.noticeWindow.window.html(that.noticeWindow.tabTitle); + that.noticeWindow.window.append(that.noticeWindow.tabContent); + for(let tab in that.noticeWindow.classType) { + that.noticeWindow.tabTitle.append(that.noticeWindow.classType[tab][0]); + that.noticeWindow.tabContent.append(that.noticeWindow.classType[tab][1]); + } + that.noticeWindow.window.hide(); + that.noticeWindow.tabTitle.find("li").eq(0).addClass("layui-this"); + that.noticeWindow.tabContent.find(".layui-tab-item").eq(0).addClass("layui-show"); + $("body").append(that.noticeWindow.window); + + layui.use('element', function(){}); + + that.properties.markerBox.unbind().on("click", that, function (event) { + event.data.hideBox(); + layer.open({ + type: 1, + id: "notice-marker-window-layer-"+that.noticeWindow.index, + title: typeof event.data.properties.option.noticeWindow["title"] === "string" ? event.data.properties.option.noticeWindow["title"] : "", + area: [event.data.noticeWindow.width - 12, event.data.noticeWindow.height], + offset: [event.data.properties.option.positionY, (that.properties.option["positionX"] === "right" ? ($("body").width()-16-Number(that.noticeWindow.width.replace("px", "")))+"px" : "16px")], + shade: 0, + maxmin: true, + content: $("#"+event.data.noticeWindow.window.attr("id")), + cancel: function () { + that.noticeWindow.window.hide(); + } + }); + event.data.properties.markerBox.removeClass(MARKER_BOX_NEWS); + event.data.properties.markerBoxBtn.removeClass(MARKER_BOX_NEWS); + }); + + /** + * 向消息窗口推送消息 + * @param option 参数对象 + * lowKey 是否使用盒子提醒 true不提醒,false提醒 + * classTypeId 消息所属消息类型的id + * content 需要推送的类型集合 Array,每组数据包括: + * title 消息标题。最大27位长度,大于27会自动省略 + * content 消息内容。最大44位长度,大于44位自动省略 + * date 消息发布时间 yyyy-MM-dd HH:mm:ss + * url 点击消息后跳转位置 + */ + that.addNews = function (option) { + for(let i in option.content) { + const item = $("
            "); + item.append("
            "+(option.content[i]["title"].length > 28 ? option.content[i]["title"].substring(0, 25)+"..." : option.content[i]["title"] )+"
            "); + item.append("
            "+option.content[i]["date"]+"
            "); + item.append("
            "+(option.content[i]["content"].length > 45 ? option.content[i]["content"].substring(0, 43)+"..." : option.content[i]["content"])+"
            "); + $("#notice-marker-window-content-"+option["classTypeId"]).prepend(item); + } + $("#notice-marker-window-title-"+option["classTypeId"]).find(".layui-badge-dot").removeClass("layui-hide"); + noticeMarkerItemEvent(option, this); + const lowKey = typeof option["lowKey"] === "undefined" ? false : option["lowKey"]; + this.remind({"lowKey": lowKey}); + }; + + var noticeMarkerItemEvent = function (option, that) { + $("#notice-marker-window-content-"+option["classTypeId"]+" .lay-jsan-notice-marker-item").unbind().on("click", function (event) { + $(this).find(".lay-jsan-notice-marker-item-title").eq(0).removeClass("lay-jsan-notice-marker-item-title-new"); + if($("#notice-marker-window-content-"+option["classTypeId"]).find(".lay-jsan-notice-marker-item-title-new").length === 0) { + $("#notice-marker-window-title-"+option["classTypeId"]).find(".layui-badge-dot").addClass("layui-hide"); + } + $(this).attr("notice-url"); + layer.open({ + type: 2, + title: that.properties.option.noticeWindow["title"], + area: [that.noticeWindow.contentWidth, that.noticeWindow.contentHeight], + shade: 0, + maxmin: true, + content: $(this).attr("notice-url") + }); + }); + } + }else if(typeof that.properties.option.noticeWindow === "object" && that.properties.option.noticeWindow["type"] === 2) { + + that.noticeWindow = {}; + that.noticeWindow.index = that.properties.index; + that.noticeWindow.url = that.properties.option.noticeWindow["url"]; + that.noticeWindow.width = typeof that.properties.option.noticeWindow["width"] === "string" ? that.properties.option.noticeWindow["width"] : "150px"; + that.noticeWindow.height = typeof that.properties.option.noticeWindow["height"] === "string" ? that.properties.option.noticeWindow["height"] : "560px"; + + that.properties.markerBox.unbind().on("click", that, function (event) { + event.data.hideBox(); + layer.open({ + type: 2, + id: "notice-marker-window-layer-"+that.noticeWindow.index, + title: typeof event.data.properties.option.noticeWindow["title"] === "string" ? event.data.properties.option.noticeWindow["title"] : "", + area: [event.data.noticeWindow.width, event.data.noticeWindow.height], + offset: [event.data.properties.option.positionY, (that.properties.option["positionX"] === "right" ? ($("body").width()-16-Number(that.noticeWindow.width.replace("px", "")))+"px" : "16px")], + shade: 0, + maxmin: true, + content: event.data.noticeWindow.url + }); + }); + } + + //初始显示设置 + that.properties.isOpen ? that.hideBox() : that.showBox(); + + return that; + }; + + return {mod: "jsanNotice", v: "1.0.11"}; +})); + +NOTICE_MARKER_INDEX = 1; \ No newline at end of file diff --git a/public/js/lay-module/mods/mods.js b/public/js/lay-module/mods/mods.js new file mode 100644 index 0000000..381faec --- /dev/null +++ b/public/js/lay-module/mods/mods.js @@ -0,0 +1,78 @@ +(function(root,factroy){ + typeof root.layui === 'object' && layui.define ? layui.define(function(mods){mods('mods',factroy(layui))}) : null; +}(this,function(layui){ + 'use strict'; + + // 预定义插件列表 + var list = { + jsanNotice:'mods/extend/jsan-notice' + }; + + // 插件加载器 + var mods = function(mod_name,callback){ + var extend = {}; + + // 如果是官方模块 + // 引入单个插件 + if(typeof mod_name === 'string'){ + if(!isLayui(mod_name)){ + if(typeof list[mod_name] !== 'string') throw new Error('引入的插件'+mod_name+'不在预定义列表中'); + extend[mod_name] = list[mod_name]; + } + } + + // 批量引入插件 + else if(Array.isArray(mod_name)){ + for(var i=0,item;item = mod_name[i++];){ + if(!isLayui(item)){ + if(typeof list[item] !== 'string') throw new Error('引入的插件'+item+'不在预定义列表中'); + extend[item] = list[item]; + } + } + } + else{ + throw new Error('mods()中,传入了无效的参数'); + } + + if(typeof callback !== 'function') throw Error('第二个参数必须是函数'); + layui.extend(extend).use(mod_name,function(){ + var arg = []; + for(var i=0,item;item = arguments[i++];){ + arg.push(item); + } + callback.apply(layui,arg); + }); + } + + var isLayui = function(mod){ + return typeof layui_mods[mod] === 'string' ? true : false; + }; + + if(typeof Array.isArray !== 'function'){ + Array.isArray = function(val){ + return Object.prototype.toString.call(val) === '[object Array]' ? true : false; + } + } + + var layui_mods = { + layer:'layer', + laydate:'laydate', + layedit:'layedit', + laypage:'laypage', + laytpl:'laytpl', + table:'table', + form:'form', + upload:'upload', + jquery:'jquery', + code:'code', + carousel:'carousel', + element:'element', + flow:'flow', + mobile:'mobile', + rate:'rate', + tree:'tree', + util:'util', + }; + + return mods; +})); \ No newline at end of file diff --git a/public/js/lay-module/moreMenus/moreMenus.js b/public/js/lay-module/moreMenus/moreMenus.js new file mode 100644 index 0000000..71040f2 --- /dev/null +++ b/public/js/lay-module/moreMenus/moreMenus.js @@ -0,0 +1,101 @@ +/** + * @Name: 基于layui + * @Author: 潘晨晨 + * 最近修改时间: 2021/04/22 + */ + +layui.define(['jquery'],function(exports){ + var $ = layui.jquery; + + var obj ={ + init:function(element){ + //默认 + element.data = element.data || [ {name:'暂无数据'}] + element.rows = element.rows || 1 + element.moreUpText = element.moreUpText || '更多' + element.moreDownText = element.moreDownText || '收起' + element.href = element.href || false + element.herfBlank = element.herfBlank?'_blank':'_self' + element.themeColor = element.themeColor || 'green' + element.size = element.size ? 'layui-btn-'+element.size :'layui-btn' + + var html; + var box = $(element.elemId); + var boxHeight ; + var itemHeight ; + var flag = true; // 如果外面包裹了pack的变false + var dyc = false; + box.addClass('box-item-pcc'); + element.data.forEach(e=>{ + boxHeight = box.height(); + itemHeight = $('.item-pcc').outerHeight(true); + element.href ? html = ''+e.name+'':html = ''+e.name+'' + if((boxHeight>itemHeight*element.rows) && flag){ + morePosition(); + flag =false; + } + box.append(html); + }) + + + boxHeight = box.height(); + itemHeight = $('.item-pcc').outerHeight(true); + + if(boxHeight>itemHeight*element.rows){ + more(); //第一次渲染页面 + dyc =true + } + + + $(document).on('click','#more-pcc',function(){ //点击更多 + more() + }) + + function morePosition(el){ + if(flag){ + if(el&&dyc){ //如果不是第一次渲染并且 + box.find('#more-box-pcc').remove(); + } + else{ + $('.item-pcc').wrapAll("") + } + var list = $('.pack-pcc .item-pcc').eq(-3); + list.before(''+element.moreUpText+''); + + } + } + + function more(){ + if(box.height()>itemHeight*element.rows){ + box.css('height',itemHeight*element.rows); + morePosition(true) + }else{ + box.css('height','auto'); + box.find('#more-box-pcc').remove(); + box.append(''+element.moreDownText+''); + flag = true; + } + + } + + } + + } + var $style = $(''); + $($('head')[0]).append($style); + + exports('moreMenus',obj); + }); + diff --git a/public/js/lay-module/notice/notice.css b/public/js/lay-module/notice/notice.css new file mode 100644 index 0000000..65c9fea --- /dev/null +++ b/public/js/lay-module/notice/notice.css @@ -0,0 +1,228 @@ +.toast-title { + font-weight: bold; +} +.toast-message { + -ms-word-wrap: break-word; + word-wrap: break-word; +} +.toast-message a, +.toast-message label { + color: #FFFFFF; +} +.toast-message a:hover { + color: #CCCCCC; + text-decoration: none; +} +.toast-close-button { + position: relative; + right: -0.3em; + top: -0.3em; + float: right; + font-size: 20px; + font-weight: bold; + color: #FFFFFF; + -webkit-text-shadow: 0 1px 0 #ffffff; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.8; + -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); + filter: alpha(opacity=80); + line-height: 1; +} +.toast-close-button:hover, +.toast-close-button:focus { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.4; + -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40); + filter: alpha(opacity=40); +} +.rtl .toast-close-button { + left: -0.3em; + float: left; + right: 0.3em; +} +/*Additional properties for button version + iOS requires the button element instead of an anchor tag. + If you want the anchor version, it requires `href="#"`.*/ +button.toast-close-button { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} +.toast-top-center { + top: 0; + right: 0; + width: 100%; +} +.toast-bottom-center { + bottom: 0; + right: 0; + width: 100%; +} +.toast-top-full-width { + top: 0; + right: 0; + width: 100%; +} +.toast-bottom-full-width { + bottom: 0; + right: 0; + width: 100%; +} +.toast-top-left { + top: 12px; + left: 12px; +} +.toast-top-right { + top: 12px; + right: 12px; +} +.toast-bottom-right { + right: 12px; + bottom: 12px; +} +.toast-bottom-left { + bottom: 12px; + left: 12px; +} +#toast-container { + position: fixed; + z-index: 999999; + pointer-events: none; + /*overrides*/ +} +#toast-container * { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +#toast-container > div { + position: relative; + pointer-events: auto; + overflow: hidden; + margin: 0 0 6px; + padding: 15px 15px 15px 50px; + width: 300px; + -moz-border-radius: 3px 3px 3px 3px; + -webkit-border-radius: 3px 3px 3px 3px; + border-radius: 3px 3px 3px 3px; + background-position: 15px center; + background-repeat: no-repeat; + -moz-box-shadow: 0 0 12px #999999; + -webkit-box-shadow: 0 0 12px #999999; + box-shadow: 0 0 12px #999999; + color: #FFFFFF; + opacity: 0.8; + -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); + filter: alpha(opacity=80); +} +#toast-container > div.rtl { + direction: rtl; + padding: 15px 50px 15px 15px; + background-position: right 15px center; +} +#toast-container > div:hover { + -moz-box-shadow: 0 0 12px #000000; + -webkit-box-shadow: 0 0 12px #000000; + box-shadow: 0 0 12px #000000; + opacity: 1; + -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); + filter: alpha(opacity=100); + cursor: pointer; +} +#toast-container > .toast-info { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=") !important; +} +#toast-container > .toast-error { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=") !important; +} +#toast-container > .toast-success { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==") !important; +} +#toast-container > .toast-warning { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=") !important; +} +#toast-container.toast-top-center > div, +#toast-container.toast-bottom-center > div { + width: 300px; + margin-left: auto; + margin-right: auto; +} +#toast-container.toast-top-full-width > div, +#toast-container.toast-bottom-full-width > div { + width: 96%; + margin-left: auto; + margin-right: auto; +} +.toast { + background-color: #030303; +} +.toast-success { + background-color: #51A351; +} +.toast-error { + background-color: #BD362F; +} +.toast-info { + background-color: #2F96B4; +} +.toast-warning { + background-color: #F89406; +} +.toast-progress { + position: absolute; + left: 0; + bottom: 0; + height: 4px; + background-color: #000000; + opacity: 0.4; + -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40); + filter: alpha(opacity=40); +} +/*Responsive Design*/ +@media all and (max-width: 240px) { + #toast-container > div { + padding: 8px 8px 8px 50px; + width: 11em; + } + #toast-container > div.rtl { + padding: 8px 50px 8px 8px; + } + #toast-container .toast-close-button { + right: -0.2em; + top: -0.2em; + } + #toast-container .rtl .toast-close-button { + left: -0.2em; + right: 0.2em; + } +} +@media all and (min-width: 241px) and (max-width: 480px) { + #toast-container > div { + padding: 8px 8px 8px 50px; + width: 18em; + } + #toast-container > div.rtl { + padding: 8px 50px 8px 8px; + } + #toast-container .toast-close-button { + right: -0.2em; + top: -0.2em; + } + #toast-container .rtl .toast-close-button { + left: -0.2em; + right: 0.2em; + } +} +@media all and (min-width: 481px) and (max-width: 768px) { + #toast-container > div { + padding: 15px 15px 15px 50px; + width: 25em; + } + #toast-container > div.rtl { + padding: 15px 50px 15px 15px; + } +} \ No newline at end of file diff --git a/public/js/lay-module/notice/notice.js b/public/js/lay-module/notice/notice.js new file mode 100644 index 0000000..475070a --- /dev/null +++ b/public/js/lay-module/notice/notice.js @@ -0,0 +1,486 @@ +/* + * Toastr + * Copyright 2012-2015 + * Authors: John Papa, Hans Fjällemark, and Tim Ferrell. + * All Rights Reserved. + * Use, reproduction, distribution, and modification of this code is subject to the terms and + * conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php + * + * ARIA Support: Greta Krafsig + * + * Project: https://github.com/CodeSeven/toastr + */ +/* global define */ +(function (define) { + define(['jquery'], function ($) { + return (function () { + var $container; + var listener; + var toastId = 0; + var toastType = { + error: 'error', + info: 'info', + success: 'success', + warning: 'warning' + }; + + var cssStyle = $(''); + $("body").append(cssStyle); + + var toastr = { + clear: clear, + remove: remove, + error: error, + getContainer: getContainer, + info: info, + options: {}, + subscribe: subscribe, + success: success, + version: '2.1.4', + warning: warning + }; + + var previousToast; + + return toastr; + + //////////////// + + function error(message, title, optionsOverride) { + return notify({ + type: toastType.error, + iconClass: getOptions().iconClasses.error, + message: message, + optionsOverride: optionsOverride, + title: title + }); + } + + function getContainer(options, create) { + if (!options) { options = getOptions(); } + $container = $('#' + options.containerId); + if ($container.length) { + return $container; + } + if (create) { + $container = createContainer(options); + } + return $container; + } + + function info(message, title, optionsOverride) { + return notify({ + type: toastType.info, + iconClass: getOptions().iconClasses.info, + message: message, + optionsOverride: optionsOverride, + title: title + }); + } + + function subscribe(callback) { + listener = callback; + } + + function success(message, title, optionsOverride) { + return notify({ + type: toastType.success, + iconClass: getOptions().iconClasses.success, + message: message, + optionsOverride: optionsOverride, + title: title + }); + } + + function warning(message, title, optionsOverride) { + return notify({ + type: toastType.warning, + iconClass: getOptions().iconClasses.warning, + message: message, + optionsOverride: optionsOverride, + title: title + }); + } + + function clear($toastElement, clearOptions) { + var options = getOptions(); + if (!$container) { getContainer(options); } + if (!clearToast($toastElement, options, clearOptions)) { + clearContainer(options); + } + } + + function remove($toastElement) { + var options = getOptions(); + if (!$container) { getContainer(options); } + if ($toastElement && $(':focus', $toastElement).length === 0) { + removeToast($toastElement); + return; + } + if ($container.children().length) { + $container.remove(); + } + } + + // internal functions + + function clearContainer (options) { + var toastsToClear = $container.children(); + for (var i = toastsToClear.length - 1; i >= 0; i--) { + clearToast($(toastsToClear[i]), options); + } + } + + function clearToast ($toastElement, options, clearOptions) { + var force = clearOptions && clearOptions.force ? clearOptions.force : false; + if ($toastElement && (force || $(':focus', $toastElement).length === 0)) { + $toastElement[options.hideMethod]({ + duration: options.hideDuration, + easing: options.hideEasing, + complete: function () { removeToast($toastElement); } + }); + return true; + } + return false; + } + + function createContainer(options) { + $container = $('
            ') + .attr('id', options.containerId) + .addClass(options.positionClass); + + $container.appendTo($(options.target)); + return $container; + } + + function getDefaults() { + return { + tapToDismiss: true, + toastClass: 'toast', + containerId: 'toast-container', + debug: false, + + showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery + showDuration: 300, + showEasing: 'swing', //swing and linear are built into jQuery + onShown: undefined, + hideMethod: 'fadeOut', + hideDuration: 1000, + hideEasing: 'swing', + onHidden: undefined, + closeMethod: false, + closeDuration: false, + closeEasing: false, + closeOnHover: true, + + extendedTimeOut: 1000, + iconClasses: { + error: 'toast-error', + info: 'toast-info', + success: 'toast-success', + warning: 'toast-warning' + }, + iconClass: 'toast-info', + positionClass: 'toast-top-right', + timeOut: 5000, // Set timeOut and extendedTimeOut to 0 to make it sticky + titleClass: 'toast-title', + messageClass: 'toast-message', + escapeHtml: false, + target: 'body', + closeHtml: '', + closeClass: 'toast-close-button', + newestOnTop: true, + preventDuplicates: false, + progressBar: false, + progressClass: 'toast-progress', + rtl: false + }; + } + + function publish(args) { + if (!listener) { return; } + listener(args); + } + + function notify(map) { + var options = getOptions(); + var iconClass = map.iconClass || options.iconClass; + + if (typeof (map.optionsOverride) !== 'undefined') { + options = $.extend(options, map.optionsOverride); + iconClass = map.optionsOverride.iconClass || iconClass; + } + + if (shouldExit(options, map)) { return; } + + toastId++; + + $container = getContainer(options, true); + + var intervalId = null; + var $toastElement = $('
            '); + var $titleElement = $('
            '); + var $messageElement = $('
            '); + var $progressElement = $('
            '); + var $closeElement = $(options.closeHtml); + var progressBar = { + intervalId: null, + hideEta: null, + maxHideTime: null + }; + var response = { + toastId: toastId, + state: 'visible', + startTime: new Date(), + options: options, + map: map + }; + + personalizeToast(); + + displayToast(); + + handleEvents(); + + publish(response); + + if (options.debug && console) { + console.log(response); + } + + return $toastElement; + + function escapeHtml(source) { + if (source == null) { + source = ''; + } + + return source + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(//g, '>'); + } + + function personalizeToast() { + setIcon(); + setTitle(); + setMessage(); + setCloseButton(); + setProgressBar(); + setRTL(); + setSequence(); + setAria(); + } + + function setAria() { + var ariaValue = ''; + switch (map.iconClass) { + case 'toast-success': + case 'toast-info': + ariaValue = 'polite'; + break; + default: + ariaValue = 'assertive'; + } + $toastElement.attr('aria-live', ariaValue); + } + + function handleEvents() { + if (options.closeOnHover) { + $toastElement.hover(stickAround, delayedHideToast); + } + + if (!options.onclick && options.tapToDismiss) { + $toastElement.click(hideToast); + } + + if (options.closeButton && $closeElement) { + $closeElement.click(function (event) { + if (event.stopPropagation) { + event.stopPropagation(); + } else if (event.cancelBubble !== undefined && event.cancelBubble !== true) { + event.cancelBubble = true; + } + + if (options.onCloseClick) { + options.onCloseClick(event); + } + + hideToast(true); + }); + } + + if (options.onclick) { + $toastElement.click(function (event) { + options.onclick(event); + hideToast(); + }); + } + } + + function displayToast() { + $toastElement.hide(); + + $toastElement[options.showMethod]( + {duration: options.showDuration, easing: options.showEasing, complete: options.onShown} + ); + + if (options.timeOut > 0) { + intervalId = setTimeout(hideToast, options.timeOut); + progressBar.maxHideTime = parseFloat(options.timeOut); + progressBar.hideEta = new Date().getTime() + progressBar.maxHideTime; + if (options.progressBar) { + progressBar.intervalId = setInterval(updateProgress, 10); + } + } + } + + function setIcon() { + if (map.iconClass) { + $toastElement.addClass(options.toastClass).addClass(iconClass); + } + } + + function setSequence() { + if (options.newestOnTop) { + $container.prepend($toastElement); + } else { + $container.append($toastElement); + } + } + + function setTitle() { + if (map.title) { + var suffix = map.title; + if (options.escapeHtml) { + suffix = escapeHtml(map.title); + } + $titleElement.append(suffix).addClass(options.titleClass); + $toastElement.append($titleElement); + } + } + + function setMessage() { + if (map.message) { + var suffix = map.message; + if (options.escapeHtml) { + suffix = escapeHtml(map.message); + } + $messageElement.append(suffix).addClass(options.messageClass); + $toastElement.append($messageElement); + } + } + + function setCloseButton() { + if (options.closeButton) { + $closeElement.addClass(options.closeClass).attr('role', 'button'); + $toastElement.prepend($closeElement); + } + } + + function setProgressBar() { + if (options.progressBar) { + $progressElement.addClass(options.progressClass); + $toastElement.prepend($progressElement); + } + } + + function setRTL() { + if (options.rtl) { + $toastElement.addClass('rtl'); + } + } + + function shouldExit(options, map) { + if (options.preventDuplicates) { + if (map.message === previousToast) { + return true; + } else { + previousToast = map.message; + } + } + return false; + } + + function hideToast(override) { + var method = override && options.closeMethod !== false ? options.closeMethod : options.hideMethod; + var duration = override && options.closeDuration !== false ? + options.closeDuration : options.hideDuration; + var easing = override && options.closeEasing !== false ? options.closeEasing : options.hideEasing; + if ($(':focus', $toastElement).length && !override) { + return; + } + clearTimeout(progressBar.intervalId); + return $toastElement[method]({ + duration: duration, + easing: easing, + complete: function () { + removeToast($toastElement); + clearTimeout(intervalId); + if (options.onHidden && response.state !== 'hidden') { + options.onHidden(); + } + response.state = 'hidden'; + response.endTime = new Date(); + publish(response); + } + }); + } + + function delayedHideToast() { + if (options.timeOut > 0 || options.extendedTimeOut > 0) { + intervalId = setTimeout(hideToast, options.extendedTimeOut); + progressBar.maxHideTime = parseFloat(options.extendedTimeOut); + progressBar.hideEta = new Date().getTime() + progressBar.maxHideTime; + } + } + + function stickAround() { + clearTimeout(intervalId); + progressBar.hideEta = 0; + $toastElement.stop(true, true)[options.showMethod]( + {duration: options.showDuration, easing: options.showEasing} + ); + } + + function updateProgress() { + var percentage = ((progressBar.hideEta - (new Date().getTime())) / progressBar.maxHideTime) * 100; + $progressElement.width(percentage + '%'); + } + } + + function getOptions() { + return $.extend({}, getDefaults(), toastr.options); + } + + function removeToast($toastElement) { + if (!$container) { $container = getContainer(); } + if ($toastElement.is(':visible')) { + return; + } + $toastElement.remove(); + $toastElement = null; + if ($container.children().length === 0) { + $container.remove(); + previousToast = undefined; + } + } + + })(); + }); +}(typeof define === 'function' && define.amd ? define : function (deps, factory) { + if (typeof module !== 'undefined' && module.exports) { //Node + module.exports = factory(require('jquery')); + } + else if (window.layui && layui.define){ + layui.define('jquery', function (exports) { //layui加载 + exports('toastr', factory(layui.jquery)); + exports('notice', factory(layui.jquery)); + }); + } + else { + window.toastr = factory(window.jQuery); + } +})); \ No newline at end of file diff --git a/public/js/lay-module/numinput/numinput.js b/public/js/lay-module/numinput/numinput.js new file mode 100644 index 0000000..a590638 --- /dev/null +++ b/public/js/lay-module/numinput/numinput.js @@ -0,0 +1 @@ +layui.define(["jquery"],function(a){var b=layui.$;b("head link:last")[0]&&b("head link:last").after("")||b("head").append("");a("numinput",{options:{topBtns:123,rightBtns:!0,listening:!0,defaultPrec:""},init:function(a){var c=this;c.options=b.extend(c.options,a),b(".layui-input-number").attr({readonly:"readonly"}).on("focus",function(){c.showKeyboard(c,b(this))})},showKeyboard:function(a,c){var d=c.next(".layui-keyboard-number");if(d[0])d.show();else{var e=a.options.rightBtns?"xs3":"xs4",f=a.options.rightBtns?"xs6":"xs4",g=["
            ","
            ","
            1
            ","
            ","
            ","
            ","
            ","
            2
            ","
            ","
            ","
            ","
            ","
            3
            ","
            ","
            "].join(""),h=["
            ","
            ","
            7
            ","
            ","
            ","
            ","
            ","
            8
            ","
            ","
            ","
            ","
            ","
            9
            ","
            ","
            "].join(""),i=["
            ","
            ","
            ","","
            ","
            ","
            "].join(""),j=["
            ","
            ","
            ","","
            ","
            ","
            "].join(""),k=["
            ","
            ","
            ","","
            ","
            ","
            "].join(""),l=["
            ","
            ","
            ","","
            ","
            ","
            "].join("");c.after(["
            ","
            ",789==a.options.topBtns?h:g,a.options.rightBtns?i:"","
            ","
            ","
            4
            ","
            ","
            ","
            ","
            ","
            5
            ","
            ","
            ","
            ","
            ","
            6
            ","
            ","
            ",a.options.rightBtns?j:"",789==a.options.topBtns?g:h,a.options.rightBtns?k:"",a.options.rightBtns?"":i,"
            ","
            ","
            0
            ","
            ","
            ","
            ","
            ","
            .
            ","
            ","
            ",a.options.rightBtns?l:"","
            ","
            "].join("")),d=c.next(".layui-keyboard-number"),d.on("touchstart click",".layui-card-body",function(d){return a.setValue(a,c,b(this)),layui.stope(d),!1}),d.on("blur",function(){var b=c.val();c.val(a.toFixedPrec(a,c,b)),d.remove()}),a.options.listening&&a.initKeyListening(a,c,d)}d.focus()},initKeyListening:function(a,c,d){var f,g;d.on("keydown",function(h){g=h.keyCode;var e=parseInt(c.val())||0;return 107===g||h.shiftKey&&187===g?0>e&&a.setValueRange(a,c,Math.abs(e)):109===g||h.shiftKey&&189===g?0g&&(g=f.length,f+=".");f.length<=g+h;)f+="0";return f},setValueRange:function(a,b,c){var d=b.attr("min")||-9223372036854776000,e=b.attr("max")||9223372036854776000;d="string"==typeof d&&-1e&&(c=e,a.tips(b,"\u6700\u5927\u503C\u4E3A "+e+"\uFF01")),c=ce?e:c,b.val(c)},setValue:function(a,c,d){var e=b.trim(c.val()),f=b.trim(d.text()),g=c.data("prec")||a.options.defaultPrec,h=-1=g)return void a.tips(c,"\u7CBE\u786E\u5EA6\u4E3A\u4FDD\u7559\u5C0F\u6570\u70B9\u540E "+g+" \u4F4D\uFF01");e=("."!==f&&"0"===e?"":e)+f,a.setValueRange(a,c,e)}else{var i=""===e?0:e,j=c.attr("step");switch(h?(j=parseFloat(j)||.1,i=parseFloat(i)):(j=parseInt(j,10)||1,i=parseInt(i,10)),d.data("keycode")){case"38 39":i=a.toFixedPrec(a,c,i,j);break;case"37 40":i=a.toFixedPrec(a,c,i,-j);break;case 8:var k=e.length;return void(k&&c.val(e.substring(0,k-1)));case-1:return void c.val("");}a.setValueRange(a,c,i)}},tips:function(a,b){return layer.tips(b,a,{tips:1,time:2e3,anim:6,zIndex:20190403})}})}); \ No newline at end of file diff --git a/public/js/lay-module/openTable/openTable.css b/public/js/lay-module/openTable/openTable.css new file mode 100644 index 0000000..e4c1c9c --- /dev/null +++ b/public/js/lay-module/openTable/openTable.css @@ -0,0 +1,84 @@ +/** + @ Name:表格可展开显示更多列 + @ Author:hbm + @ License:MIT + @ gitee:https://gitee.com/hbm_461/layui_extend_openTable + */ + +/* 样式加载完毕的标识 */ +html #layuicss-regionSelect { + display: none; + position: absolute; + width: 1989px; +} + +.layui-openTable { + +} + +/* 组件样式 */ +.openTable-i-table-open { + display: block; + width: 10px; + height: 10px; + user-select: none; + background: url(./right.svg) 0 0 no-repeat; + background-size: 10px 10px; +} + + +/*表格展开三角动画*/ +.openTable-open-dow { + transform: rotate(90deg) +} + +.openTable-i-table-open { + position: relative; + display: inline-block; + transition: transform .2s ease-in-out; +} + +.openTable-open-td { + padding-bottom: 20px !important; + background-color: #fdfdfd !important; +} + +.openTable-open-td:hover { + background-color: white; +} + + +/*展开列 容器*/ +.openTable-open-item-div { + display: inline-block; + margin-top: 20px; + margin-right: 20px; + font-size: 16px; +} + +/*展开列 title*/ +.openTable-item-title { + color: #99a9bf; +} + + +/*展开列 可修改 */ +.openTable-exp-value-edit { + background-color: #F6F6F6; +} + +/*展开列 仅展示 */ +.openTable-exp-value { + padding: 0 20px 0 20px; + min-width: 80px; + display: inline-block; + border: none; + border-bottom: #dedede solid 1px; + padding-bottom: 2px; + padding-top: 4px !important +} + + +.openTable-open-item-div input { + height: 29px !important; +} diff --git a/public/js/lay-module/openTable/openTable.js b/public/js/lay-module/openTable/openTable.js new file mode 100644 index 0000000..c32b533 --- /dev/null +++ b/public/js/lay-module/openTable/openTable.js @@ -0,0 +1,258 @@ +/** + @ Name:表格冗余列可展开显示 + @ Author:hbm + @ License:MIT + */ + +layui.define(['form', 'table'], function (exports) { + var $ = layui.$ + , table = layui.table + , form = layui.form + , VERSION = 1.0, MOD_NAME = 'openTable', ELEM = '.layui-openTable', ON = 'on', OFF = 'off' + + //外部接口 + , openTable = { + index: layui.openTable ? (layui.openTable.index + 10000) : 0 + + //设置全局项 + , set: function (options) { + var that = this; + that.config = $.extend({}, that.config, options); + return that; + } + + //事件监听 + , on: function (events, callback) { + return layui.onevent.call(this, MOD_NAME, events, callback); + } + } + + //操作当前实例 + , thisIns = function () { + var that = this + , options = that.config + , id = options.id || options.index; + + return { + reload: function (options) { + that.reload.call(that, options); + } + , config: options + } + } + + //构造器 + , Class = function (options) { + var that = this; + that.index = ++openTable.index; + that.config = $.extend({}, that.config, openTable.config, options); + that.render(); + }; + + //默认配置 + Class.prototype.config = { + openType: 0 + }; + + //渲染视图 + Class.prototype.render = function () { + var that = this + , options = that.config + , colArr = options.cols[0] + , openCols = options.openCols || [] + , done = options.done; + + delete options["done"]; + // 1、在第一列 插入可展开操作 + colArr.splice(0, 0, { + align: 'left', + width: 50, + templet: function (item) { + return ""; + } + }); + + + // 2、表格Render + table.render( + $.extend({ + done: function (res, curr, count) { + initExpandedListener(); + if (done) { + done(res, curr, count) + } + } + }, options)); + + + // 3、展开事件 + function initExpandedListener() { + + + //扩大点击事件范围 为父级div + $(".openTable-i-table-open") + .parent() + .unbind() + .click(function () { + var that = $(this).children(); + + // 关闭类型 + if (options.openType === 0) { + var sta = $(".openTable-open-dow").attr("status"), + isThis = (that.attr("data") === $(".openTable-open-dow").attr("data")); + //1、关闭展开的 + $(".openTable-open-dow") + .addClass("openTable-open-up") + .removeClass("openTable-open-dow") + .attr("status", OFF); + + //2、如果当前 = 展开 && 不等于当前的 关闭 + if (sta === ON && isThis) { + $(".openTable-open-td").slideUp(100, function () { + $(".openTable-open-td").remove(); + }); + + return; + } else { + that.attr("status", OFF) + $(".openTable-open-td").remove(); + } + + + } + + + var _this = this + , item = JSON.parse(that.attr("data")) + , status = that.attr("status") === 'on'; + + // 1、如果当前为打开,再次点击则关闭 + if (status) { + that.removeClass("openTable-open-dow"); + that.attr("status", 'off'); + this.addTR.find("div").slideUp(100, function () { + _this.addTR.remove(); + }); + return; + } + + var html = ["
            "]; + + // 2、从左到右依次排列 Item + openCols.forEach(function (val, index) { + // 1、自定义模板 + if (val.templet) { + html.push("
            ") + html.push(val.templet(item)); + html.push("
            ") + // 2、可下拉选择类型 + } else if (val.type && val.type === 'select') { + var child = ["
            "]; + child.push("" + val["title"] + ":"); + child.push("
            "); + child.push("
            "); + html.push(child.join("")); + setTimeout(function () { + layui.form.render(); + // 监听 select 修改 + layui.form.on('select(' + val.field + ')', function (data) { + if (options.edit && val.onSelect(data, item)) { + var json = {}; + json.value = data.value; + json.field = val.field; + item[val.field] = data.value; + json.data = JSON.parse(JSON.stringify(item)); + options.edit(json); + } + }); + }, 20); + } else { + // 3、默认类型 + html.push("
            "); + html.push("" + val["title"] + ":"); + html.push((val.edit ? + ("") + : ("" + item[val["field"]] + "") + )); + html.push("
            "); + } + + }); + + that.addClass("openTable-open-dow"); + + // 把添加的 tr 绑定到当前 移除时使用 + this.addTR = $([ + "" + , html.join("") + "
            " + , ""].join("") + ); + that.parent().parent().parent().after(this.addTR); + this.addTR.find("div").slideDown(200); + that.attr("status", 'on'); + + $(".openTable-exp-value-edit") + .blur(function () { + var that = $(this), name = that.attr("name"), val = that.val(); + // 设置了回调 &&发生了修改 + if (options.edit && item[name] + "" !== val) { + var json = {}; + json.value = that.val(); + json.field = that.attr("name"); + item[name] = val; + json.data = item; + options.edit(json); + } + }).keypress(function (even) { + even.which === 13 && $(this).blur() + }) + + + }); + + } + + // 4、监听排序事件 + var elem = $(options.elem).attr("lay-filter"); + + // 5、监听表格排序 + table.on('sort(' + elem + ')', function (obj) { + if (options.sort) { + options.sort(obj) + } + // 重新绑定事件 + initExpandedListener(); + }); + + // 6、单元格编辑 + layui.table.on('edit(' + elem + ')', function (obj) { + if (options.edit) { + options.edit(obj) + } + }); + + }; + + //核心入口 + openTable.render = function (options) { + var ins = new Class(options); + return thisIns.call(ins); + }; + + //加载组件所需样式 + layui.link(layui.cache.base + "openTable/openTable.css?v="+(new Date).getTime(), function () { + //此处的“openTable”要对应 openTable.css 中的样式: html #layuicss-openTable{} + }, 'openTable'); + + exports('openTable', openTable); +}); diff --git a/public/js/lay-module/openTable/right.svg b/public/js/lay-module/openTable/right.svg new file mode 100644 index 0000000..633299e --- /dev/null +++ b/public/js/lay-module/openTable/right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/js/lay-module/passwordIntensity/passwordIntensity.js b/public/js/lay-module/passwordIntensity/passwordIntensity.js new file mode 100644 index 0000000..69e437f --- /dev/null +++ b/public/js/lay-module/passwordIntensity/passwordIntensity.js @@ -0,0 +1,316 @@ +/*! + * passwordIntensity v1.0 + * author JerryZst + * qq 1309579432 + * Date: 2021/01/20 0007 + */ +layui.define(['jquery', 'element'], function (exports) { + var $ = layui.jquery; + var element = layui.element; + var _MOD = 'passwordIntensity'; + var passwordIntensity = function (opt) { + this.version = 'passwordIntensity-1.0'; + this.tmpId = new Date().getTime(); + this.tmpId = opt.uniqueId ? opt.uniqueId : this.tmpId + Math.round(Math.random() * 1000 + 9999); + this.passwordLevel = [ + 'low', + 'middle', + 'midhigh', + 'high', + ] + this._level = null; + // 配置项 + this.options = $.extend(true, { + data: opt.data || '', + chang: opt.chang || 8, + on: opt.on || null, + }, opt); + this.init(); // 初始化 + this.bindEvents(); // 绑定事件 + }; + + /** 获取各个组件 */ + passwordIntensity.prototype.getComponents = function () { + var that = this; + var $elem = $(that.options.elem); + var filter = $elem.attr('lay-filter'); + if (!filter) { + filter = that.options.elem.substring(1); + $elem.attr('lay-filter', filter); + } + return { + $elem: $elem, // 容器 + filter: filter, // 容器的lay-filter + }; + }; + + // 初始化 + passwordIntensity.prototype.init = function () { + var components = this.getComponents(); + var html = ""; + for (var i = 0; i < this.passwordLevel.length; i++) { + var color = ""; + var name = ""; + var levelId = this.tmpId + this.passwordLevel[i]; + if (this.passwordLevel[i] === 'low') { + name = "弱"; + color = "layui-bg-red"; + } else if (this.passwordLevel[i] === 'middle') { + name = "中"; + color = "layui-bg-orange"; + } else if (this.passwordLevel[i] === 'midhigh') { + name = "中上"; + color = "layui-bg-orange"; + } else { + name = "强"; + color = "layui-bg-green"; + } + html += '
            ' + + '
            ' + + '
            ' + '
            ' + + '
            ' + '' + name + '
            '; + } + html = '
            ' + html + '
            '; + components.$elem.parent('div').append(html); + if (this.options.data) { + components.$elem.val(this.options.data); + var that = this; + var _level = checkStrong(this.options.data,this.options.chang); + switch (_level) { + case 0: + that.setLevel('low', '100%'); + break; + case 1: + that.setLevel('low', '100%'); + break; + case 2: + that.setLevel('middle', '100%'); + break; + case 3: + that.setLevel('midhigh', '100%'); + break; + default: + that.setLevel('high', '100%'); + break; + } + } + return html; + } + + /** + * 绑定事件 + */ + passwordIntensity.prototype.bindEvents = function () { + var components = this.getComponents(); + var that = this; + // 实时输入事件 + components.$elem.off('input propertychange').on('input propertychange', function () { + var pwd = $(this).val(); + if (pwd == "" || pwd == null) { + that.setLevel(''); + that._level = null; + } else { + var _level = checkStrong(pwd,that.options.chang); + that._level = _level; + switch (_level) { + case 0: + that.setLevel('low', '100%'); + break; + case 1: + that.setLevel('low', '100%'); + break; + case 2: + that.setLevel('middle', '100%'); + break; + case 3: + that.setLevel('midhigh', '100%'); + break; + default: + that.setLevel('high', '100%'); + break; + } + } + }); + + // 光标消失事件 + components.$elem.off('blur').on('blur', function () { + var pwd = $(this).val(); + if (pwd == "" || pwd == null) { + that.setLevel(''); + that._level = null; + } else { + var _level = checkStrong(pwd,that.options.chang); + that._level = _level; + switch (_level) { + case 0: + that.setLevel('low', '100%'); + break; + case 1: + that.setLevel('low', '100%'); + break; + case 2: + that.setLevel('middle', '100%'); + break; + case 3: + that.setLevel('midhigh', '100%'); + break; + default: + that.setLevel('high', '100%'); + break; + } + } + }); + } + + /** + * 设置级别 + */ + passwordIntensity.prototype.setLevel = function (level, value) { + value = value || '100%'; + for (var i = 0; i < this.passwordLevel.length; i++) { + if (level === '') { + element.progress(this.tmpId + this.passwordLevel[i], '0%'); + } else { + if (level === this.passwordLevel[i]) { + element.progress(this.tmpId + level, value); + } else { + if (value === '100%') { + element.progress(this.tmpId + this.passwordLevel[i], '0%'); + } + } + } + } + return true; + } + + /** + * 返回当前的密码级别 + * @returns {string} + */ + passwordIntensity.prototype.getLevel = function () { + return _levelByName(this._level); + } + + /** + * 当前密码是否弱 + * @returns {boolean} + */ + passwordIntensity.prototype.isLow = function () { + return _levelByName(this._level) === 'low' || this._level === null; + } + + /** + * 当前密码是否中 + * @returns {boolean} + */ + passwordIntensity.prototype.isMiddle = function () { + return _levelByName(this._level) === 'middle'; + } + + /** + * 当前密码是否强 + * @returns {boolean} + */ + passwordIntensity.prototype.isHigh = function () { + return _levelByName(this._level) === 'high'; + } + /** + * 当前密码是否强 + * @returns {boolean} + */ + passwordIntensity.prototype.ismidHigh = function () { + return _levelByName(this._level) === 'midhigh'; + } + + /** + * 级别转换 + * @param level + * @returns {string} + * @private + */ + function _levelByName(level) { + var name = 'low'; + switch (level) { + case 0: + name = "low"; + break; + case 1: + name = "low"; + break; + case 2: + name = "middle"; + //that.setLevel('middle', '100%'); + break; + case 3: + name = "midhigh"; + //that.setLevel('midhigh', '100%'); + break; + default: + name = "high"; + break; + } + return name; + } + + /** + * 监听事件 // 预留 + * @param events + * @param callback + * @returns {*} + */ + passwordIntensity.prototype.on = function (events, callback) { + return layui.onevent.call(this, _MOD, events, callback); + }; + + + //判断输入密码的类型 + function charMode(iN) { + if (iN >= 48 && iN <= 57) //数字 + return 1; + if (iN >= 65 && iN <= 90) //大写 + return 2; + if (iN >= 97 && iN <= 122) //小写 + return 4; + else + return 8; + } + + //计算密码模式 + function bitTotal(num) { + modes = 0; + for (i = 0; i < 4; i++) { + if (num & 1) modes++; + num >>>= 1; + } + return modes; + } + + //返回强度级别 + function checkStrong(sPW,chang) { + if (sPW.length <= 6) { + return 0; //密码太短 + } + if(sPW.length < chang){ + return 0; + } + Modes = 0; + for (i = 0; i < sPW.length; i++) { + //密码模式 + Modes |= charMode(sPW.charCodeAt(i)); + } + return bitTotal(Modes); + } + + /** 外部方法 */ + var iS = { + /* 渲染 */ + render: function (options) { + return new passwordIntensity(options); + }, + /* 事件监听 */ + on: function (events, callback) { + return layui.onevent.call(this, _MOD, events, callback); + } + }; + exports(_MOD, iS); +}); \ No newline at end of file diff --git a/public/js/lay-module/regionCheckBox/regionCheckBox.css b/public/js/lay-module/regionCheckBox/regionCheckBox.css new file mode 100644 index 0000000..11c2948 --- /dev/null +++ b/public/js/lay-module/regionCheckBox/regionCheckBox.css @@ -0,0 +1,59 @@ +/** + @ Name:layui.regionCheckBox 中国省市复选框 + @ Author:wanmianji + */ + +html #layuicss-regionCheckBox { + display: none; + position: absolute; + width: 1989px; +} + +.layui-regionContent { + margin: 6px; + border: 1px solid #e6e6e6; +} + +.layui-regionContent .all { + margin-left: 15px; +} + +.layui-regionContent .area { + margin-bottom: 0; +} + +.layui-regionContent .area label { + width: 45px; +} + +.layui-regionContent .area .province { + margin-left: 75px; +} + +.layui-regionContent .province ul li { + float: left; + position: relative; + border: 1px solid #fff; + padding: 9px 0 9px 15px; +} + +.layui-regionContent .province ul li .layui-form-checkbox[lay-skin=primary] { + margin-top: 0; +} + +.layui-regionContent .province ul li.parent:hover { + border: 1px solid #e4e4e4; +} + +.layui-regionContent .province .city{ + position: absolute; + background: #fff; + width: 330px; + left: -1px; + top: 100%; + z-index: 2; + line-height: 26px; + border: 1px solid #e4e4e4; + padding: 6px 0 6px 15px; + display: none; +} \ No newline at end of file diff --git a/public/js/lay-module/regionCheckBox/regionCheckBox.js b/public/js/lay-module/regionCheckBox/regionCheckBox.js new file mode 100644 index 0000000..ede990a --- /dev/null +++ b/public/js/lay-module/regionCheckBox/regionCheckBox.js @@ -0,0 +1,292 @@ +/** + @ Name:layui.regionCheckBox 中国省市复选框 + @ Author:wanmianji + */ +layui.define('form', function(exports){ + + 'use strict'; + + var $ = layui.$ + ,form = layui.form + ,MOD_NAME = 'regionCheckBox', ELEM_CLASS = 'layui-regionContent' + ,regionCheckBox = { + index: layui.regionCheckBox ? (layui.regionCheckBox.index + 10000) : 0 + + ,set: function(options){ + var that = this; + that.config = $.extend({}, that.config, options); + return that; + } + + ,on: function(events, callback){ + return layui.onevent.call(this, MOD_NAME, events, callback); + } + } + ,thisIns = function(){ + var that = this + ,options = that.config + ,id = options.id || options.index; + + return { + reload: function(options){ + that.config = $.extend({}, that.config, options); + that.render(); + } + ,val: function(valueArr){ + setValue(options, valueArr); + } + ,config: options + }; + } + ,Class = function(options){ + var that = this; + that.index = ++regionCheckBox.index; + that.config = $.extend({}, that.config, regionCheckBox.config, options); + that.render(); + }; + + + Class.prototype.config = { + data: [] + ,all: ['所有地域', '所有地域'] + ,value: [] + ,width: '550px' + ,border: true + ,change: function(result){} + ,ready: function(){} + }; + + Class.prototype.render = function(){ + var that = this + ,options = that.config; + + options.elem = $(options.elem); + var id = options.elem.attr('id'); + + if(!options.elem.hasClass('layui-form')){ + options.elem.addClass('layui-form'); + } + options.elem.addClass(ELEM_CLASS); + options.elem.css('width', options.width); + if(!options.border){ + options.elem.css('border', 'none'); + } + options.elem.attr('lay-filter', 'region-' + id); + + options.elem.html(getCheckBoxs(options)); + + //初始值 + setValue(options, options.value); + + options.elem.find('.parent').mouseover(function() { + $(this).find('.city').show(); + }); + options.elem.find('.parent').mouseout(function() { + $(this).find('.city').hide(); + }); + + form.on('checkbox(regionCheckBox-'+id+')', function(data) { + if($(data.elem).parents('.all').length > 0) { //选择全部 + if(data.elem.checked) { + options.elem.find(':checkbox').prop('checked', true); + } else { + options.elem.find(':checkbox').prop('checked', false); + } + } else { + //选择省(不包括直辖市) + if($(data.elem).parent().hasClass('parent')) { + if(data.elem.checked) { + $(data.elem).parent().find('.city :checkbox').prop('checked', true); + } else { + $(data.elem).parent().find('.city :checkbox').prop('checked', false); + } + } + //选择城市 + if($(data.elem).parent().hasClass('city')) { + $(data.elem).parents('.parent').attr('name', options.name); + if(data.elem.checked) { + var is_all = true; + $(data.elem).parent().find(':checkbox').each(function(i, item) { + if(! item.checked) { + is_all = false; + return false; + } + }); + if(is_all) { + $(data.elem).parents('.parent').find(':checkbox:first').prop('checked', true); + } + } else { + $(data.elem).parents('.parent').find(':checkbox:first').prop('checked', false); + } + } + //选择除全部外任意 + if(data.elem.checked) { + var is_all = true; + options.elem.find('.province :checkbox').each(function(i, item) { + if(! item.checked) { + is_all = false; + return false; + } + }); + if(is_all) { + options.elem.find('.all :checkbox').prop('checked', true); + } + } else { + options.elem.find('.all :checkbox').prop('checked', false); + } + } + form.render('checkbox', options.elem.attr('lay-filter')); + + renderParentDom(options.elem); + initName(options); + + options.change(data); + }); + + options.ready(); + } + + function getCheckBoxs(options){ + var data = options.data, + all = options.all, + name = options.name, + id = options.elem.attr('id'), + skin = 'primary', + filter = 'regionCheckBox-' + id, + boxs = '', + hasArea = true; + + if(all != null && all.length == 2){ + boxs = '
            ' + + '' + + '
            ' + boxs; + } + + if(data[0].TYPE == 'province'){ + hasArea = false; + } + + if(!hasArea){ + boxs += '
            ' + + '
            ' + + '
              '; + } + + for(var i=0; i' + area.TITLE + '' + + '
              ' + + '
                '; + + var provinceList = area.CHILDREN; + for(var j=0; j 0 ? ' class="parent"' : '') + '>' + + ''; + + if(city_num > 0){ + li += '
                '; + for(var k=0; k'; + } + li += '
                '; + } + + li += ''; + + return li; + } + + function setValue(options, valueArr){ + options.elem.find(':checkbox').prop('checked', false); + var all_value = options.elem.find('.all :checkbox').val(); + if(valueArr.indexOf(all_value) > -1){ + options.elem.find(':checkbox').prop('checked', true); + }else{ + if(typeof valueArr == 'string'){ + valueArr = valueArr.split(','); + } + for(var i=0; i'; + html += '
              '; + html += '
              '; + html += '
              '; + html += '
              '; + html += '
              '+tips+'
              '; + for(var i=0;i'; + html += '
              '; + html += ''+d[i][f.titleName]+''; + html += '
              '; + html +=''; + } + html += '
              '; + html += '
            '; + $E.html(html); + } + + //设置选中值 + this.set = function(selected){ + var o=this,c=o.config; + var s = typeof selected=='undefined' ? c.selected : selected; + $E = $(c.elem); + $E.find('.layui-form-checkbox').removeClass('layui-form-checked'); + $E.find('dd').removeClass('layui-this'); + //为默认选中值添加类名 + var max = s.length>c.max ? c.max : s.length; + for(var i=0;i'+tips+''); + } + else{ + items.each(function(){ + $this = $(this); + var item ={}; + var v = $this.attr('lay-value'); + var n = $this.find('span').text(); + item[f.idName] = v; + item[f.titleName] = n; + values.push(v); + names.push(n); + spans.push(''+n+''); + selected.push(item); + }); + } + spans.push(''); + $E.find('.multiple').html(spans.join('')); + $E.find('.layui-select-title').find('input').each(function(){ + if(typeof first=='undefined'){ + this.defaultValue = values.join(c.delimiter); + } + this.value = values.join(c.delimiter); + }); + + var h = $E.find('.multiple').height()+14; + $E.find('.layui-form-select dl').css('top',h+'px'); + o.values=values,o.names=names,o.selected = selected; + } + //ajax方式获取候选数据 + this.getData = function(url){ + var d; + $.ajax({ + url:url, + dataType:'json', + async:false, + success:function(json){ + d=json; + }, + error: function(){ + console.error(MOD_NAME+' hint:候选数据ajax请求错误 '); + d = false; + } + }); + return d; + } + }; + //渲染一个实例 + obj.prototype.render = function(){ + var o=this,c=o.config,f=c.field; + $E = $(c.elem); + if($E.length==0){ + console.error(MOD_NAME+' hint:找不到容器 ' +c.elem); + return false; + } + if(Object.prototype.toString.call(c.data)!='[object Array]'){ + var data = o.getData(c.data); + if(data===false){ + console.error(MOD_NAME+' hint:缺少分类数据'); + return false; + } + o.config.data = data; + } + + //给容器添加一个类名 + $E.addClass('lay-ext-mulitsel'); + if(/^\d+$/.test(c.width)){ + $E.css('width',c.width+'px'); + } + //添加专属的style + if($('#lay-ext-mulitsel-style').length==0){ + var style = '.lay-ext-mulitsel .layui-form-select dl dd div{margin-top:0px!important;}.lay-ext-mulitsel .layui-form-select dl dd.layui-this{background-color:#fff}.lay-ext-mulitsel .layui-input.multiple{line-height:auto;height:auto;padding:4px 10px 4px 10px;overflow:hidden;min-height:38px;margin-top:-38px;left:0;z-index:99;position:relative;background:#fff;}.lay-ext-mulitsel .layui-input.multiple a{padding:2px 5px;background:#5FB878;border-radius:2px;color:#fff;display:block;line-height:20px;height:20px;margin:2px 5px 2px 0;float:left;}.lay-ext-mulitsel .layui-input.multiple a i{margin-left:4px;font-size:14px;} .lay-ext-mulitsel .layui-input.multiple a i:hover{background-color:#009E94;border-radius:2px;}.lay-ext-mulitsel .danger{border-color:#FF5722!important}.lay-ext-mulitsel .tips{pointer-events: none;position: absolute;left: 10px;top: 10px;color:#757575;}'; + $('').text(style).appendTo($('head')); + }; + + //创建选项 + o.createOption(); + //设置选中值 + o.set(); + + //展开/收起选项 + $E.on('click','.layui-select-title,.multiple,.multiple.layui-edge',function(e){ + //隐藏其他实例显示的弹层 + $('.lay-ext-mulitsel').not(c.elem).removeClass('layui-form-selected'); + if($(c.elem).is('.layui-form-selected')){ + $(c.elem).removeClass('layui-form-selected'); + + $(document).off('click',mEvent); + } + else{ + $(c.elem).addClass('layui-form-selected'); + + $(document).on('click',mEvent=function(e){ + if(e.target.id!==c.elem && e.target.className!=='layui-input multiple'){ + $(c.elem).removeClass('layui-form-selected'); + $(document).off('click',mEvent); + } + }); + } + }); + + //点击选项 + $E.on('click','dd',function(e){ + var _dd = $(this); + if(_dd.hasClass('layui-disabled')){ + return false; + } + //点 请选择 + if(_dd.is('.layui-select-tips')){ + _dd.siblings().removeClass('layui-this'); + $(c.elem).find('.layui-form-checkbox').removeClass('layui-form-checked'); + } + //取消选中 + else if(_dd.is('.layui-this')){ + _dd.removeClass('layui-this'); + _dd.find('.layui-form-checkbox').removeClass('layui-form-checked'); + e.stopPropagation(); + } + //选中 + else{ + if(o.selected.length >= c.max){ + $(c.elem+' .multiple').addClass('danger'); + layer.tips('最多只能选择 '+c.max+' 个', c.elem+' .multiple', { + tips: 3, + time: 1000, + end:function(){ + $(c.elem+' .multiple').removeClass('danger'); + } + }); + return false; + } + else{ + _dd.addClass('layui-this'); + _dd.find('.layui-form-checkbox').addClass('layui-form-checked'); + e.stopPropagation(); + } + } + + o.setSelected(); + }); + + //删除选项 + $E.on('click','a i',function(e){ + var _this = $(this).prev('span'); + var v = _this.attr('lay-value'); + if(v){ + var _dd = $(c.elem).find('dd[lay-value='+v+']'); + _dd.removeClass('layui-this'); + _dd.find('.layui-form-checkbox').removeClass('layui-form-checked'); + } + o.setSelected(); + _this.parent().remove(); + e.stopPropagation(); + + }); + + //验证失败样式 + $E.find('input').focus(function(){ + $(c.elem+' .multiple').addClass('danger'); + setTimeout(function(){ + $(c.elem+' .multiple').removeClass('danger'); + },3000); + }); + } + + //输出模块 + exports(MOD_NAME, function (config) { + var _this = new obj(config); + _this.render(); + return _this; + }); +}); \ No newline at end of file diff --git a/public/js/lay-module/selectN/selectN.js b/public/js/lay-module/selectN/selectN.js new file mode 100644 index 0000000..df02610 --- /dev/null +++ b/public/js/lay-module/selectN/selectN.js @@ -0,0 +1,279 @@ +/* +* @version: 1.2 +* @Author: tomato +* @Date: 2018-4-24 22:56:00 +* @Last Modified by: tomato +* @Last Modified time: 2018-5-26 18:08:43 +*/ +//无限级下拉框 +layui.define(['jquery', 'form'], function(exports){ + var MOD_NAME = 'selectN'; + var $ = layui.jquery; + var form = layui.form; + var obj = function(config){ + //当前选中数据值名数据 + this.selected =[]; + //当前选中的值 + this.values = []; + //当前选中的名 + this.names = []; + //当前选中最后一个值 + this.lastValue = ''; + //当前选中最后一个值 + this.lastName = ''; + //是否已选 + this.isSelected = false; + //初始化配置 + this.config = { + //选择器id或class + elem: '', + //无限级分类数据 + data: [], + //默认选中值 + selected: [], + //空值项提示,可设置为数组['请选择省','请选择市','请选择县'] + tips: '请选择', + //是否允许搜索,可设置为数组[true,true,true] + search:false, + //选择项宽度,可设置为数组['80','90','100'] + width:null, + //为真只取最后一个值 + last: false, + //值验证,与lay-verify一致 + verify: '', + //事件过滤器,lay-filter名 + filter: '', + //input的name 不设置与选择器相同(去#.) + name: '', + //数据分隔符 + delimiter: ',', + //数据的键名 status=0为禁用状态 + field:{idName:'id',titleName:'name',statusName:'status',childName:'children'}, + //多表单区分 form.render(type, filter); 为class="layui-form" 所在元素的 lay-filter="" 的值 + formFilter: null + } + + //实例化配置 + this.config = $.extend(this.config,config); + + //“请选择”文字 + this.setTips = function(){ + var o = this,c = o.config; + if(Object.prototype.toString.call(c.tips)!='[object Array]'){ + return c.tips; + } + else{ + var i=$(c.elem).find('select').length; + return c.tips.hasOwnProperty(i) ? c.tips[i] : '请选择'; + } + } + + //设置是否允许搜索 + this.setSearch = function(){ + var o = this,c = o.config; + if(Object.prototype.toString.call(c.search)!='[object Array]'){ + return c.search===true ? 'lay-search ' : ' '; + } + else{ + var i=$(c.elem).find('select').length; + if(c.search.hasOwnProperty(i)){ + return c.search[i]===true ? 'lay-search ' : ' '; + } + } + return ' '; + } + + //设置是否允许搜索 + this.setWidth = function(){ + var o = this,c = o.config; + if(Object.prototype.toString.call(c.width)!='[object Array]'){ + return /^\d+$/.test(c.width) ? 'style="width:'+c.width+'px;" ' : ' '; + } + else{ + var i=$(c.elem).find('select').length; + if(c.width.hasOwnProperty(i)){ + return /^\d+$/.test(c.width[i]) ? 'style="width:'+c.width[i]+'px;" ' : ' '; + } + } + } + + //创建一个Select + this.createSelect = function(optionData){ + var o = this,c = o.config,f=c.field; + var html = ''; + html+= '
            '; + html+= ' '; + html+= '
            '; + return html; + }; + + //获取当前option的数据 + this.getOptionData=function(catData,optionIndex){ + var f = this.config.field; + var item = catData; + for(var i=0;i
            '; + html += o.createSelect(c.data); + $E.html(html); + selected = typeof selected=='undefined' ? c.selected : selected; + var index=[]; + for(var i=0;i 0) { + // obj.children = temp; + // } + // tree.push(obj); + // } + // } + // return tree; + // }; + this.getChild = function(data,pid){ + var child = []; + for (var i = 0; i < data.length; i++) { + if (data[i].pid == pid) { + child.push(data[i]); + } + } + return child; + }; + this.createChild = function(items){ + var o = this; + var ul = '
              '; + for(var i = 0; i < items.length; i++){ + var row = o.getChild(o.data,items[i].id); + row = row.length != 0 ? 'child-row' : ''; + var off = items[i].off ? 'layui-disabled' : ''; + ul += '
            • '+items[i].name+'
            • ' + } + ul += '
            '; + return ul; + }; + this.setValue = function (value) { + var o = this, data = o.data, itemID = []; + for(var i=0;i i && x.eq(j).scrollTop(t + x.eq(j).scrollTop() - i + a + 3), + t < 0 && x.eq(j).scrollTop(t + x.eq(j).scrollTop() - 5) + } + } + }; + this.data = this.c.url ? this.getData(this.c.url) : this.c.data; + } + P.prototype.render = function(){ + var o = this, c = o.c, e = c.elem; + var html = '
            ', + show = ''+c.placeholder+'', + pop = '
            '; + $(e).wrap(html); + $(e).after(show + pop); + + o.showPopup(o.data,0); + + if($(e).val()!=''){ + var value = $(e).val().split('/'); + var showValue = value.join('/'); + $(e).parent().find('.show').html(showValue); + + var setID = this.setValue(value); + for(var i = 0; i < setID.length; i ++){ + o.showPopup(o.data,setID[i]); + var fistUi = $(e).parent().find('.pop ul'); + fistUi.eq(i).find('li[data-id='+setID[i]+']').addClass('active'); + + } + } + + $(e).parent().find('.pop').on('click','ul li',function (en) { + if($(this).hasClass("layui-disabled")){ + layer.msg(c.disabledTips); + return false; + } + $(this).addClass('active').siblings().removeClass('active'); + $(this).parent().nextAll('ul').remove(); + var id = $(this).data('id'); + o.showPopup(o.data,id); + if(!$(this).hasClass("child-row")){ + $(e).parent().find('.pop').hide(); + var activeLi = $(e).parent().find('.pop li.active'); + var checkValue = [], ids = []; + for(var i = 0; i < activeLi.length; i ++){ + checkValue.push(activeLi.eq(i).html()); + ids.push(activeLi.eq(i).data('id')) + } + var inputValue = checkValue.join('/'); + var showValue = checkValue.join('/'); + $(e).val(inputValue); + $(e).parent().find('.show').html(showValue); + + //回调函数 + if (typeof c.success === "function") { + var callback = { + //数据name 如安徽省/合肥市/蜀山区 + data:inputValue, + //数据ID数组,如1,8,16 + ids:ids + }; + c.success(callback); + } + } + layui.stope(en); + }); + + $(e).parent().find('.show').on('click',function(en){ + //if() + $(e).parent().find('.pop').show(); + o.scroll(); + layui.stope(en); + }) + $(document).on('click',function(){ + $(e).parent().find('.pop').hide(); + }) + + + } + exports('selectY', function(o){ + var t = new P(o); + t.render(); + }); +}).link(layui.cache.base+"selectY/selectY.css?v="+(new Date).getTime()); \ No newline at end of file diff --git a/public/js/lay-module/selectY/selectY2.js b/public/js/lay-module/selectY/selectY2.js new file mode 100644 index 0000000..164f185 --- /dev/null +++ b/public/js/lay-module/selectY/selectY2.js @@ -0,0 +1,233 @@ +layui.define('layer', function(exports) { + var $ = layui.$, layer = layui.layer; + 'use strict'; + + var SelectY2 = function(o) { + this.c = { + elem: '', + data: '', + url: '', + placeholder: '请选择:', + disabledTips: '', + success: function() {} + }; + this.c = $.extend(this.c, o); + this.init(); + }; + + SelectY2.prototype = { + init: function() { + this.data = this.c.url ? this.getData(this.c.url) : this.c.data; + this.render(); + }, + + getData: function(url) { + var res; + $.ajax({ + url: url, + type: "get", + dataType: 'json', + async: false, + success: function(e) { + res = e.data || layer.msg('数据接口错误,请正确配置'); + }, + error: function() { + layer.msg('数据接口错误,请正确配置'); + res = ''; + } + }); + return res; + }, + + getChild: function(data, pid) { + var child = []; + for (var i = 0; i < data.length; i++) { + if (data[i].pid == pid) { + child.push(data[i]); + } + } + return child; + }, + + createChild: function(items) { + var o = this; + var ul = '
              '; + for (var i = 0; i < items.length; i++) { + var row = o.getChild(o.data, items[i].id); + row = row.length != 0 ? 'child-row' : ''; + var off = items[i].off ? 'layui-disabled' : ''; + ul += '
            • ' + items[i].name + '
            • '; + } + ul += '
            '; + return ul; + }, + + setValue: function(value) { + var o = this, data = o.data; + var elem = $(o.c.elem); + var parent = elem.parent(); + + // 清空当前选中状态和弹出层内容 + parent.find('.pop').empty(); + parent.find('.show').html(o.c.placeholder); + elem.val(''); + + // 查找匹配的值路径 + var path = []; + var currentItems = o.getChild(data, 0); // 从顶级开始 + + for (var i = 0; i < value.length; i++) { + var found = false; + for (var j = 0; j < currentItems.length; j++) { + if (currentItems[j].name === value[i]) { + path.push(currentItems[j]); + currentItems = o.getChild(data, currentItems[j].id); + found = true; + break; + } + } + if (!found) { + break; // 如果没找到匹配项,中断循环 + } + } + + if (path.length > 0) { + // 重建弹出层结构 + parent.find('.pop').empty(); + for (var k = 0; k < path.length; k++) { + var items = o.getChild(data, k === 0 ? 0 : path[k-1].id); + var itemsHtml = o.createChild(items); + parent.find('.pop').append(itemsHtml); + + // 激活当前层级的选择项 + var currentUl = parent.find('.pop ul').eq(k); + currentUl.find('li[data-id="' + path[k].id + '"]').addClass('active'); + } + + // 添加下一级(如果有) + var lastItem = path[path.length - 1]; + var childItems = o.getChild(data, lastItem.id); + if (childItems.length > 0) { + var childHtml = o.createChild(childItems); + parent.find('.pop').append(childHtml); + } + + // 更新显示值和输入框值 + var showValue = path.map(function(item) { return item.name; }).join('/'); + var inputValue = path.map(function(item) { return item.name; }).join('/'); + + parent.find('.show').html(showValue); + elem.val(inputValue); + + // 调用成功回调 + if (typeof o.c.success === "function") { + var ids = path.map(function(item) { return item.id; }); + var vvv = path.map(function(item){return item.name;}); + o.c.success({ + data: vvv, + ids: ids + }); + } + + // 返回 ID 数组(关键修改) + return path.map(function(item) { return item.id; }); + } + + return []; // 如果没有匹配项,返回空数组 + }, + + showPopup: function(data, id) { + var o = this; + var items = o.getChild(data, id); + if (items.length != 0) { + var itemsHtml = o.createChild(items); + $(o.c.elem).parent().find('.pop').append(itemsHtml); + } + }, + + scroll: function() { + var o = this, c = o.c, ele = c.elem; + var x = $(ele).parent().find('.pop ul'); + for (var j = 0; j < x.length; j++) { + var e = x.eq(j).children('li.active'); + if (e[0]) { + var t = e.position().top, i = x.eq(j).height(), a = e.height(); + t > i && x.eq(j).scrollTop(t + x.eq(j).scrollTop() - i + a + 3), + t < 0 && x.eq(j).scrollTop(t + x.eq(j).scrollTop() - 5) + } + } + }, + + render: function() { + var o = this, c = o.c, e = c.elem; + var html = '
            ', + show = '' + c.placeholder + '', + pop = '
            '; + $(e).wrap(html); + $(e).after(show + pop); + + o.showPopup(o.data, 0); + + if ($(e).val() != '') { + var value = $(e).val().split('/'); + var showValue = value.join('/'); + $(e).parent().find('.show').html(showValue); + + // 获取 setValue 返回的 ID 数组 + var setID = o.setValue(value); + for (var i = 0; i < setID.length; i++) { + o.showPopup(o.data, setID[i]); + var fistUi = $(e).parent().find('.pop ul'); + fistUi.eq(i).find('li[data-id=' + setID[i] + ']').addClass('active'); + } + } + + $(e).parent().find('.pop').on('click', 'ul li', function(en) { + if ($(this).hasClass("layui-disabled")) { + layer.msg(c.disabledTips); + return false; + } + $(this).addClass('active').siblings().removeClass('active'); + $(this).parent().nextAll('ul').remove(); + var id = $(this).data('id'); + o.showPopup(o.data, id); + if (!$(this).hasClass("child-row")) { + $(e).parent().find('.pop').hide(); + var activeLi = $(e).parent().find('.pop li.active'); + var checkValue = [], ids = []; + for (var i = 0; i < activeLi.length; i++) { + checkValue.push(activeLi.eq(i).html()); + ids.push(activeLi.eq(i).data('id')) + } + var inputValue = checkValue.join('/'); + var showValue = checkValue.join('/'); + $(e).val(inputValue); + $(e).parent().find('.show').html(showValue); + + if (typeof c.success === "function") { + var callback = { + data: checkValue, + ids: ids + }; + c.success(callback); + } + } + layui.stope(en); + }); + + $(e).parent().find('.show').on('click', function(en) { + $(e).parent().find('.pop').show(); + o.scroll(); + layui.stope(en); + }); + + $(document).on('click', function() { + $(e).parent().find('.pop').hide(); + }); + } + }; + + exports('selectY2', function(o) { + return new SelectY2(o); + }); +}).link(layui.cache.base + "selectY/selectY.css?v=" + (new Date).getTime()); \ No newline at end of file diff --git a/public/js/lay-module/sliderTime/sliderTime.css b/public/js/lay-module/sliderTime/sliderTime.css new file mode 100644 index 0000000..3d83996 --- /dev/null +++ b/public/js/lay-module/sliderTime/sliderTime.css @@ -0,0 +1,168 @@ +@charset "UTF-8"; +/*sliderTime*/ +.layui-slider-bar-selected { + position: absolute; + background-color: #696969; + height: 100%; + z-index: 1; +} + +.step-hour { + height: 20px !important; + width: 1px !important; + top: -16px !important; +} + +.step-half-hour { + height: 10px !important; + width: 1px !important; + top: -6px !important; +} + +.step-hour, +.step-half-hour, +.step-start-wrap, +.step-end-wrap { + border-radius: 0; + border-radius: unset; +} + +.layui-slider-step { + border-radius: 0; + height: 6px; + width: 1px; + top: -2px; +} + +.layui-slider-bar { + z-index: 1; +} + +.step-start-num { + position: absolute; + left: -18px; + top: 8px; +} + +.step-end-num { + position: absolute; + right: -15px; + top: 6px; +} + +.step-start-wrap { + position: absolute; + left: 0px; + top: -16px; + height: 20px; + width: 2px; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); +} + +.step-end-wrap { + position: absolute; + right: -2px; + top: -16px; + height: 20px; + width: 2px; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); +} + +.slider-danger-tips { + position: absolute; + top: 12px; + z-index: 66666666; + white-space: nowrap; + display: none; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); + border-radius: 3px; + height: 25px; + line-height: 25px; + padding: 0 10px; +} + +.layui-disabled .step-hour, .layui-disabled .step-half-hour, .layui-disabled .step-start-wrap, .layui-disabled .step-end-wrap, .layui-disabled .layui-slider-step { + background: #696969; +} + +/*垂直*/ +.layui-slider-vertical .step-start-num { + left: -16px; + top: inherit; + top: unset; + bottom: -22px; +} + +.layui-slider-vertical .step-end-num { + top: -22px; + right: inherit; + right: unset; + left: -16px; +} + +.layui-slider-vertical .step-start-wrap { + height: 2px; + width: 20px; + bottom: 0; + top: inherit; + top: unset; + left: 10px; +} + +.layui-slider-vertical .step-end-wrap { + height: 2px; + width: 20px; + right: inherit; + right: unset; + left: 10px; + top: 0; +} + +.layui-slider-vertical .step-hour { + height: 1px !important; + width: 20px !important; + top: inherit !important; + top: unset !important; +} + +.layui-slider-vertical .step-half-hour{ + height: 1px !important; + width: 10px !important; + top: inherit !important; + top: unset !important; +} +.layui-slider-vertical .layui-slider-step{ + height: 1px; + width: 4px; +} +.layui-slider-vertical .layui-slider-bar-selected{ + width: 4px; + bottom: 150px; + height: 37.5%; +} +.layui-slider-vertical .slider-danger-tips{ + top: inherit; + top: unset; + left: 100px; +} + + +/*主题颜色*/ +.step-hour, +.step-half-hour, +.step-start-wrap, +.step-end-wrap { + background: #009688 +} + +.layui-slider-step { + background: #00bfb1; +} + +.slider-danger-tips { + color: #FFF; + background: #FF5722; +} \ No newline at end of file diff --git a/public/js/lay-module/sliderTime/sliderTime.js b/public/js/lay-module/sliderTime/sliderTime.js new file mode 100644 index 0000000..6ad1651 --- /dev/null +++ b/public/js/lay-module/sliderTime/sliderTime.js @@ -0,0 +1,327 @@ +/** + * 时间范围滑块选择插件 + * 基于 layui.slider 扩展的时间范围选择插件 + * + * sliderTime.js License By momo + * V1.0 2021-04-19 + * + * + * 使用说明: + * 1.sliderTime 基于 layui.slider,所以 slider 的配置都得以保留 + * 2.range 参数强制为 true + * 3.新增 disabledValue 属性,禁止选择时间范围 + * 例如:[[8:00, 10:00], [14:00, 15:00]] + * 4.新增 disabledText 属性,禁止选择时间范围被选中后的提示内容,不配置不显示提示 + * 例如:'{start} - {end} 已占用',支持start和end时间占位符 + * 5.新增 getValue 方法,返回当前选择的时间段(时间格式) + * 6.新增 isOccupation 方法,返回当前选择的时间段是否包含了禁止选择的时间段 + * 7.新增 numToTime 公用方法,用于将十进制转为时间格式。例如:480 → 08:00 + * 8.新增 timeToNum 公用方法,用于将时间格式转为十进制。例如:24:00 → 1440 + */ +layui.define(['slider'], function (exports) { + "use strict"; + var MOD_NAME = 'sliderTime', + $ = layui.jquery, + slider = layui.slider; + ; + + /** + * 外部接口 + */ + var SliderTime = { + config: {} + /** + * 核心入口 + */ + , render: function (options) { + var object = new Class(options); + return thisSliderTime.call(object); + } + + /** + * 数字转时间格式 + */ + , numToTime: function (value) { + var hours = Math.floor(value / 60); + var mins = (value - hours * 60); + return (hours < 10 ? "0" + hours : hours) + ":" + (mins < 10 ? "0" + mins : mins); + } + + /** + * 时间转数字 + */ + , timeToNum: function (value) { + var time = value.split(":"); + return time[0] * 60 + parseInt(time[1]); + } + }; + + /** + * 构造器 + */ + var Class = function (options) { + var that = this; + that.config = $.extend({}, SliderTime.config, options); + that.render(); + }; + + /** + * 操作当前实例 + */ + var thisSliderTime = function () { + var that = this + , options = that.config; + + return { + /** + * 设置时间段 + */ + setValue: function (value, index) { + if (index !== 1) { + index = 0; + } + value = SliderTime.timeToNum(value); + var start, end, width, + $wrap = $(options.elem).find('.layui-slider-wrap'), + val = [$wrap.eq(0).data("value"), $wrap.eq(1).data("value")]; + if (value === val[0] || value === val[1]) { + // 设置值等于当前最小或最大值,不移动 + return; + } else if (value > val[1]) { + // 设置值大于当前最大值,移动后面圆点 + val[1] > val[0] ? index = 1 : index = 0; + val[index] = value; + } else if (value < val[0]) { + // 设置值小于当前最小值,移动前面圆点 + val[0] < val[1] ? index = 0 : index = 1; + val[index] = value; + } else { + // 设置值间与中间,则按照index移动 + index === 0 ? val[0] = value : val[1] = value; + } + $wrap.eq(index).data("value", value); + + start = (val[0] - options.min) / (options.max - options.min) * 100; + end = (val[1] - options.min) / (options.max - options.min) * 100; + // 设置圆点位置 + if ("vertical" === options.type) { + $(options.elem).find('.layui-slider-wrap').eq(index).css("bottom", (index === 0 ? start : end) + '%'); + } else { + $(options.elem).find('.layui-slider-wrap').eq(index).css("left", (index === 0 ? start : end) + '%'); + } + // 设置滑动位置 + width = Math.abs(end - start); + if ("vertical" === options.type) { + $(options.elem).find('.layui-slider-bar').css({ + "height": width + '%', + "bottom": (start < end ? start : end) + '%' + }); + } else { + $(options.elem).find('.layui-slider-bar').css({ + "width": width + '%', + "left": (start < end ? start : end) + '%' + }); + } + return; + // return that.slider.setValue(SliderTime.timeToNum(value), index) + } + + /** + * 获取选中的时间段 + */ + , getValue: function () { + var $wrap = $(options.elem).find('.layui-slider-wrap'); + var value = [$wrap.eq(0).data("value"), $wrap.eq(1).data("value")]; + //如果前面的圆点超过了后面的圆点值,则调换顺序 + if (value[0] > value[1]) { + value.reverse(); + } + return [SliderTime.numToTime(value[0]), SliderTime.numToTime(value[1])]; + } + /** + * 选择的时间段是否包含被禁止选择的时间段 + * 包含:返回禁止选择的时间段 + * 不包含:false + */ + , isOccupation: function () { + if (options.disabledValue) { + var $wrap = $(options.elem).find('.layui-slider-wrap'); + var value = [$wrap.eq(0).data("value"), $wrap.eq(1).data("value")]; + //如果前面的圆点超过了后面的圆点值,则调换顺序 + if (value[0] > value[1]) { + value.reverse(); + } + for (var i in options.disabledValue) { + var date = options.disabledValue[i]; + var min = SliderTime.timeToNum(date[0]); + var max = SliderTime.timeToNum(date[1]); + if (value[0] > min && value[0] < max + || value[1] > min && value[1] < max + || value[0] <= min && value[1] >= max) { + return date; + } else { + that.closeTips(options.elem); + } + } + return false; + } else { + return false; + } + } + , config: options + } + }; + + /** + * 滑块渲染 + * @param options + */ + Class.prototype.render = function () { + var that = this + , options = that.config; + // 是否显示时刻,默认是 + options.showstep = options.showstep == false ? false : true; + // 开启滑块的范围拖拽 + options.range = true; + // 最小值,最小为0=0:00 + options.min = options.min == undefined || SliderTime.timeToNum(options.min) < 0 ? 0 : SliderTime.timeToNum(options.min); + // 最大值,最大为1440=24*60=24:00 + options.max = options.max == undefined || SliderTime.timeToNum(options.max); + // options.max = options.max == undefined || SliderTime.timeToNum(options.max) > 1440 ? 1440 : SliderTime.timeToNum(options.max); + //间隔值不能大于 max + options.step = options.step > options.max ? options.max : options.step; + // 初始值 + options.value = typeof (options.value) == 'object' ? options.value : [SliderTime.numToTime(options.min), SliderTime.numToTime(options.min)]; + options.value[0] = SliderTime.timeToNum(options.value[0]); + options.value[1] = SliderTime.timeToNum(options.value[1]); + + // 初始化 + that.slider = slider.render({ + elem: options.elem + , type: options.type + , height: options.height + , theme: options.theme + , range: options.range + , showstep: options.showstep + , min: options.min + , max: options.max + , step: options.step + , value: options.value + , disabled: options.disabled + , setTips: function (value) { + value = SliderTime.numToTime(value); + if (options.setTips) { + return options.setTips(value); + } else { + return value; + } + } + , change: function (value) { + if (options.disabledValue && options.disabledText) { + // 当滑动到禁用时间范围时,自动提醒 + for (var i in options.disabledValue) { + var date = options.disabledValue[i]; + var min = SliderTime.timeToNum(date[0]); + var max = SliderTime.timeToNum(date[1]); + if (value[0] > min && value[0] < max + || value[1] > min && value[1] < max + || value[0] <= min && value[1] >= max) { + var disabledText = options.disabledText.replace('{start}', date[0]).replace('{end}', date[1]) + that.showTips(options, disabledText) + break; + } else { + that.closeTips(options.elem); + } + } + } + if (options.change) { + options.change(value); + } + } + }); + + // 扩展元素 + $(options.elem).find('.layui-slider') + // 开始结尾刻度时间 + .append('
            ' + SliderTime.numToTime(options.min) + '
            ') + .append('
            ' + SliderTime.numToTime(options.max) + '
            ') + // 警告提示Tips + .append('
            '); + if (options.showstep) { + // 开始结尾刻度标识 + $(options.elem).find('.layui-slider') + .append('
            ') + .append('
            '); + } + + + // 定义整点和半点时刻标识 + var $steps = $(options.elem).find('.layui-slider .layui-slider-step'); + for (var i = 0, num = options.min; num <= options.max && i < $steps.length - 1; i++, num += options.step) { + if (that.numToMins(num) == 0 && i > 0) { + $steps.eq(i - 1).addClass('step-hour'); + + } else if (that.numToMins(num) == 30) { + $steps.eq(i - 1).addClass('step-half-hour'); + } + } + + // 初始化选中禁止选择时间范围 + if (options.disabledValue) { + options.disabledValue.forEach(function (date) { + var dateNum = [SliderTime.timeToNum(date[0]), SliderTime.timeToNum(date[1])]; + + var start = (dateNum[0] - options.min) / (options.max - options.min) * 100, + end = (dateNum[1] - options.min) / (options.max - options.min) * 100, + width = end - start; + var selectedSlider = '
            '; + $(options.elem).find('.layui-slider-bar').after(selectedSlider); + }); + } + + // 主题设置 + if (options.theme) { + $(options.elem).find('.step-hour,.step-half-hour,.step-start-wrap,.step-end-wrap,.layui-slider-step').css("background", options.theme); + } + }; + + /** + * 数字转分钟 + */ + Class.prototype.numToMins = function (value) { + var hours = Math.floor(value / 60); + var mins = (value - hours * 60); + return mins; + }; + + /** + * 显示警告信息 + */ + Class.prototype.showTips = function (options, text) { + if ("vertical" === options.type) { + $(options.elem).find('.slider-danger-tips').css({ + "display": "block", + "bottom": $(options.elem).find('.layui-slider-tips')[0].style.bottom + }).text(text); + } else { + $(options.elem).find('.slider-danger-tips').css({ + "display": "block", + "left": $(options.elem).find('.layui-slider-tips')[0].style.left + }).text(text); + } + + }; + + /** + * 关闭警告信息 + */ + Class.prototype.closeTips = function (elem) { + $(elem).find('.slider-danger-tips').css({ + "display": "none" + }).text(''); + }; + + // 加载css + layui.link(layui.cache.base + "sliderTime/sliderTime.css?v="+(new Date).getTime()); + exports(MOD_NAME, SliderTime); +}); \ No newline at end of file diff --git a/public/js/lay-module/sliderVerify/sliderVerify.js b/public/js/lay-module/sliderVerify/sliderVerify.js new file mode 100644 index 0000000..87296aa --- /dev/null +++ b/public/js/lay-module/sliderVerify/sliderVerify.js @@ -0,0 +1 @@ +layui.define(["jquery","layer","form"],function(t){var e=layui.jquery,c=layui.form,s=layui.layer,l=layui.device(),a=(function(){var v=".slider-item{height:38px;line-height:38px;background-color:#d0d0d0;position:relative;border: 1px solid white;}.slider-bg{position:absolute;width:40px;height:100%;z-index:100}.slider-btn{width:40px;height:96%;position:absolute;border:1px solid #ccc;cursor:move;text-align:center;background-color:#fff;user-select:none;color:#666;z-index:120}.slider-btn-success{font-size:26px}.slider-text{position:absolute;text-align:center;width:100%;height:100%;user-select:none;font-size:14px;color:#fff;z-index:120}.slider-error{animation:glow 800ms ease-out infinite alternate;}@keyframes glow{0%{border-color:#e6e6e6}100%{border-color:#ff5722}}",w=document.createElement("style");w.innerHTML=v;w.type="text/css";(e("head link:last")[0]&&e("head link:last").after(w))||e("head").append(w)})(),i={version:"1.7.2"},n=function(v){return v[0]},p=function(){var v=this;return{isOk:function(){return v.isOk.call(v)},reset:function(){return v.reset.call(v)}}},h="sliderVerify",j="slider-btn",k="slider-bg",r="slider-text",b="layui-icon-next",u="layui-icon-ok-circle",g="slider-btn-success",o="layui-bg-green",d="slider-error",q="请拖动滑块验证",f="验证通过",m=function(v){var w=this;w.config=e.extend({},w.config,v);w.render()};m.prototype.config={elem:"",onOk:null,isOk:false,isAutoVerify:true,timer:null,bg:o,text:q};m.prototype.render=function(){var x=this,v=x.config,w=e(v.elem);if(!w[0]){return}if(v.domid){v.domid.remove()}v.domid=x.createIdNum();var y=e(['
            ','
            ','
            '+v.text+"
            ",'
            '].join(""));w.hide().after(y);v.domid=e("#"+v.domid);x.events();if(v.isAutoVerify){c.verify({sliderVerify:function(z,A){if(!z){A.classList.add(d);return v.text}}})}};m.prototype.isMobile=function(){return(l.os=="ios"||l.os=="android"||l.android||l.ios||(l.weixin&&typeof l.weixin===Boolean))};m.prototype.createIdNum=function(){return(h+(+new Date()).toString()+Math.random().toString().substr(2,7))};m.prototype.isOk=function(){return this.config.isOk};m.prototype.error=function(v){return s.msg(v,{icon:5})};m.prototype.distance=function(){var v=this.config.container;return v.box.offsetWidth-v.btn.offsetWidth};m.prototype.reset=function(){this.config.isOk=false;return this.render()};m.prototype.resize=function(x){var w=this,v=w.config.container;var x=x||w.distance();v.btn.style.left=x+"px";v.bg.style.width=x+"px"};m.prototype.cancelTransition=function(){var v=this.config.container;this.config.domid[0].classList.remove(d);v.btn.style.transition="";v.bg.style.transition=""};m.prototype.down=function(z){var y=this,x=y.config,w=x.container,z=z||window.event,A=z.clientX||z.touches[0].clientX;y.cancelTransition();var v=function(B){y.move(A,B)};y.events.move=v;if(y.isMobile()){document.addEventListener("touchmove",y.events.move)}else{document.onmousemove=v}if(navigator.userAgent.indexOf("UCBrowser")>-1){z.preventDefault()}};m.prototype.move=function(D,C){var B=this,z=B.config,v=z.container;var C=C||window.event;var x=C.clientX||C.touches[0].clientX;var A=x-D;if(A>v.distance){A=v.distance}else{if(A<0){A=0}}v.btn.style.left=A+"px";v.bg.style.width=A+"px";if(A==v.distance){v.text.innerHTML=f;var w=window.getComputedStyle?window.getComputedStyle(v.bg,null):v.bg.currentStyle;v.btn.style.border="1px solid "+w.backgroundColor;v.btn.style.color=w.backgroundColor;v.btn.classList.remove(b);v.btn.classList.add(u,g);z.isOk=true;v.box.value=true;if(B.isMobile()){v.btn.removeEventListener("touchstart",B.events.down,false);document.removeEventListener("touchmove",B.events.move,false)}else{v.btn.onmousedown=null;document.onmousemove=null}z.onOk&&typeof z.onOk=="function"&&z.onOk();return}var y=function(E){B.stop(E)};B.events.seup=y;if(B.isMobile()){document.addEventListener("touchend",y)}else{document.onmouseup=y}};m.prototype.stop=function(y){var x=this,w=x.config,v=w.container;if(x.isOk()){return}else{v.btn.style.left=0;v.bg.style.width=0;v.btn.style.transition="left 1s";v.bg.style.transition="width 1s"}document.onmousemove=null;document.onmouseup=null;if(x.isMobile()){document.removeEventListener("touchmove",x.events.move,false);document.removeEventListener("touchend",x.events.seup,false)}};m.prototype.events=function(){var A=this,z=A.config;if(!z.domid){return A.error("创建滑块验证失败")}var y=z.domid.find("."+j),x=z.domid.find("."+k),B=z.domid.find("."+r),w={box:n(z.domid),btn:n(y),bg:n(x),text:n(B)};z.container=w;w.distance=A.distance();var C=function(D){A.down(D)};A.events.down=C;if(A.isMobile()){w.btn.addEventListener("touchstart",A.events.down)}else{w.btn.onmousedown=C}var v=e(window);v.on("resize",z.domid,function(){if(A.config.isOk){A.resize()}else{clearTimeout(z.timer);z.timer=setTimeout(function(){A.render()},400)}})};i.render=function(v){var w=new m(v);return p.call(w)};t(h,i)}); \ No newline at end of file diff --git a/public/js/lay-module/step-lay/step.css b/public/js/lay-module/step-lay/step.css new file mode 100644 index 0000000..cc5e2ce --- /dev/null +++ b/public/js/lay-module/step-lay/step.css @@ -0,0 +1,79 @@ +.lay-step { + font-size: 0; + width: 400px; + margin: 0 auto; + max-width: 100%; + padding-left: 200px; +} + +.step-item { + display: inline-block; + line-height: 26px; + position: relative; + font-size: 14px; +} + +.step-item-tail { + width: 100%; + padding: 0 10px; + position: absolute; + left: 0; + top: 13px; +} + +.step-item-tail i { + display: inline-block; + width: 100%; + height: 1px; + vertical-align: top; + background: #c2c2c2; + position: relative; +} + +.step-item-tail .step-item-tail-done { + background: #009688; +} + +.step-item-head { + position: relative; + display: inline-block; + height: 26px; + width: 26px; + text-align: center; + vertical-align: top; + color: #009688; + border: 1px solid #009688; + border-radius: 50%; + background: #ffffff; +} + +.step-item-head.step-item-head-active { + background: #009688; + color: #ffffff; +} + +.step-item-main { + display: block; + position: relative; + margin-left: -50%; + margin-right: 50%; + padding-left: 26px; + text-align: center; +} + +.step-item-main-title { + font-weight: bolder; + color: #555555; +} + +.step-item-main-desc { + color: #aaaaaa; +} + +.lay-step + [carousel-item]:before { + display: none; +} + +.lay-step + [carousel-item] > * { + background-color: transparent; +} \ No newline at end of file diff --git a/public/js/lay-module/step-lay/step.js b/public/js/lay-module/step-lay/step.js new file mode 100644 index 0000000..69a96a0 --- /dev/null +++ b/public/js/lay-module/step-lay/step.js @@ -0,0 +1,101 @@ +layui.define(['layer', 'carousel'], function (exports) { + var $ = layui.jquery; + var layer = layui.layer; + var carousel = layui.carousel; + + // 添加步骤条dom节点 + var renderDom = function (elem, stepItems, postion) { + var stepDiv = '
            '; + for (var i = 0; i < stepItems.length; i++) { + stepDiv += '
            '; + // 线 + if (i < (stepItems.length - 1)) { + if (i < postion) { + stepDiv += '
            '; + } else { + stepDiv += '
            '; + } + } + + // 数字 + var number = stepItems[i].number; + if (!number) { + number = i + 1; + } + if (i == postion) { + stepDiv += '
            ' + number + '
            '; + } else if (i < postion) { + stepDiv += '
            '; + } else { + stepDiv += '
            ' + number + '
            '; + } + + // 标题和描述 + var title = stepItems[i].title; + var desc = stepItems[i].desc; + if (title || desc) { + stepDiv += '
            '; + if (title) { + stepDiv += '
            ' + title + '
            '; + } + if (desc) { + stepDiv += '
            ' + desc + '
            '; + } + stepDiv += '
            '; + } + stepDiv += '
            '; + } + stepDiv += '
            '; + + $(elem).prepend(stepDiv); + + // 计算每一个条目的宽度 + var bfb = 100 / stepItems.length; + $('.step-item').css('width', bfb + '%'); + }; + + var step = { + // 渲染步骤条 + render: function (param) { + param.indicator = 'none'; // 不显示指示器 + param.arrow = 'always'; // 始终显示箭头 + param.autoplay = false; // 关闭自动播放 + if (!param.stepWidth) { + param.stepWidth = '400px'; + } + + // 渲染轮播图 + carousel.render(param); + + // 渲染步骤条 + var stepItems = param.stepItems; + renderDom(param.elem, stepItems, 0); + $('.lay-step').css('width', param.stepWidth); + + //监听轮播切换事件 + carousel.on('change(' + param.filter + ')', function (obj) { + $(param.elem).find('.lay-step').remove(); + renderDom(param.elem, stepItems, obj.index); + $('.lay-step').css('width', param.stepWidth); + }); + + // 隐藏左右箭头按钮 + $(param.elem).find('.layui-carousel-arrow').css('display', 'none'); + + // 去掉轮播图的背景颜色 + $(param.elem).css('background-color', 'transparent'); + }, + // 下一步 + next: function (elem) { + $(elem).find('.layui-carousel-arrow[lay-type=add]').trigger('click'); + }, + // 上一步 + pre: function (elem) { + $(elem).find('.layui-carousel-arrow[lay-type=sub]').trigger('click'); + } + }; + + layui.link(layui.cache.base + "step-lay/step.css?v="+(new Date).getTime()); + + exports('step', step); +}); diff --git a/public/js/lay-module/tableMerge/tableMerge.js b/public/js/lay-module/tableMerge/tableMerge.js new file mode 100644 index 0000000..4d26b3c --- /dev/null +++ b/public/js/lay-module/tableMerge/tableMerge.js @@ -0,0 +1,75 @@ +/** + * Created by YujieYang. + * @name: 子表格扩展 + * @author: 杨玉杰 + * @version 1.0 + */ +layui.define(['table'], function (exports) { + + var $ = layui.jquery; + + // 封装方法 + var mod = { + /** + * 渲染入口 + * @param myTable + */ + render: function (myTable) { + var tableBox = $(myTable.elem).next().children('.layui-table-box'), + $main = $(tableBox.children('.layui-table-body').children('table').children('tbody').children('tr').toArray().reverse()), + $fixLeft = $(tableBox.children('.layui-table-fixed-l').children('.layui-table-body').children('table').children('tbody').children('tr').toArray().reverse()), + $fixRight = $(tableBox.children('.layui-table-fixed-r').children('.layui-table-body').children('table').children('tbody').children('tr').toArray().reverse()), + cols = myTable.cols[0], mergeRecord = {}; + + for (let i = 0; i < cols.length; i++) { + var item3 = cols[i], field=item3.field; + if (item3.merge) { + var mergeField = [field]; + if (item3.merge !== true) { + if (typeof item3.merge == 'string') { + mergeField = [item3.merge] + } else { + mergeField = item3.merge + } + } + mergeRecord[i] = {mergeField: mergeField, rowspan:1} + } + } + + $main.each(function (i) { + + for (var item in mergeRecord) { + if (i==$main.length-1 || isMaster(i, item)) { + $(this).children('[data-key$="-'+item+'"]').attr('rowspan', mergeRecord[item].rowspan).css('position','static'); + $fixLeft.eq(i).children('[data-key$="-'+item+'"]').attr('rowspan', mergeRecord[item].rowspan).css('position','static'); + $fixRight.eq(i).children('[data-key$="-'+item+'"]').attr('rowspan', mergeRecord[item].rowspan).css('position','static'); + mergeRecord[item].rowspan = 1; + } else { + $(this).children('[data-key$="-'+item+'"]').remove(); + $fixLeft.eq(i).children('[data-key$="-'+item+'"]').remove(); + $fixRight.eq(i).children('[data-key$="-'+item+'"]').remove(); + mergeRecord[item].rowspan +=1; + } + } + }) + + function isMaster (index, item) { + var mergeField = mergeRecord[item].mergeField; + var dataLength = layui.table.cache[myTable.id].length; + for (var i=0; i= 1){ + return false; + } + + var t = elem.offset().top + elem.outerHeight()+"px"; + var l = elem.offset().left +"px"; + var tableName = "tableSelect_table_" + new Date().getTime(); + var tableBox = '
            '; + tableBox += '
            '; + tableBox += '
            '; + tableBox += ''; + tableBox += '
            '; + tableBox += ''; + tableBox += '
            '; + tableBox += '
            '; + tableBox += '
            '; + tableBox = $(tableBox); + $('body').append(tableBox); + + //数据缓存 + var checkedData = []; + + //渲染TABLE + opt.table.elem = "#"+tableName; + opt.table.id = tableName; + opt.table.done = function(res, curr, count){ + defaultChecked(res, curr, count); + setChecked(res, curr, count); + tableDone(res, curr, count); + }; + var tableSelect_table = table.render(opt.table); + + //分页选中保存数组 + table.on('radio('+tableName+')', function(obj){ + if(opt.checkedKey){ + checkedData = table.checkStatus(tableName).data + } + updataButton(table.checkStatus(tableName).data.length) + }) + table.on('checkbox('+tableName+')', function(obj){ + if(opt.checkedKey){ + if(obj.checked){ + for (var i=0;i $(window).height(); + var overWidth = (elem.offset().left + tableBox.outerWidth()) > $(window).width(); + overHeight && tableBox.css({'top':'auto','bottom':'0px'}); + overWidth && tableBox.css({'left':'auto','right':'5px'}) + + //关键词搜索 + form.on('submit(tableSelect_btn_search)', function(data){ + tableSelect_table.reload({ + where: data.field, + page: { + curr: 1 + } + }); + return false; + }); + + //双击行选中 + table.on('rowDouble('+tableName+')', function(obj){ + var checkStatus = {data:[obj.data]}; + selectDone(checkStatus); + }) + + //按钮选中 + tableBox.find('.tableSelect_btn_select').on('click', function() { + var checkStatus = table.checkStatus(tableName); + if(checkedData.length > 1){ + checkStatus.data = checkedData; + } + selectDone(checkStatus); + }) + + //写值回调和关闭 + function selectDone (checkStatus){ + if(opt.checkedKey){ + var selected = []; + for(var i=0;i' + ,'
              ' + ,'{{# if(d.data){ }}' + ,'{{# d.data.forEach(function(item){ }}' + ,'{{# var selectedClass = d.callbackFn(item) }}' + ,'
            • ' + ,'
              {{ item.value }}
              ' + ,'
            • ' + ,'{{# }); }}' + ,'{{# } else { }}' + ,'
            • 无数据
            • ' + ,'{{# } }}' + ,'
            ' + , '
            ' + ].join('') + ,selectMoreTpl = [ //多选下拉框模板 + '
            ' + ,'
            ' + ,'
              ' + ,'{{# if(d.data){ }}' + ,'{{# d.data.forEach(function(item){ }}' + ,'{{# var selectedClass = d.callbackFn(item) }}' + ,'
            • ' + ,'
              {{ item.value }}
              ' + ,'
            • ' + ,'{{# }); }}' + ,'{{# } else { }}' + ,'
            • 无数据
            • ' + ,'{{# } }}' + ,'
            ' + ,'
            ' + ,'
            ' + ,'
            ' + ,'' + ,'
            ' + ,'
            ' + ,'' + ,'
            ' + ,'
            ' + ,'
            ' + ].join(''); + //组件用到的css样式 + var thisCss = []; + thisCss.push('.layui-tableEdit-div{position:absolute;background-color:#fff;font-size:14px;border:1px solid #d2d2d2;z-index:19910908445;max-height: 252px;}'); + thisCss.push('.layui-tableEdit-tpl{max-height:216px;overflow-y:auto;}'); + thisCss.push('.layui-tableEdit-div li{line-height:36px;padding-left:5px;}'); + thisCss.push('.layui-tableEdit-div li:hover{background-color:#f2f2f2;}'); + thisCss.push('.layui-tableEdit-selected{background-color:#5FB878;}'); + thisCss.push('.layui-tableEdit-checked i{background-color:#60b979!important;}'); + thisCss.push('.layui-tableEdit-ul div{padding-left:0px!important;}'); + thisCss.push('.layui-tableEdit-input{text-align:center;position:absolute;left:0;bottom:0;width:100%;height:38px;z-index: 19910908;}'); + thisCss.push('.layui-tableEdit-add{position: absolute;right: 3px;top: 21px;margin-top: -15px;z-index: 199109084;}') + thisCss.push('.layui-tableEdit-sub{position: absolute;left: 3px;top: 21px;margin-top: -15px;z-index: 199109084;}') + var thisStyle = document.createElement('style'); + thisStyle.innerHTML = thisCss.join('\n'),document.getElementsByTagName('head')[0].appendChild(thisStyle); + + var configs = { + callbacks:{} + , + verify: { + required: [ + /[\S]+/ + ,'必填项不能为空' + ] + ,phone: [ + /^1[34578]\d{9}$/ + ,'请输入正确的手机号' + ] + ,email: [ + /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/ + ,'邮箱格式不正确' + ] + ,url: [ + /(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/ + ,'链接格式不正确' + ] + ,number:[ + /(^[-+]?\d+$)|(^[-+]?\d+\.\d+$)/ + ,'只能填写数字' + ] + ,date: [ + /^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/ + ,'日期格式不正确' + ] + ,identity: [ + /(^\d{15}$)|(^\d{17}(x|X|\d)$)/ + ,'请输入正确的身份证号' + ] + } + }; + + var Class = function () { //单列模式 也就是只能new一个对象。 + var instance; + Class = function Class() { + return instance; + }; + Class.prototype = this; //保留原型属性 + instance = new Class(); + instance.constructor = Class; //重置构造函数指针 + return instance + }; //构造器 + var singleInstance = new Class(); + var inFunc = function () {singleInstance.leaveStat = false;},outFunc = function () {singleInstance.leaveStat = true;}; + document.onclick = function () {if(singleInstance.leaveStat)singleInstance.deleteAll();}; + + //日期选择框 + Class.prototype.date = function(options){ + var othis = this; + othis.callback = options.callback,othis.element = options.element,othis.dateType = options.dateType; + othis.dateType = othis.isEmpty(othis.dateType) ? "datetime":othis.dateType; + var that = options.element; + if ($(that).find('input').length>0)return; + othis.deleteAll(),othis.leaveStat = false; + var input = $(''); + (39 - that.offsetHeight > 3) && input.css('height','30px'); + (that.offsetHeight - 39 > 3) && input.css('height','50px'); + $(that).append(input),input.focus(); + //日期时间选择器 (show: true 表示直接显示) + laydate.render({elem: input[0],type: othis.dateType,show: true,done:function (value, date) { + othis.deleteAll(); + if(othis.callback)othis.callback.call(that,value); + }}); + $('div.layui-laydate').hover(inFunc,outFunc),$(that).hover(inFunc,outFunc); + _layui.stope(); + }; + + //输入框 + Class.prototype.input = function(options){ + var othis = this; + othis.callback = options.callback,othis.element = options.element; + othis.oldValue = options.oldValue; + othis.oldValue = othis.oldValue ? othis.oldValue : ''; + var that = options.element; + if ($(that).find('input').length>0)return; + othis.deleteAll(),othis.leaveStat = false; + var input = $(''); + (39 - that.offsetHeight > 3) && input.css('height','30px'); + (that.offsetHeight - 39 > 3) && input.css('height','50px'); + input.val(othis.oldValue); + $(that).append(input),input.focus(); + input.click(function (e) { + _layui.stope(e); + }); + input.bind('change', function(e){othis.callback.call(othis.element,this.value)}); + $(that).hover(inFunc,outFunc); + _layui.stope(); + }; + + //带加号和减号的输入框(只支持输入数字) + Class.prototype.signedInput = function(options){ + var othis = this; + othis.callback = options.callback,othis.element = options.element; + othis.oldValue = options.oldValue; + othis.oldValue = othis.oldValue ? othis.oldValue : ''; + var that = options.element; + if ($(that).find('input').length>0)return; + othis.deleteAll(),othis.leaveStat = false; + var thisWidth = that.offsetWidth-49; + var input = $('');// + var leftBtn = $(''); + var rightBtn = $(''); + if(39 - that.offsetHeight > 3){ + input.css('height','30px');leftBtn.css('top','16px');rightBtn.css('top','16px'); + } + if(that.offsetHeight - 39 > 3){ + input.css('height','50px');leftBtn.css('top','25px');rightBtn.css('top','25px'); + } + input.val(othis.oldValue); + $(that).append(leftBtn);leftBtn.find('i').html(''); + $(that).append(input),input.focus();$(that).append(rightBtn);rightBtn.find('i').html(''); + input.click(function (e) { + _layui.stope(e); + }); + input.bind('change', function(e){othis.callback.call(othis.element,this.value)}); + $(that).hover(inFunc,outFunc); + _layui.stope(); + $(that).find('button.layui-tableEdit-sub,button.layui-tableEdit-add').bind('click',function () { + var input = $(that).find('input.layui-tableEdit-input'); + var val = input.val(); + if(!val || val.length<=0)val=0; + val = parseInt(val); + if($(this).hasClass('layui-tableEdit-add')){ + ++val;input.val(val); + othis.callback.call(othis.element,val) + }else{ + --val;input.val(val); + othis.callback.call(othis.element,val) + } + }) + }; + + //判断是否为空函数 + Class.prototype.isEmpty = function(dataStr){return typeof dataStr === 'undefined' || dataStr === null || dataStr.length <= 0;}; + + //生成下拉框函数入口 + Class.prototype.register = function(options){ + var othis = this; + othis.enabled = options.enabled,othis.callback = options.callback; + othis.data = options.data,othis.element = options.element; + othis.selectedData = options.selectedData; + var that = othis.element; + if($(that).find('input.layui-tableEdit-input')[0]) return; + othis.deleteAll(),othis.leaveStat = false; + var input = $('') + ,tableEdit = $('
            ') + ,tableBody = $(that).parents('div.layui-table-body') + ,tablePage = $(that).parents('div.layui-table-box').eq(0).next(); + (39 - that.offsetHeight > 3) && input.css('height','30px'); + (that.offsetHeight - 39 > 3) && input.css('height','50px'); + tableEdit.append(input),$(that).append(tableEdit),input.focus(); + var thisY = input[0].getBoundingClientRect().top //输入框y坐标 + ,thisHeight = ((39 - that.offsetHeight > 3) ? 30 : input[0].offsetHeight) //输入框高度 + ,thisHeight = ((that.offsetHeight - 39 > 3) ? 50 :thisHeight) + ,thisWidth = input[0].offsetWidth //输入框宽度 + ,elemY = that.getBoundingClientRect().top //输入框y坐标 + ,tableBodyY = tableBody[0].getBoundingClientRect().top + ,pageY = tablePage[0].getBoundingClientRect().top + ,tableBodyHeight = tableBody.height() //表格高度 + ,isType = thisY-tableBodyY > 0.8*tableBodyHeight + ,type = isType ? 'top: auto;bottom: '+(thisHeight+2)+'px;' : 'bottom: auto;top: '+(thisHeight+2)+'px;'; + if(elemY pageY) && !isType && (tableBody[0].scrollTop = that.offsetTop);//调整滚动条位置 + othis.events(); + }; + + //删除所有下拉框和时间选择框 + Class.prototype.deleteAll = function(){ + $('div.layui-tableEdit-div,div.layui-tableEdit,div.layui-laydate,input.layui-tableEdit-input,button.layui-tableEdit-sub,button.layui-tableEdit-add').remove(); + delete this.leaveStat;//清除(离开状态属性) + }; + + //注册事件 + Class.prototype.events = function(){ + var othis = this; + var searchFunc = function(val){ //关键字搜索 + $('div.layui-tableEdit-div li').each(function () { + othis.isEmpty(val) || $(this).data('value').indexOf(val) > -1 ? $(this).show() : $(this).hide(); + }); + },liClickFunc = function(){ //给li元素注册点击事件 + var liArr = $('div.layui-tableEdit-div li'); + liArr.unbind('click'),liArr.bind('click',function (e) { + _layui.stope(e); + if(othis.enabled){//多选 + $(this).hasClass("layui-tableEdit-checked") ? ($(this).removeClass("layui-tableEdit-checked"), + $(this).removeClass("layui-tableEdit-selected")) + : $(this).addClass("layui-tableEdit-checked") + }else {//单选 + othis.deleteAll(); + if(othis.callback)othis.callback.call(othis.element,{name:$(this).data("name"),value:$(this).data("value")}); + } + }); + },btnClickFunc = function (){ //给button按钮注册点击事件 + $("div.layui-tableEdit-div button").bind('click',function () { + var eventType = $(this).attr("event-type"), btn = this,dataList = new Array(); + if(eventType === 'close') singleInstance.deleteAll(); //“关闭”按钮 + if(eventType === 'confirm') { //“确定”按钮 + $('div.layui-tableEdit-div li').each(function (e) { + if(!$(this).hasClass("layui-tableEdit-checked"))return; + dataList.push({name:$(this).data("name"),value:$(this).data("value")}); + }); + othis.deleteAll(); + if(othis.callback)othis.callback.call(othis.element,dataList); + } + }); + }; + //事件注册 + $(othis.element).find('input.layui-tableEdit-input').bind('input propertychange', function(){searchFunc(this.value)}); + othis.enabled ? (liClickFunc(),btnClickFunc()) : liClickFunc(); + $(othis.element).hover(inFunc,outFunc); + }; + + var AopEvent = function(cols){this.config = {colsConfig:{}};this.parseCols(cols)};//aop构造器 + /** + * 解析出tableEdit组件所需要的配置信息 + * @param cols + */ + AopEvent.prototype.parseCols = function(cols){ + var that = this; + cols.forEach(function (ite) { + ite.forEach(function (item) { + if(!item.config)return; + that.config.colsConfig[item.field] = item.config; + }); + }); + }; + /** + * aop代理event + * @param event 事件名称 + * @param callback 回调函数 + */ + AopEvent.prototype.on = function(event,callback){ + var othis = this;othis.config.event = event,othis.config.callback = callback; + table.on(othis.config.event,function (obj) { + var zthis = this,field = $(zthis).data('field'),config = othis.config.colsConfig[field]; + if(!config){ + othis.config.callback.call(zthis,obj);return; + } + obj.field = field; + var callbackFn = function (res) { + if(config.verify && !othis.verify(res,config.verify,this))return; //验证为空 + obj.value = Array.isArray(res) ? (res.length>0 ? res : [{name:'',value:''}]) : res; + othis.config.callback.call(zthis,obj); + if(!singleInstance.isEmpty(config.cascadeSelectField)){ + var csElement = $(this.parentNode).find("td[data-field='"+config.cascadeSelectField+"']"); + $(csElement).attr("cascadeSelect-data",JSON.stringify({data:res,field:field})); + } + }; + var csd = $(this).attr("cascadeSelect-data");//联动数据 + if(singleInstance.isEmpty(csd)){ //非联动事件 + if(config.type === 'select'){ + singleInstance.register({data:config.data,element:zthis,enabled:config.enabled,selectedData:obj.data[field],callback:callbackFn}); + }else if(config.type === 'date'){ + singleInstance.date({dateType:config.dateType,element:zthis,callback:callbackFn}); + }else if(config.type === 'input'){ + singleInstance.input({element:zthis,oldValue:obj.data[field],callback:callbackFn}); + }else if(config.type === 'signedInput'){ + singleInstance.signedInput({element:zthis,oldValue:obj.data[field],callback:callbackFn}); + }else othis.config.callback.call(zthis,obj); + } else {//联动事件 + if(config.type === 'date') return; + //获取当前单元格的table表格的lay-filter属性值 + var filter = $(zthis).parents('div.layui-table-view').eq(0).prev().attr('lay-filter') + ,rs = active.callbackFn.call(zthis,'clickBefore('+filter+')',JSON.parse(csd)); + //异步操作,由使用者调用。 + //判断条件为rs为空时。 + if(singleInstance.isEmpty(rs)){ + active.on("async("+filter+")",function (result) { + singleInstance.register({data:result.data,element:zthis,enabled:result.enabled,selectedData:obj.data[field],callback:callbackFn}); + }) + }else { + singleInstance.register({data:rs.data,element:zthis,enabled:rs.enabled,selectedData:obj.data[field],callback:callbackFn}); + } + } + + }); + }; + + /** + * 验证数据是否符合要求 + * @param data 被验证数据 + * @param verify 正则参数 + * @param td 当前单元格 + * @returns {boolean} true验证通过 false验证未通过 + */ + AopEvent.prototype.verify = function (data,verify,td) { + var verifyObj = configs.verify[verify.type]; + var verifyMsg = verify.msg; + verifyMsg = verifyMsg ? verifyMsg : (verifyObj ? verifyObj[1] : '必填项不能为空'); + if(singleInstance.isEmpty(data)){ + layer.tips(verifyMsg, td,{tipsMore:true}); + return false; + } + if(Array.isArray(data) && data.length <= 0){ + layer.tips(verifyMsg, td,{tipsMore:true}); + return false; + } + if((typeof data === 'object' && singleInstance.isEmpty(data.name)) + || data.name === 'undefined'){ + layer.tips(verifyMsg, td,{tipsMore:true}); + return false; + } + if(!verifyObj && !verify.regx){ + return true; + } + if(verify.regx){ //自定义正则判断 + if(typeof verify.regx === "function"){//为函数时 + if(verify.regx(data))return true; + layer.tips(verifyMsg, td,{tipsMore:true}); + return false; + } + if(typeof verify.regx === "string"){ //为字符串正则时 + var regx = new RegExp(verify.regx); + if(regx.test(data))return true; + layer.tips(verifyMsg, td,{tipsMore:true}); + return false; + } + if(verify.regx.test(data))return true; //为正则时 + layer.tips(verifyMsg, td,{tipsMore:true}); + return false; + } + if(!verifyObj[0].test(data)){ + layer.tips(verifyMsg, td,{tipsMore:true}); + return false; + } + return true; + }; + + /** + * 提交数据的验证 + * @param options {elem:'#test',data:[],verifyKey:'id'} + * elem: 表格id,带井号 data: 验证数据,数组类型。 + * verifyKey: data中的元素的唯一值字段,且必须在表格中有此字段的单元格。 + * @returns {*} + */ + AopEvent.prototype.submitValidate = function (options) { + var that = this,failedTds = []; + if(!options || singleInstance.isEmpty(options.verifyKey) + || singleInstance.isEmpty(options.data) + || singleInstance.isEmpty(options.elem))return failedTds; + var body = $(options.elem).next().find('div.layui-table-box div.layui-table-body tr'); + options.data.forEach(function (item) { + for(var field in item){ + var config = that.config.colsConfig[field]; + if(!config || !config.verify)continue; + var verify = config.verify; + var tds = body.find('td[data-field="'+options.verifyKey+'"]'); + var thisTd; + tds.each(function () { + var text = $(this).find('div.layui-table-cell').text(); + if(text+'' === item[options.verifyKey]+''){ + thisTd = $(this); + } + }); + if(!thisTd)continue; + var td = thisTd.parent().children('td[data-field="'+field+'"]'); + if(!that.verify(item[field],verify,td))failedTds.push(td[0]); + } + }); + return failedTds; + }; + + var active = { + aopObj:function(cols){return new AopEvent(cols);}, + on:function (event,callback) { + var filter = event.match(/\((.*)\)$/),eventName = (filter ? (event.replace(filter[0],'')+'_'+ filter[1]) : event); + configs.callbacks[moduleName+'_'+eventName]=callback; + }, + callbackFn:function (event,params) { + var filter = event.match(/\((.*)\)$/),eventName = (filter ? (event.replace(filter[0],'')+'_'+ filter[1]) : event); + var key = moduleName+'_'+eventName,func = configs.callbacks[key]; + if(!func) return; + return func.call(this,params); + } + }; + + active.on("tableEdit(getEntity)",function () { + return singleInstance; + }); + + exports(moduleName, active); +}); \ No newline at end of file diff --git a/public/js/lay-module/tableTree/tableTree.js b/public/js/lay-module/tableTree/tableTree.js new file mode 100644 index 0000000..2fb5e66 --- /dev/null +++ b/public/js/lay-module/tableTree/tableTree.js @@ -0,0 +1,1255 @@ +layui.define(["laydate","laytpl","table","layer","tableEdit","form"],function(exports) { + "use strict"; + var moduleName = 'tableTree' + ,_layui = layui + ,laytpl = _layui.laytpl + ,$ = _layui.$ + ,table = _layui.table + ,layer = _layui.layer + ,tableEdit = _layui.tableEdit + ,editEntity = tableEdit.callbackFn('tableEdit(getEntity)') + ,form = _layui.form; + //组件用到的css样式 + var thisCss = []; + thisCss.push('.layui-tableEdit-checked i{background-color:#60b979!important;}'); + thisCss.push('.layui-tableEdit-edit{position:absolute;right:2px;font-size:18px;bottom:8px;z-index:199109084;}'); + thisCss.push('.layui-tableEdit-edge{position:absolute;left:8px;bottom:0;font-size:15x;z-index:19910908445;}'); + thisCss.push('.layui-tableEdit-tab{position:absolute;left:25px;bottom:0;font-size:15px;z-index:19910908445;}'); + thisCss.push('.layui-tableEdit-span {position:absolute;left:25px;bottom:0;}'); + var thisStyle = document.createElement('style'); + thisStyle.innerHTML = thisCss.join('\n'),document.getElementsByTagName('head')[0].appendChild(thisStyle); + + var configs = { + callbacks:{} + ,tableTreeCache:{} + ,isEmpty:function(data){ + return typeof data === 'undefined' || data === null || data.length <= 0; + } + ,parseConfig:function (cols,field) { + var type,data,enabled,dateType,csField; + cols.forEach(function (ite) { + ite.forEach(function (item) { + if(field !== item.field || !item.config)return; + var config = item.config; + type = config.type; + data = config.data; + enabled = config.enabled; + dateType = config.dateType; + csField = config.cascadeSelectField; + }); + }); + return {type:type,data:data,enabled:enabled,dateType:dateType,cascadeSelectField:csField}; + } + }; + + + var TableTree = function () {this.config = {}}; + TableTree.prototype = { + /** + * 表格树渲染 + * @param options 表格参数 + * @param isFirstRender 是否首次渲染 + */ + render:function (options,isFirstRender) { + var that = this; + if(isFirstRender){ + that._done = options.done; + $.extend(that.config,options); + var treeConfig = that.config['treeConfig']; + var done = function(result){ + if(!result.data) result.data = table.cache[that.config.id] = []; + configs.tableTreeCache[$(this.elem).attr('id')] = result.data; + var params = {field:treeConfig.showField,element:this.elem}; + that.initTree(params); + if(that._done){ + that._done.call(this,result); + } + }; + that.config.done = done; + }else { + that.config = options; + } + that.tableEntity = table.render(that.config); + } + /** + * 初始化表格树及为表格树注册相关事件 + * @param options + */ + ,initTree:function (options) { + var data = configs.tableTreeCache[$(options.element).attr('id')]; + this.doInitTree(data,options.field,null,0); + this.closeAllTreeNodes(); + this.parseColsConfig(); + //先注销事件 + this.off(); + //注册事件 + this.events(); + } + /** + * 初始化表格树 + * @param arr 表格数据data + * @param field 树状单元格字段 + * @param tr 初始化当前tr元素 + * @param lvl 当前行等级 + */ + ,doInitTree:function(arr,field,tr,lvl){ + var that = this; + var tableBody = $(that.config.elem).next().find('div.layui-table-body'); + _layui.each(arr,function (key,item) { + if(lvl === 0){ + tr = tableBody.find('tr[data-index="'+key+'"]'); + $(tr).attr('tree-id',item[that.config.treeConfig.treeid]); + var td = $(tr).find('td[data-field="'+field+'"]'); + var _div = td.find('div.layui-table-cell'); + var span = _div.find('span.layui-tableEdit-span'); + var text = item[that.config.treeConfig.showField];text = configs.isEmpty(text) ? "" : text; + !span[0] && (_div.text(''),_div.prepend('
            '+text+'
            ')); + if(!td.find('i.layui-tableEdit-edge')[0])that.addIcon(tr,td,lvl); + }else { + that.addTreeRow(tr,item); + } + if(item.treeList){ + var nextTr = lvl === 0 ? tr : $(tr).next(); + that.doInitTree(item.treeList,field,nextTr,lvl+1) + } + }); + }, + parseColsConfig:function () { + var that = this;that.config.colsConfig = {}; + that.config.cols.forEach(function (ite) { + ite.forEach(function (item) { + if(!item.config)return; + that.config.colsConfig[item.field] = item.config; + }); + }); + } + /** + *给当前tr新增子节点 + * @param tr 当前节点 + * @param data 子节点数据 + */ + ,addTreeRow:function (tr,data) { + var that = this,lvl = tr.data('lvl'); + lvl = configs.isEmpty(lvl) ? 0 : parseInt(lvl); + var treeid = that.config.treeConfig.treeid; + if(configs.isEmpty(data[treeid])){ + data[treeid] = Number(Math.random().toString().substr(3, 3) + Date.now()).toString(36); + } + var newTr = $(''); + tr.after(newTr); + that.addElementToTr(data,tr,newTr,lvl+1); + } + /** + * 根据当前treeid获取相关数据 + * @param arr 表格树数据 + * @param uniqueTreeid 当前treeid值 + * @returns {*} + */ + ,getDataByTreeId:function (arr,uniqueTreeid) { + var treeid = this.config.treeConfig.treeid; + for(var i=0;i0) { + var data = this.getDataByTreeId(item.treeList, uniqueTreeid); + if(data) return data; + } + } + } + } + /** + * 给当前行添加图标按钮 + * @param tr 当前tr + * @param td 树状单元格 + * @param lvl 当前tr等级 + */ + ,addIcon:function(tr,td,lvl) { + var iconClass = this.config.treeConfig.iconClass; + iconClass = configs.isEmpty(iconClass) ? 'layui-icon-triangle-r' : iconClass; + var that = this + ,icon = $('') + ,div = td.find('div.layui-tableTree-div') + ,_div = $('
            ') + ,span = div.children('span.layui-tableEdit-span'); + if(lvl>0){ + var spanLeft = lvl*21 + 25,iconLeft = lvl*21+5; + span.css('left',spanLeft+'px'); + icon.css('left',iconLeft+'px'); + } + div.append(icon),$(td).append(_div); + var add = $('');_div.append(add); + var update = $('');_div.append(update) + var remove = $('');_div.append(remove); + var sort = $('');_div.append(sort); + _div.hide();sort.css('transform','rotate(90deg)'); + var heightStyle = that.config.size; + heightStyle = heightStyle ? heightStyle : ''; + if(heightStyle === 'sm'){ //兼容table的size类型 + span.css('bottom','0px'); + icon.css('bottom','0px'); + update.css('bottom','4px'); + add.css('bottom','4px'); + remove.css('bottom','4px'); + sort.css('bottom','4px'); + } + if(heightStyle === 'lg'){ + span.css('bottom','-1px'); + icon.css('bottom','-1px'); + update.css('bottom','15px'); + add.css('bottom','15px'); + remove.css('bottom','15px'); + sort.css('bottom','15px'); + } + sort.css('right','3px'),remove.css('right','24px'),update.css('right','48px'),add.css('right','72px'); + } + /** + * 事件注册 + */ + ,events:function () { + var that = this,filter = $(that.config.elem).attr('lay-filter') + ,tableBody = $(this.config.elem).next().find('div.layui-table-body') + ,tableTreeid = $(that.config.elem).attr('id') + ,iconClass = this.config.treeConfig.iconClass + ,showField = that.config.treeConfig.showField; + + //编辑图标事件注册 + tableBody.on('click','i.layui-tableEdit-edit',function (e) { + //_layui.stope(e); + var isAdd = $(this).hasClass('layui-icon-add-1') + ,isUpdate = $(this).hasClass('layui-icon-edit') + ,treeData = configs.tableTreeCache[tableTreeid] + ,isDelete = $(this).hasClass('layui-icon-delete') + ,isSort = $(this).hasClass('layui-icon-return'); + var tr = $(this).parents('tr'); + var td = $(this).parents('td'),field = td.data('field'); + var treeid = tr.attr('tree-id'); + var data = that.getDataByTreeId(treeData,treeid); + var lvl = tr.data('lvl'); + lvl = lvl ? lvl : 0; + if(isAdd){//新增 + //回调tool(lay-filter)对应的方法 异步/同步获取数据后回调add方法进行新增行 + var thisObj = {value:null,data:data,field:field,add:function (newTree) { + !newTree && (newTree = []); + newTree = that.removeRepeatData(newTree);//treeid去重 + if(newTree.length <=0){ + var thisData = {}; + that.config.cols.forEach(function (item1) { + item1.forEach(function (item) { + if(item.field){ + if(!(item.field in data)){ + thisData[item.field] = null; + } + } + }); + }); + newTree.push(thisData); + } + that.asyncAddTree(newTree,data,tr,lvl); + var treeList = data.treeList; + if(!treeList) treeList = data.treeList = []; + var count= 0; + while (count确定删除吗?
            ', function(index){ + var thisObj = {value:null,data:data,field:field,event:'del',del:function () { + var lvl = tr.data('lvl');lvl = lvl ? lvl : 0; + //删除页面隐藏的树 + that.removeChildren(tr,lvl); + //清楚缓存中的数据 + that.clearCacheData(data); + tr.remove(); + }}; + active.callbackFn.call(td[0],'tool('+filter+')',thisObj); + layer.close(index); + }); + } + + //排序 => 根据tree-id进行排序 + if(isSort){ + var icon = $(this); + var isAsc = icon.hasClass('asc'); + isAsc ? (icon.removeClass('asc'),icon.css('transform','rotate(-90deg)'),icon.addClass('desc')) + : (icon.removeClass('desc'),icon.css('transform','rotate(90deg)'),icon.addClass('asc')); + that.exeTreeNodesSort(tr,that.config.treeConfig.treeid,isAsc); + } + }); + + + table.on('checkbox('+filter+')', function(obj){ + var thisInput = this + ,treeData = configs.tableTreeCache[tableTreeid]; + var tr = $(thisInput).parents('tr'),lvl = tr.data('lvl'); + lvl = lvl ? lvl : 0; + obj.type === 'one' && function(){ + that.setChildNodeChecked(tr,lvl,obj.checked); + that.setSupperNodeChecked(tr,obj.checked); + }(); + var _treeid = tr.attr("tree-id"); + //注意 + //layui组件获取的obj.data可能不正确 + //所以在此直接以此方式获取 保证取到相对应节点数据 + obj.data = that.getDataByTreeId(treeData,_treeid); + active.callbackFn.call(this,'checkbox('+filter+')',obj); + }); + + //showField字段单元格点击事件,展开子叶节点 + var SPAN_SELECTOR = 'div.layui-tableTree-div span.layui-tableEdit-span'; + tableBody.on('click',SPAN_SELECTOR,function (e) { + _layui.stope(e); + var thisTreeElem = $(this).parents('tr'); + var lvl = thisTreeElem.data('lvl');lvl = lvl ? lvl : 0; + var icon = $(this).parents('td').find('i.layui-tableEdit-edge'); + var isShow = icon.hasClass('layui-tableEdit-clicked'); + //关闭或者展开子元素 + that.showOrHideChildrenNodes(thisTreeElem,lvl,!isShow); + //选择三角图标 + that.rotateFunc(icon,!isShow); + }); + + //展示工具栏 + if(this.config.treeConfig.showToolbar){ + //操作栏鼠标经过事件 + tableBody.on("mouseenter ",'td[data-field="'+showField+'"]',function (e) { + _layui.stope(e) + tableBody.find('div.layui-tableTree-edit').hide(); + $(this).find('div.layui-tableTree-edit').show(); + }); + tableBody.on("mouseleave",'td[data-field="'+showField+'"]',function (e) { + _layui.stope(e) + tableBody.find('div.layui-tableTree-edit').hide(); + }); + } + + var INPUT_SELECTOR = 'td[data-field="'+showField+'"] input.layui-tableEdit-input'; + tableBody.on('blur',INPUT_SELECTOR,function () { + $(this).remove(); + }); + + var toolEvent = 'tool('+filter+')'; + table.on(toolEvent,function (obj) { + var zthis = this + ,field = $(zthis).data('field') + ,config = that.config.colsConfig[field] + ,treeData = configs.tableTreeCache[tableTreeid]; + obj.field = field; + if(!config){ + active.callbackFn.call(zthis,toolEvent,obj);return; + } + var callbackFn = function (res) { + obj.value = Array.isArray(res) ? (res.length>0 ? res : [{name:'',value:''}]) : res; + active.callbackFn.call(zthis,toolEvent,obj); + }; + var _treeid = $(zthis.parentNode).attr("tree-id"); + //子节点或表格树删除最上级节点后,layui组件得到的数据就不对了。 + //因此直接用getDataByTreeId函数获取 + obj.data = that.getDataByTreeId(treeData,_treeid); + obj.update = function (fields) { + if(!fields)return; + for(var key in fields){ + obj.data[key] = fields[key]; + var showValue = that.parseTempletData(obj.data,key); + showValue = showValue ? showValue : ''; + $(zthis).find('div.layui-table-cell').text(showValue) + } + }; + if(config.type === 'select'){ + editEntity.register({data:config.data,element:zthis,enabled:config.enabled,selectedData:obj.data[field],callback:callbackFn}); + }else if(config.type === 'date'){ + editEntity.date({dateType:config.dateType,element:zthis,callback:callbackFn}); + }else if(config.type === 'input'){ + editEntity.input({element:zthis,oldValue:obj.data[field],callback:callbackFn}); + }else if(config.type === 'signedInput'){ + editEntity.signedInput({element:zthis,oldValue:obj.data[field],callback:callbackFn}); + }else active.callbackFn.call(zthis,toolEvent,obj); + }); + + var SELECTOR='div.layui-tableTree-div i.'+iconClass; + tableBody.on('click',SELECTOR,function (e) { + _layui.stope(e); + var tr = $(this).parents('tr') + ,treeData = configs.tableTreeCache[tableTreeid]; + var lvl = tr.data('lvl');lvl = lvl ? lvl : 0; + var td = $(this).parents('td') + ,field = td.data('field') + ,treeid = tr.attr('tree-id') + ,data = that.getDataByTreeId(treeData,treeid) + ,thisObj = {value:null,data:data,field:field,async:function (asyncTree) { + that.openTreeNode(tr); + if(!asyncTree || asyncTree.length === 0)return; + asyncTree = that.removeRepeatData(asyncTree); + if(asyncTree.length === 0)return; + that.asyncAddTree(asyncTree,data,tr,lvl); + if(!data.treeList)data.treeList = []; + var count= 0; + while (count0){ + that.asyncAddTree(e.treeList,e,tr.next(),lvl+1); + } + }); + var nextIcon = tr.find('td[data-field="'+that.config.treeConfig.showField+'"] i.layui-tableEdit-edge'); + that.rotateFunc(nextIcon,true); + } + /** + * 按table表格列模板(templet)解析数据 + * @param d 当前节点数据 + * @param field 列字段 + * @returns {*} + */ + ,parseTempletData:function (d,field) { + var rs = null; + this.config.cols.forEach(function (item1) { + item1.forEach(function (item2) { + if(item2.field === field){ + var templet = item2.templet; + if(templet){ + if(typeof templet === 'string'){ + rs = laytpl($(templet).html()).render(d); + }else { + rs = templet(d,field); + } + }else { + rs = d[field]; + } + } + }); + }); + return rs; + } + /** + * 清除当前数据 + * @param data 当前数据 + */ + ,clearCacheData:function (data) { + if(!data)return + var treeid = this.config.treeConfig.treeid; + var tableTreeid = $(this.config.elem).attr('id'); + var treeData = configs.tableTreeCache[tableTreeid]; + this.clearChildCacheData(treeData,data[treeid]); + } + /** + * 清除当前数据的子叶节点数据 + * @param list 表格树数据 + * @param uniqueTreeid 当前节点treeid字段值 + */ + ,clearChildCacheData:function (list,uniqueTreeid) { + var treeid = this.config.treeConfig.treeid; + for(var i=0;i 0){ + this.clearChildCacheData(item.treeList,uniqueTreeid) + } + } + } + } + /** + * 旋转三角图标 + * @param icon 当前三角图标 + * @param isOpen true为关闭 false打开 + */ + ,rotateFunc:function (icon,isOpen) { + if(!isOpen){ + icon.css('transform',''); + icon.removeClass('layui-tableEdit-clicked'); + }else { + icon.css('transform','rotate(90deg)'); + icon.addClass('layui-tableEdit-clicked'); + } + } + /** + * 展开或者关闭当前节点及其叶子节点 + * @param elemTree 当前节点tr + * @param lvl 当前节点等级 + * @param isShow true打开 false关闭 + */ + ,showOrHideChildrenNodes:function (elemTree,lvl,isShow) { + var nextTreeElem = elemTree.next(),nextlvl = nextTreeElem.data('lvl'); + nextlvl = nextlvl ? nextlvl : 0; + if(nextTreeElem[0] && nextlvl > lvl){ + if(isShow){ + if((nextlvl-lvl) === 1){ + nextTreeElem.show(); + var nextIcon = nextTreeElem.find('td[data-field="'+this.config.treeConfig.showField+'"] i.layui-tableEdit-edge'); + if(nextIcon.hasClass('layui-tableEdit-clicked')){ + nextTreeElem.show(); + this.showTreeNodes(nextTreeElem,nextlvl); + } + } + }else { + nextTreeElem.hide(); + } + this.showOrHideChildrenNodes(nextTreeElem,lvl,isShow); + } + } + /** + * 展开当前节点及其叶子节点 + * @param elemTree 当前节点tr + * @param lvl 当前节点等级 + */ + ,showTreeNodes:function (elemTree,lvl) { + var nextTreeElem = elemTree.next(),nextlvl = nextTreeElem.data('lvl'); + nextlvl = nextlvl ? nextlvl : 0; + if(nextTreeElem[0] && nextlvl > lvl){ + if((nextlvl-lvl) === 1){ + nextTreeElem.show(); + var nextIcon = nextTreeElem.find('td[data-field="'+this.config.treeConfig.showField+'"] i.layui-tableEdit-edge'); + if(nextIcon.hasClass('layui-tableEdit-clicked')){ + nextTreeElem.show(); + this.showTreeNodes(nextTreeElem,nextlvl); + } + } + this.showTreeNodes(nextTreeElem,lvl); + } + } + /** + * 获取当前节点的父节点 + * @param elem 当前节点tr + * @param lvl 当前节点等级 + * @returns {null|*|jQuery|*} + */ + ,getParentNode:function (elem,lvl) { + if(lvl===0)return null; + var tr = $(elem) + ,prevElem = tr.prev() + ,prevLvl = prevElem.data('lvl'); + prevLvl = prevLvl ? prevLvl : 0; + if(lvl-prevLvl === 1) return prevElem; + return this.getParentNode(prevElem,lvl); + } + /** + * 删除当前节点的叶子节点 + * @param elemTree 当前节点tr + * @param lvl 当前节点等级 + */ + ,removeChildren:function (elemTree,lvl) { + var nextTreeElem = elemTree.next(),nextlvl = nextTreeElem.data('lvl'); + nextlvl = nextlvl ? nextlvl : 0; + if(nextTreeElem[0] && nextlvl > lvl){ + nextTreeElem.remove(); + this.removeChildren(elemTree,lvl); + } + } + /** + * 判断当前节点数据的treeid是否重复 + * @param data 当前节点数据 + * @returns {boolean} + */ + ,checkedRepeatData:function (data) { + var treeid = this.config.treeConfig.treeid; + var tableTreeid = $(this.config.elem).attr('id'); + var treeData = configs.tableTreeCache[tableTreeid]; + var ckeckData = this.getDataByTreeId(treeData,data[treeid]); + return ckeckData ? true : false; + } + /** + * 获取选中节点数据 + * @returns {*} + */ + ,getCheckedTreeNodeData:function () { + var that = this; + var tableBody = $(this.config.elem).next().find('div.layui-table-body'); + var tableTreeid = $(that.config.elem).attr('id'); + var treeData = configs.tableTreeCache[tableTreeid]; + var checkedElem = tableBody.find('td.layui-table-col-special div.layui-form-checked'); + var dataSet = new Set(); + checkedElem.each(function () { + var tr = $(this).parents('tr').eq(0); + var treeid = tr.attr('tree-id'); + var data = that.getDataByTreeId(treeData,treeid); + dataSet.add(data); + }); + return Array.from(dataSet); + } + /** + * 获取当前节点的最顶级节点 + * @param elem 当前节点 + * @returns {*} + */ + ,getTopNode:function (elem) { + var lvl = elem.data('lvl') + ,prevTr = elem.prev(); + lvl = lvl ? lvl : 0; + if(lvl === 0) return elem; + return this.getTopNode(prevTr); + } + /** + * 重载表格树 + * @param options + */ + ,reload:function (options) { + if(options && options.done){ + this._done = options.done; + } + this.render(this.config); + } + /** + * 指定节点或节点id展开节点 + * @param params 节点或节点id + */ + ,openTreeNode:function (params) { + var that = this; + var treeid,tr; + if(typeof params === 'string' || Object.prototype.toString.call(params) === '[object Number]'){ + treeid = params; + tr = $(that.config.elem).next().find('div.layui-table-body tr[tree-id="'+treeid+'"]'); + }else { + tr = $(params); + } + tr.show(); + var icon = tr.find('td[data-field="'+that.config.treeConfig.showField+'"] i.layui-tableEdit-edge'); + var lvl = tr.data('lvl'); + lvl = lvl ? lvl : 0; + that.showOrHideChildrenNodes(tr,lvl,true); + that.rotateFunc(icon,true); + var parentNode = that.getParentNode(tr,lvl); + while(parentNode && parentNode[0]){ + parentNode.show(); + icon = parentNode.find('td[data-field="'+that.config.treeConfig.showField+'"] i.layui-tableEdit-edge'); + lvl = parentNode.data('lvl'); + lvl = lvl ? lvl : 0; + that.rotateFunc(icon,true); + parentNode = that.getParentNode(parentNode,lvl); + } + var topNode = that.getTopNode(tr); + that.showOrHideChildrenNodes(topNode,0,true); + } + /** + * 指定节点或节点id折叠节点 + * @param params 节点或节点id + */ + ,closeTreeNode:function (params) { + var that = this; + var treeid,tr; + if(typeof params === 'string' || Object.prototype.toString.call(params) === '[object Number]'){ + treeid = params; + tr = $(that.config.elem).next().find('div.layui-table-body tr[tree-id="'+treeid+'"]'); + }else { + tr = $(params); + } + var icon = tr.find('td[data-field="'+that.config.treeConfig.showField+'"] i.layui-tableEdit-edge'); + var lvl = tr.data('lvl'); + lvl = lvl ? lvl : 0; + that.showOrHideChildrenNodes(tr,lvl,false); + that.rotateFunc(icon,false); + } + /** + * 展开所有节点 + */ + ,openAllTreeNodes:function () { + var that = this; + var tableBody = $(this.config.elem).next().find('div.layui-table-body'); + tableBody.find('tr').each(function (e) { + var tr = $(this); + var icon = tr.find('td[data-field="'+that.config.treeConfig.showField+'"] i.layui-tableEdit-edge'); + $(this).show(); + that.rotateFunc(icon,true); + }); + } + /** + * 折叠所有节点 + */ + ,closeAllTreeNodes:function () { + var that = this; + var tableBody = $(this.config.elem).next().find('div.layui-table-body'); + tableBody.find('tr').each(function (e) { + var tr = $(this); + var icon = tr.find('td[data-field="'+that.config.treeConfig.showField+'"] i.layui-tableEdit-edge'); + var lvl = tr.data('lvl'); + lvl = lvl ? lvl : 0; + if(lvl > 0)$(this).hide(); + that.rotateFunc(icon,false); + }); + } + /** + * 表格树节点逐级排序 + * @param options {field:'xxx',desc:true} field:排序字段 desc => true降序、false升序 + * @param treeData 表格树数据 + */ + ,sort:function (options,treeData) { + var that = this; + treeData.sort(function(o1, o2){ + var isNum = /(^[-+]?\d+$)|(^[-+]?\d+\.\d+$)/ + ,v1 = o1[options.field] + ,v2 = o2[options.field]; + if(isNum.test(v1) && isNum.test(v2)){ + v1 = parseFloat(v1); + v2 = parseFloat(v2); + if(v1 > v2){ + return 1; + } else if (v1 < v2) { + return -1; + } else { + return 0; + } + }else { + if(v1 && !v2){ + return 1; + } else if(!v1 && v2){ + return -1; + } + (v1+'').localeCompare(v2+'','zh-CN') + } + }); + if(options.desc){ + treeData.reverse(); + } + treeData.forEach(function (e) { + if(e.treeList && e.treeList.length>0){ + that.sort(options,e.treeList); + } + }); + } + /** + * 获取表格树参数配置 + * @returns {*} + */ + ,getTableTreeData:function () { + var tableTreeid = $(this.config.elem).attr('id'); + var treeData = configs.tableTreeCache[tableTreeid]; + return treeData; + } + /** + * 增加最上级节点 => 单个节点 + * @param data object类型 + */ + ,addTopTreeNode:function (data) { + var that = this; + var treeid = that.config.treeConfig.treeid; + var treepid = that.config.treeConfig.treepid; + var tableTreeid = $(that.config.elem).attr('id') + ,treeData = configs.tableTreeCache[tableTreeid]; + if(treeData.length<=0){ + if(!data){ + data = {}; + } + that.config.cols.forEach(function (item1) { + item1.forEach(function (item) { + if(item.field){ + if(!(item.field in data)){ + data[item.field] = null; + } + } + }); + }); + delete data[treepid]; //最上级行不能有treepid + var url = that.config.url; + var hasUrl = !configs.isEmpty(url); + hasUrl && delete that.config.url; + if(!data[treeid]){ + data[treeid] = Number(Math.random().toString().substr(3, 3) + Date.now()).toString(36) + } + treeData.push(data); + that.config.data = treeData; + that.render(that.config); + if(hasUrl){ + delete that.config.data; + that.config.url = url; + } + return; + } + + if(!data || (data && typeof data !== 'object')){ + data = {}; + for(var key in treeData[0]){ + data[key] = null; + } + } + delete data[treepid]; //最上级行不能有treepid + var tr = $(that.config.elem).next().find('div.layui-table-body tr').eq(0); + if(configs.isEmpty(data[treeid]) || that.checkedRepeatData(data)){ + data[treeid] = Number(Math.random().toString().substr(3, 3) + Date.now()).toString(36) + } + var newTr = $(''); + tr.before(newTr); + data['LAY_TABLE_INDEX'] = treeData.length; + treeData.push(data); + that.addElementToTr(data,tr,newTr,0); + that.rotateFunc( newTr.find('td[data-field="'+that.config.treeConfig.showField+'"] i.layui-tableEdit-edge'),true); + } + /** + * 新增行tr + * @param data 当前行数据 + * @param tr newTr的前一行的tr + * @param newTr 新增行tr + * @param lvl 新增行等级 + */ + ,addElementToTr:function (data,tr,newTr,lvl) { + var that = this; + tr.children('td').each(function () { + var field = $(this).data('field'),td = null; + if(field+"" === '0'){ + td = $(''); + td.append(this.innerHTML); + }else { + var attrsStr = [] + ,_divclass = $(this).children('div.layui-table-cell').attr("class") + ,attrs = this.attributes; + for(var i=0;i
            '+text+'
            '); + that.addIcon(newTr,td,lvl); + }else { + newTr.append('
            '+text+'
            '); + } + } + newTr.append(td); + }); + form.render('checkbox'); + } + /** + * 指定节点或节点id删除节点及其叶子节点 + * @param params + */ + ,delTreeNode:function (params) { + if(!params)return; + var that = this; + var tableTreeid = $(that.config.elem).attr('id'); + var treeData = configs.tableTreeCache[tableTreeid]; + var treeid,tr; + if(typeof params === 'string' || Object.prototype.toString.call(params) === '[object Number]'){ + treeid = params; + tr = $(that.config.elem).next().find('div.layui-table-body tr[tree-id="'+treeid+'"]'); + }else { + tr = $(params); + treeid = tr.attr("tree-id"); + } + var data = that.getDataByTreeId(treeData,treeid); + var lvl = tr.data('lvl');lvl = lvl ? lvl : 0; + //删除页面隐藏的树 + that.removeChildren(tr,lvl); + //清楚缓存中的数据 + that.clearCacheData(data); + tr.remove(); + } + /** + * 判断当前节点的叶子节点是否有被选中的 + * @param elem 当前节点tr + * @param lvl 当前节点等级 + * @returns {boolean|*} + */ + ,childNodeHasChecked:function (elem,lvl) { + var nextTreeElem = elem.next(),nextlvl = nextTreeElem.data('lvl'); + nextlvl = nextlvl ? nextlvl : 0; + if(nextTreeElem[0] && nextlvl > lvl){ + var div = $(nextTreeElem).find('td.layui-table-col-special div.layui-form-checkbox'); + return div.hasClass('layui-form-checked') ? true : this.childNodeHasChecked(nextTreeElem,lvl); + }else { + return false; + } + } + /** + * 设置当前节点及其叶子节点为被选中状态 + * @param elem 当前节点 + * @param lvl 当前节点等级 + * @param isChecked true为选中 false为取消选中 + */ + ,setChildNodeChecked:function (elem,lvl,isChecked) { + var nextTreeElem = elem.next(),nextlvl = nextTreeElem.data('lvl'); + nextlvl = nextlvl ? nextlvl : 0; + if(nextTreeElem[0] && nextlvl > lvl){ + this.setCheckboxInfo(nextTreeElem,isChecked); + this.setChildNodeChecked(nextTreeElem,lvl,isChecked); + } + } + /** + * 设置当前节点的祖辈节点为被选中状态 + * @param elem 当前节点 + * @param isChecked true为选中 false为取消选中 + */ + ,setSupperNodeChecked:function (elem,isChecked) { + var that = this; + var thisTreeid = $(elem).attr("tree-id") + ,treepid = that.config.treeConfig.treepid + ,tableTreeid = $(that.config.elem).attr('id') + ,treeData = configs.tableTreeCache[tableTreeid] + ,data = that.getDataByTreeId(treeData,thisTreeid) + ,thisTreepid = data[treepid] + ,tableBody = $(that.config.elem).next().find('div.layui-table-body') + ,ptr = tableBody.find('tr[tree-id="'+thisTreepid+'"]'); + var lvl = ptr.data('lvl'); + lvl = lvl ? lvl : 0; + if(!isChecked){ + if(!that.childNodeHasChecked(ptr,lvl)){ + that.setCheckboxInfo(ptr,isChecked); + } + }else { + that.setCheckboxInfo(ptr,isChecked); + } + if(lvl === 0)return; + that.setSupperNodeChecked(ptr,isChecked); + } + /** + * 设置当前节点的选中状态 + * @param elem 当前节点 + * @param isChecked true为选中 false为取消选中 + */ + ,setCheckboxInfo:function (elem,isChecked) { + var div = elem.find('td.layui-table-col-special div.layui-form-checkbox') + ,input = elem.find('td.layui-table-col-special input[type="checkbox"]'); + isChecked ? (div.addClass('layui-form-checked'),input.prop('checked',true)) + : (div.removeClass('layui-form-checked'),input.prop('checked',false)); + } + /** + * 关键词检索相关节点 + * @param value 关键词参数 + */ + ,keywordSearch:function (value) { + var that = this; + var tableBody = $(this.config.elem).next().find('div.layui-table-body'); + var spans = tableBody.find('td[data-field="'+this.config.treeConfig.showField+'"] span.layui-tableEdit-span'); + var tableTreeid = $(that.config.elem).attr('id'); + var treeData = configs.tableTreeCache[tableTreeid]; + var treepid = that.config.treeConfig.treepid; + that.clearSearch();//每次搜索前都清除标记 然后再进行标记 + tableBody.find("tr").each(function () { + var tr = $(this); + if(!tr.is(":hidden")){ + tr.addClass("layui-tableTree-search"); + } + }); + var showArr = new Set(); + spans.each(function () { + var thisValue = this.innerText; + var tr = $(this).parents('tr').eq(0); + if(thisValue.indexOf(value)>-1){ + tr.show(); + var icon = tr.find('td[data-field="'+that.config.treeConfig.showField+'"] i.layui-tableEdit-edge') + tr.hasClass('layui-tableTree-search') + && !icon.hasClass('layui-tableEdit-clicked') + && (that.rotateFunc(icon,true),icon.addClass('layui-tableTree-search')); + var treeid = tr.attr('tree-id'); + showArr.add(treeid) + var data = that.getDataByTreeId(treeData,treeid); + var _treepid = data[treepid]; + var parentNode = tableBody.find('tr[tree-id="'+_treepid+'"]'); + while (parentNode[0]) { + parentNode.show(); + treeid = parentNode.attr('tree-id'); + data = that.getDataByTreeId(treeData,treeid); + if(data){ + showArr.add(treeid); + icon = parentNode.find('td[data-field="'+that.config.treeConfig.showField+'"] i.layui-tableEdit-edge') + parentNode.hasClass('layui-tableTree-search') + && !icon.hasClass('layui-tableEdit-clicked') + && (that.rotateFunc(icon,true),icon.addClass('layui-tableTree-search')); + } + _treepid = data[treepid]; + parentNode = tableBody.find('tr[tree-id="'+_treepid+'"]'); + } + }else { + var treeid = tr.attr('tree-id'); + !showArr.has(treeid) && tr.hide(); + } + }); + } + /** + * 重置表格树到检索前折叠状态 + */ + ,clearSearch:function () { + var that = this; + var tableBody = $(this.config.elem).next().find('div.layui-table-body'); + var searchTrs = tableBody.find("tr.layui-tableTree-search"); + if(searchTrs.length<=0) return; + tableBody.find("tr").each(function () { + var tr = $(this); + if(tr.hasClass("layui-tableTree-search")){ + tr.show(); + var icon = tr.find('td[data-field="'+that.config.treeConfig.showField+'"] i.layui-tableEdit-edge.layui-tableTree-search'); + if(icon[0]){ + that.rotateFunc(icon,false); + icon.removeClass('layui-tableTree-search'); + icon.removeClass('layui-tableEdit-clicked'); + } + }else { + tr.hide(); + } + tr.removeClass("layui-tableTree-search"); + }); + } + /** + * 刷新当前表格树 => 局部刷新 + * @param data 表格树数据 + * @param tr 当前节点tr + * @param lvl 当前节点等级 + * @param isFirst 当前节点是否为表格树的第一个节点 + */ + ,refreshTableBody:function (data,tr,lvl,isFirst) { + var that = this; + var treeid = that.config.treeConfig.treeid; + var tbody = $(that.config.elem).next().find('div.layui-table-body tbody'); + _layui.each(data,function (index,item) { + var newTr; + if(!isFirst)tr = tbody.children(':last-child'); + newTr = lvl<=0 ? $('') + : $(''); + !isFirst ? tr.after(newTr) : (tbody.html(''),tbody.append(newTr)); + isFirst = false; + that.addElementToTr(item,tr,newTr,lvl); + if(item.treeList){ + that.refreshTableBody(item.treeList,newTr,lvl+1,false); + } + }); + } + /** + * 刷新当前表格树 => 局部刷新 + * @param data 表格树数据 + */ + ,refresh:function (data) { + var tableTreeid = $(this.config.elem).attr('id'); + var treeid= this.config.treeConfig.treeid; + var treeData = configs.tableTreeCache[tableTreeid]; + if(data){ + treeData.splice(0,treeData.length); + data.forEach(function (e) { + if(!e[treeid]){ + e[treeid] = Number(Math.random().toString().substr(3, 3) + Date.now()).toString(36) + } + treeData.push(e); + }) + } + this.sort({field:this.config.treeConfig.treeid},treeData); + if(treeData.length<-0) return; + _layui.each(treeData,function (index,item) { + item['LAY_TABLE_INDEX'] = index; + }); + var tr = $(this.config.elem).next().find('div.layui-table-body tr').eq(0); + this.refreshTableBody(treeData,tr,0,true); + this.openAllTreeNodes(); + form.render('checkbox'); + } + /** + * 根据当前节点id获取叶子节点数据 + * @param treeData 表格树数据 + * @param treeid 当前节点id + * @param childArr 获取的子节点数据 + */ + ,findChildTreeNodes:function (treeData,treeid,childArr) { + var that = this; + var treepid = this.config.treeConfig.treepid; + treeData.forEach(function (item) { + if(treepid in item){ + if(item[treepid]+"" === treeid+""){ + childArr.push(item); + }else { + if(item.treeList){ + that.findChildTreeNodes(item.treeList,treeid,childArr); + } + } + }else { + if(item.treeList){ + that.findChildTreeNodes(item.treeList,treeid,childArr); + } + } + }); + } + /** + * 指定节点/节点id逐级排序 + * @param params 节点/节点id + * @param field 排序字段 + * @param isAsc true升序 false降序 + */ + ,sortByTreeNode:function (params,field,isAsc) { + if(!params)return; + if(typeof params === 'string' || Object.prototype.toString.call(params) === '[object Number]'){ + params = $(this.config.elem).next().find('div.layui-table-body tr[tree-id="'+params+'"]'); + } + var tr = $(params);tr.show(); + this.exeTreeNodesSort(tr,field,isAsc); + var lvl = tr.data('lvl'),icon;lvl = lvl ? lvl : 0; + var parentNode = this.getParentNode(tr,lvl); + while(parentNode && parentNode[0]){ + parentNode.show(); + icon = parentNode.find('td[data-field="'+this.config.treeConfig.showField+'"] i.layui-tableEdit-edge'); + lvl = parentNode.data('lvl'); + lvl = lvl ? lvl : 0; + this.rotateFunc(icon,true); + parentNode = this.getParentNode(parentNode,lvl); + } + var topNode = this.getTopNode(tr); + this.showOrHideChildrenNodes(topNode,0,true); + } + /** + * 当前节点叶子节点逐级排序 + * @param tr 当前节点 + * @param field 排序字段 + * @param isAsc true升序 false降序 + */ + ,exeTreeNodesSort:function (tr,field,isAsc) { + var that = this; + var tableTreeid = $(that.config.elem).attr('id'); + var treeData = configs.tableTreeCache[tableTreeid]; + var lvl = tr.data('lvl');lvl = lvl ? lvl : 0; + var brotherArr = []; + that.findChildTreeNodes(treeData,tr.attr('tree-id'),brotherArr); + var $treeid = that.config.treeConfig.treeid; + if(brotherArr.length>0){ + that.sort({field:field,desc:isAsc},brotherArr); + brotherArr.forEach(function (item) { + var ztr = $('tr[tree-id="'+item[$treeid]+'"]'),zlvl = ztr.data('lvl'); + zlvl = zlvl ? zlvl : 0; + that.removeChildren(ztr,zlvl); + ztr.remove(); + }); + var sortData = that.getDataByTreeId(treeData,tr.attr('tree-id')); + that.asyncAddTree(brotherArr,sortData,tr,lvl); + } + } + }; + + var active = { + on:function (event,callback) { + var filter = event.match(/\((.*)\)$/),eventName = (filter ? (event.replace(filter[0],'')+'_'+ filter[1]) : event); + configs.callbacks[moduleName+'_'+eventName]=callback; + }, + callbackFn:function (event,params) { + var filter = event.match(/\((.*)\)$/),eventName = (filter ? (event.replace(filter[0],'')+'_'+ filter[1]) : event); + var key = moduleName+'_'+eventName,func = configs.callbacks[key]; + if(!func) return; + return func.call(this,params); + }, + render:function (options) { + var tableTree = new TableTree(); + tableTree.render(options,true); + return { + getTreeOptions:function () { + return tableTree.config; + }, + getCheckedTreeNodeData:function () { + return tableTree.getCheckedTreeNodeData(); + }, + reload:function (options) { + tableTree.reload(options); + }, + openTreeNode:function (params) { + tableTree.openTreeNode(params); + } + ,closeTreeNode:function (params) { + tableTree.closeTreeNode(params); + } + ,closeAllTreeNodes:function () { + tableTree.closeAllTreeNodes(); + } + ,openAllTreeNodes:function () { + tableTree.openAllTreeNodes(); + } + ,sort:function (options) { + var tableTreeid = $(tableTree.config.elem).attr('id'); + var treeData = configs.tableTreeCache[tableTreeid]; + tableTree.sort(options,treeData); + if(treeData.length<-0) return; + _layui.each(treeData,function (index,item) { + item['LAY_TABLE_INDEX'] = index; + }); + var tr = $(tableTree.config.elem).next().find('div.layui-table-body tr').eq(0); + tableTree.refreshTableBody(treeData,tr,0,true); + tableTree.openAllTreeNodes(); + form.render('checkbox'); + } + ,getTableTreeData:function () { + return tableTree.getTableTreeData(); + } + ,addTopTreeNode:function (data) { + tableTree.addTopTreeNode(data); + } + ,delTreeNode:function (params) { + tableTree.delTreeNode(params); + } + ,keywordSearch:function (value) { + tableTree.keywordSearch(value); + } + ,clearSearch:function () { + tableTree.clearSearch(); + } + ,refresh:function (data) { + tableTree.refresh(data); + } + ,sortByTreeNode:function (tr,field,isAsc) { + tableTree.sortByTreeNode(tr,field,isAsc); + } + }; + } + }; + exports(moduleName, active); +}); \ No newline at end of file diff --git a/public/js/lay-module/tablefilter/tableFilter.css b/public/js/lay-module/tablefilter/tableFilter.css new file mode 100644 index 0000000..b775f30 --- /dev/null +++ b/public/js/lay-module/tablefilter/tableFilter.css @@ -0,0 +1,22 @@ +/*table 过滤*/ +.layui-table-filter {height:100%;cursor: pointer;position: absolute;right:15px;padding:0 5px;} +.layui-table-filter i {font-size: 12px;color: #ccc;} +.layui-table-filter:hover i {color: #666;} +.layui-table-filter.tableFilter-has i {color: #1E9FFF;} +.layui-table-filter-view {min-width:200px;background:#FFFFFF;border: 1px solid #d2d2d2;box-shadow: 0 2px 4px rgba(0,0,0,.12);position:absolute;top:0px;left:0px;z-index:90000000;} +.layui-table-filter-box {padding:10px;} +.layui-table-filter-box .loading {width: 100%;height: 100%;text-align: center;line-height: 150px;} +.layui-table-filter-box .loading i {font-size: 18px;} +.layui-table-filter-box input.layui-input {margin-bottom:10px;} +.layui-table-filter-box ul {border: 1px solid #eee;height:150px;overflow: auto;margin-bottom:10px;padding:5px 10px 5px 10px;} +.layui-table-filter-box ul li {padding:3px 0;} +.layui-table-filter-box ul.radio {padding:0px;} +.layui-table-filter-box ul.radio li {padding:0px;} +.layui-table-filter-box ul li .layui-form-radio {display: block;color:#666;margin:0px;padding:0px;transition: .1s linear;} +.layui-table-filter-box ul li .layui-form-radio div {display: block;padding:0 10px;} +.layui-table-filter-box ul li .layui-form-radio i {display: none;} +.layui-table-filter-box ul li .layui-form-radio:hover {background:#f9f9f9;} +.layui-table-filter-box ul li .layui-form-radio.layui-form-radioed {background:#5FB878;color: #fff;} +.layui-table-filter-date {margin-bottom:10px;} +.layui-table-filter-date .layui-laydate {box-shadow:none;border:0;} + diff --git a/public/js/lay-module/tablefilter/tableFilter.js b/public/js/lay-module/tablefilter/tableFilter.js new file mode 100644 index 0000000..ed91588 --- /dev/null +++ b/public/js/lay-module/tablefilter/tableFilter.js @@ -0,0 +1,532 @@ +/** + TABLEFILTER +**/ + +layui.define(['table', 'jquery', 'form', 'laydate'], function (exports) { + + var MOD_NAME = 'tableFilter', + $ = layui.jquery, + table = layui.table, + form = layui.form, + laydate = layui.laydate; + + var tableFilter = { + "v" : '1.0.0' + }; + + //缓存 + tableFilter.cache = {} + + //渲染 + tableFilter.render = function(opt){ + + //配置默认值 + var elem = $(opt.elem || '#table'), + elemId = elem.attr("id") || "table_" + new Date().getTime(), + filters = opt.filters || [], + parent = opt.parent || 'body', + mode = opt.mode || "local"; + opt.done = opt.done || function(){}; + + //写入默认缓存 + tableFilter.cache[elemId]={}; + + //主运行 + var main = function (){ + + //默认过滤 + if(mode == "local"){ + var trsIndex = tableFilter.getShowTrIndex(elem, elemId, filters); + if(trsIndex.length > 0){ + var trs = elem.next().find('.layui-table-body tr'); + trs.each(function(i, tr){ + if($.inArray($(tr).data("index"), trsIndex) != -1){ + $(tr).removeClass("layui-hide") + }else{ + $(tr).addClass("layui-hide") + } + }) + }else{ + elem.next().find('.layui-table-body tr').removeClass("layui-hide") + } + + //FIX全选监听 + tableFilter.fixAll(elem); + //重载表格尺寸 (FIX刷新表格时的表格异常) + table.resize(elemId); + } + + + + + //遍历过滤项 + layui.each(filters, function(i, filter){ + var filterField = filter.field, + filterName = filter.name || filter.field, + filterType = filter.type || "input", + filterData = filter.data || [], + filterUrl = filter.url || ""; + + //插入图标 + var th = elem.next().find('.layui-table-header th[data-field="'+filterField+'"]'); + var icon = filterType == 'input' ? 'layui-icon-search' : 'layui-icon-down'; + var filterIcon = $(''); + th.find('.layui-table-cell').append(filterIcon) + + //图标默认高亮 + if(tableFilter.cache[elemId][filterName]){ + filterIcon.addClass("tableFilter-has") + }else{ + filterIcon.removeClass("tableFilter-has") + } + + //图标点击事件 + filterIcon.on("click", function(e) { + e.stopPropagation(); + //得到过滤项的选项 + //如果开启本地 并且没设置数据 就读本地数据 + if( !filter.data && !filterUrl && filterType != "input"){ + filterData = tableFilter.eachTds(elem, filterField); + } + + //弹出层 + var t = $(this).offset().top + $(parent).scrollTop() + $(this).outerHeight() + 5 +"px"; + var l_fix = filterType == "date" ? 530 : 164; + var l = $(this).offset().left - ($('body').outerWidth(true) - $(parent).outerWidth(true)) - l_fix +"px"; + + var filterBox = $('
            '); + if(filterType == "input"){ + filterBox.find('form').append(''); + } + if(filterType == "checkbox"){ + filterBox.find('form').append('
              '); + if(!filterUrl){ + layui.each(filterData, function(i, item){ + filterBox.find('ul').append('
            • '); + }) + } + } + if(filterType == "radio"){ + filterBox.find('form').append('
                '); + if(!filterUrl){ + filterBox.find('ul').append('
              • '); + layui.each(filterData, function(i, item){ + filterBox.find('ul').append('
              • '); + }) + } + } + if(filterType == "date"){ + filterBox.find('form').append('
                '); + filterBox.find('form').append(''); + + } + filterBox.find('form').append(''); + filterBox.find('form').append(''); + + //设置清除是否可用 + $(this).hasClass('tableFilter-has') && filterBox.find('.filter-del').removeClass("layui-btn-disabled").removeAttr("disabled","disabled"); + + //加入DOM + $(parent).append(filterBox); + + //赋值FORM + form.val("table-filter-form", tableFilter.toLayuiFrom(elemId, filterName, filterType)); + + //渲染layui form + form.render(null, 'table-filter-form'); + + //渲染日期 + if(filterType == "date"){ + laydate.render({ + elem: '.layui-table-filter-date', + range: true, + type: 'date', + value: $('.layui-table-filter-date').next().val(), + position: 'static', + showBottom: false, + change: function(value, date, endDate){ + $('.layui-table-filter-date').next().val(value) + } + }); + } + + //渲染FORM 如果是searchInput 就默认选中 + var searchInput = filterBox.find('form input[type="search"]'); + searchInput.focus().select(); + + //处理异步filterData + if((filterType == 'checkbox' || filterType == 'radio') && filterUrl){ + var filterBoxUl = filterBox.find('.layui-table-filter-box ul'); + filterBoxUl.append('
                '); + $.getJSON(filterUrl + "?_t=" + new Date().getTime(), function(res, status, xhr){ + filterBoxUl.empty(); + filterType == "radio" && filterBoxUl.append('
              • '); + layui.each(res.data, function(i, item){ + filterType == "checkbox" && filterBoxUl.append('
              • '); + filterType == "radio" && filterBoxUl.append('
              • '); + }) + form.render(null, 'table-filter-form'); + form.val("table-filter-form", tableFilter.toLayuiFrom(elemId, filterName, filterType)); + }); + } + + //点击确认开始过滤 + form.on('submit(tableFilter)', function(data){ + //重构复选框结果 + if(filterType == "checkbox"){ + var NEWfield = []; + for(var key in data.field){ + NEWfield.push(data.field[key]) + } + data.field[filterName] = NEWfield + } + + //过滤项写入缓存 + tableFilter.cache[elemId][filterName] = data.field[filterName]; + + //如果有过滤项 icon就高亮 + if(tableFilter.cache[elemId][filterName].length > 0){ + filterIcon.addClass("tableFilter-has") + }else{ + filterIcon.removeClass("tableFilter-has") + } + + if(mode == "local"){ + //本地交叉过滤 + var trsIndex = tableFilter.getShowTrIndex(elem, elemId, filters); + if(trsIndex.length > 0 || data.field[filterName].length > 0){ + var trs = elem.next().find('.layui-table-body tr'); + trs.each(function(i, tr){ + if($.inArray($(tr).data("index"), trsIndex) != -1){ + $(tr).removeClass("layui-hide") + }else{ + $(tr).addClass("layui-hide") + } + }) + }else{ + elem.next().find('.layui-table-body tr').removeClass("layui-hide") + } + //更新合计行 + tableFilter.updataTotal(elem); + //更新序列号 + tableFilter.upNumbers(elem); + //取消表格选中 + tableFilter.uncheck(elem); + //重载表格尺寸 + table.resize(elemId) + }else if(mode == "api"){ + //服务端交叉过滤 + //将数组转字符串 + var new_where = {}; + for (var key in tableFilter.cache[elemId]) { + var filterKey = key, + filterValue = tableFilter.cache[elemId][key]; + if($.isArray(filterValue)){ + new_where[filterKey] = filterValue.join(","); + }else{ + new_where[filterKey] = filterValue; + } + } + table.reload(elemId,{"where":new_where}) + } + + //写入回调函数 + opt.done(tableFilter.cache[elemId]); + + filterBox.remove(); + return false; + }) + + //点击清除此项过滤 + filterBox.find('.layui-table-filter-box .filter-del').on('click', function(e) { + delete tableFilter.cache[elemId][filterName]; + filterIcon.removeClass("tableFilter-has"); + if(mode == "local"){ + var trsIndex = tableFilter.getShowTrIndex(elem, elemId, filters); + if(trsIndex.length > 0){ + var trs = elem.next().find('.layui-table-body tr'); + trs.each(function(i, tr){ + if($.inArray($(tr).data("index"), trsIndex) != -1){ + $(tr).removeClass("layui-hide") + }else{ + $(tr).addClass("layui-hide") + } + }) + }else{ + elem.next().find('.layui-table-body tr').removeClass("layui-hide") + } + //更新合计行 + tableFilter.updataTotal(elem) + //更新序列号 + tableFilter.upNumbers(elem) + //取消表格选中 + tableFilter.uncheck(elem) + //重载表格尺寸 + table.resize(elemId) + }else if(mode == "api"){ + //需要清除where里的对应的值 + var where = {}; + where[filterName] = '' + table.reload(elemId,{"where" : where}) + } + + opt.done(tableFilter.cache[elemId]); + filterBox.remove(); + }) + + //点击其他区域关闭 + $(document).mouseup(function(e){ + var userSet_con = $('.layui-table-filter-view'); + if(!userSet_con.is(e.target) && userSet_con.has(e.target).length === 0){ + filterBox.remove(); + } + }); + + }) + }) + + }; + main(); + + //函数返回 + var returnObj = { + 'config' : opt, + 'reload' : function(opt){ + main(); + //更新序列号 + tableFilter.upNumbers(elem); + } + } + return returnObj + } + + //遍历行获取本地列集合 return tdsArray[] + tableFilter.eachTds = function(elem, filterField){ + var tdsText = [], + tdsArray = []; + var tds = elem.next().find('.layui-table-body td[data-field="'+filterField+'"]'); + tds.each(function(i, td){ + tdsText.push($.trim(td.innerText)) + }) + tdsText = tableFilter.tool.uniqueObjArray(tdsText); + layui.each(tdsText, function(i, item){ + tdsArray.push({'key':item, 'value':item}) + }) + return tdsArray; + } + + //获取匹配的TR的data-index return trsIndex[] + tableFilter.getShowTrIndex = function(elem, elemId, filters){ + var trsIndex = []; + var filterValues = tableFilter.cache[elemId]; + + for (var key in filterValues) { + var filterKey = key, + filterValue = filterValues[key]; + + //如果有name比对filterField + layui.each(filters, function(i, item){ + if(filterKey == item.name){ + filterKey = item.field + } + }) + + var tds = elem.next().find('.layui-table-body td[data-field="'+filterKey+'"]'); + //获取这一次过滤的匹配 + var this_trsIndex = []; + tds.each(function(i, td){ + if($.isArray(filterValue)){ + //过滤值=数组 inArray 复选框 + if($.inArray($.trim(td.innerText), filterValue) >= 0 && filterValue && filterValue.length > 0){ + this_trsIndex.push($(td).parent().data("index")) + } + }else if(filterValue.indexOf(" - ") >= 0){ + //是否在时间段内 + var d = $.trim(td.innerText); + var s = filterValue.split(" - ")[0]; + var e = filterValue.split(" - ")[1]; + if(tableFilter.tool.isDuringDate(d,s,e)){ + this_trsIndex.push($(td).parent().data("index")) + } + }else{ + //过滤值=字符串 indexOf 单选框 输入框 + if($.trim(td.innerText).indexOf(filterValue) >= 0){ + this_trsIndex.push($(td).parent().data("index")) + } + } + }) + //取最终结果 合并数组后去相同值 + //第一次 不合并 + if(trsIndex.length <= 0){ + trsIndex = this_trsIndex + }else{ + if(this_trsIndex.length > 0){ + //这一次有值 和前面N次取相同值 + trsIndex = tableFilter.tool.getSameArray(trsIndex, this_trsIndex); + }else{ + //这一次没值 前面N次有值,如果字符串过滤未有值 就显示空 + trsIndex = $.isArray(filterValue) ? trsIndex : []; + } + } + } + return tableFilter.tool.uniqueObjArray(trsIndex); + } + + //JSON 数据转layuiFOMR 可用的 处理checkbox + tableFilter.toLayuiFrom = function(elemId, filterName, filterType){ + var form_val = JSON.stringify(tableFilter.cache[elemId]); + form_val = JSON.parse(form_val); + if(filterType == "checkbox"){ + layui.each(form_val[filterName], function(i, value){ + form_val[filterName + "["+value+"]"] = true; + }) + delete form_val[filterName]; + } + return form_val; + } + + //更新合计行数据 + tableFilter.updataTotal = function(elem){ + var elemId = elem.attr("id"); + table.eachCols(elemId, function(i, item){ + if(item.totalRow){ + var tdAllnum = 0; + var tds = elem.next().find('.layui-table-body td[data-field="'+item.field+'"]') + tds.each(function(i, td){ + if(!$(td).parent().hasClass('layui-hide')){ + //FIX JS计算精度 + tdAllnum = (tdAllnum*10 + Number($.trim(td.innerText))*10)/10 + } + }) + var totalTds = elem.next().find('.layui-table-total td[data-field="'+item.field+'"]') + totalTds.each(function(i, td){ + $(td).find(".layui-table-cell").html(tdAllnum || "0") + }) + } + }) + } + + //更新序号列 + tableFilter.upNumbers = function(elem){ + //当前第几页 + var cur = elem.next().find('.layui-laypage-curr').text(); + cur = Number(cur || '1') + var limit = elem.next().find('.layui-laypage-limits select').val(); + limit = Number(limit) + + var trs = elem.next().find('.layui-table-main tr'); + var n = cur==1 ? 0 : limit*(cur-1); + + trs.each(function(i, tr){ + if(!$(tr).hasClass('layui-hide')){ + n += 1; + $(tr).find('.laytable-cell-numbers').html(n) + } + }) + + if(elem.next().find('.layui-table-fixed').length >= 1){ + var trs_f = elem.next().find('.layui-table-fixed .layui-table-body tr'); + var n_f = cur==1 ? 0 : limit*(cur-1); + + trs_f.each(function(i, tr_f){ + if(!$(tr_f).hasClass('layui-hide')){ + n_f += 1; + $(tr_f).find('.laytable-cell-numbers').html(n_f) + } + }) + } + } + + //表格取消选中 + tableFilter.uncheck = function(elem){ + var elemId = elem.attr("id"); + var tableName = elem.attr("lay-filter"); + + var trs = elem.next().find('.layui-table-fixed-l tr'); + trs.each(function(i, tr){ + var c = $(tr).find("[name='layTableCheckbox']"); + if(c.prop("checked")){ + $(tr).find('.layui-form-checked i').click() + } + }) + } + + //FIX 表格全选选中隐藏项 + tableFilter.fixAll = function(elem){ + var elemId = elem.attr("id"); + var tableName = elem.attr("lay-filter"); + var trs = elem.next().find('.layui-table-main tr'); + + table.on('checkbox('+tableName+')', function(obj){ + if(obj.type=="all"){ + var data = table.cache[elemId]; + trs.each(function(i, tr){ + if($(tr).hasClass('layui-hide')){ + data[i].LAY_CHECKED = false; + } + }) + + } + }) + + } + + //隐藏选择器 + tableFilter.hide = function(){ + $('.layui-table-filter-view').remove(); + } + + //工具 + tableFilter.tool = { + //数组&对象数组去重 + 'uniqueObjArray' : function(arr, type){ + var newArr = []; + var tArr = []; + if(arr.length == 0){ + return arr; + }else{ + if(type){ + for(var i=0;i= beginDate && curDate <= endDate) { + return true; + } + return false; + } + } + + //输出接口 + exports(MOD_NAME, tableFilter); +}).link(layui.cache.base+"tablefilter/tableFilter.css?v="+(new Date).getTime()); \ No newline at end of file diff --git a/public/js/lay-module/textool/textool.js b/public/js/lay-module/textool/textool.js new file mode 100644 index 0000000..69e59c5 --- /dev/null +++ b/public/js/lay-module/textool/textool.js @@ -0,0 +1,308 @@ +/** + * Layui 文本输入工具组件 + * + * @author iTanken + * @since 20200310 + */ +layui.define(['jquery'], function (exports) { + var $ = layui.$, baseClassName = 'layext-text-tool', extClassName = 'layext-textool-pane', + style, alignRight = true, alignClass, nodes = [], tools = { + "hide": null, "count": null, "copy": null, "reset": null, "clear": null, + hideIndex: -1, countIndex: -1, copyIndex: -1, resetIndex: -1, clearIndex: -1, + hideName: "hide", countName: "count", copyName: "copy", resetName: "reset", clearName: "clear", + hideClass: "layext-textool-minmax", countClass: "layext-textool-count", maxClass: "layext-textool-max", + copyClass: "layext-textool-copy", resetClass: "layext-textool-reset", clearClass: "layext-textool-clear", + copyTextId: baseClassName + '-copy-text', lengthClass: 'layext-textool-length', + lengthOverClass: baseClassName + '-length-over', laytips: 'layext-textool-laytips' + }, defaultOptions = { + // 根据元素 id 值单独渲染,为空默认根据 class='layext-text-tool' 批量渲染 + eleId: null, + // 批量设置输入框最大长度,可结合 eleId 单独设置最大长度 + maxlength: -1, + // 初始化回调,无参 + initEnd: $.noop, + // 显示回调,参数为当前输入框和工具条面板的 jQuery 对象 + showEnd: $.noop, + // 隐藏回调,参数为当前输入框和工具条面板的 jQuery 对象 + hideEnd: $.noop, + // 初始化展开,默认展开,否则收起 + initShow: true, + // 工具条是否位于输入框内部,默认位于外部 + inner: false, + // 工具条对齐方向,默认右对齐,可选左对齐 'left' + align: 'right', + // 启用指定工具模块,默认依次为字数统计、复制内容、重置内容、清空内容,按数组顺序显示 + tools: ['count', 'copy', 'reset', 'clear'], + // 工具按钮提示类型,默认为 'title' 属性,可选 'laytips',使用 layer 组件的吸附提示, 其他值不显示提示 + tipType: 'title', + // 吸附提示背景颜色 + tipColor: '#01AAED', + // 工具条字体颜色 + color: '#666666', + // 工具条背景颜色 + bgColor: '#FFFFFF', + // 工具条边框颜色 + borderColor: '#E6E6E6', + // 工具条附加样式类名 + className: '', + // z-index + zIndex: 19891014 + }, Class = function (custom) { + var _this = this; + _this.tipsAttr = null; + _this.selector = null; + _this.init(_this, custom || {}); + }; + + /** 初始化 */ + Class.prototype.init = function (_this, custom) { + _this.options = $.extend({}, defaultOptions, custom); + _this.selector = $.trim(_this.options.eleId) === '' ? '.' + baseClassName : '#' + _this.options.eleId; + + _this.initStyle(_this); + _this.initPrototype(); + + $(_this.selector).each(function (i, n) { + var $this = $(this), maxlength = _this.options.maxlength; + !isNaN(maxlength) && maxlength > -1 && $this.attr('maxlength', maxlength); + _this.addTextool(_this, $this); + }); + _this.initTips(_this); + typeof _this.options.initEnd === 'function' && _this.options.initEnd(); + }; + + /** 初始化扩展样式 */ + Class.prototype.initStyle = function (_this) { + _this.options.zIndex = isNaN(_this.options.zIndex) ? 0 : _this.options.zIndex || 0; + + style = [''].join(''); + + $('head link:last')[0] && $('head link:last').after(style) || $('head').append(style); + }; + + /** 初始化默认方法,处理 JS 兼容问题 */ + Class.prototype.initPrototype = function () { + // 获取数组元素下标 + !Array.prototype.indexOf && (Array.prototype.indexOf = function (array, value) { + array = array || []; + for (var i = array.length; i--;) { + if (array[i] == value) { + return i; + } + } + + return -1; + }); + }; + + /** 添加文本工具 */ + Class.prototype.addTextool = function (_this, $target) { + var $extPane = $target.next('.' + extClassName); + // 若已存在,则移除元素,支持重复渲染 + $extPane && $extPane.length && $extPane.remove(); + // 添加元素 + $target.after(_this.getToolsNode(_this, $target)); + $extPane = $target.next('.' + extClassName); + _this.setEvent(_this, $target, $extPane); + + $extPane.fadeIn(200, function() { + !_this.options.initShow && $extPane.find('.' + tools.hideClass).trigger('click'); + }); + }; + + /** 复制文本 */ + Class.prototype.copyText = function (_this, $target) { + if (!$target) { + return false; + } + !$('#' + tools.copyTextId).length && $('body').append(''); + var $copy = $('#' + tools.copyTextId), value = $target.val(); + $copy.val(value === '' ? ' ' : value).select(); + document.execCommand('copy'); + _this.showTip(_this, $target, '已复制!'); + }; + + /** 设置内容长度 */ + Class.prototype.setValLength = function (_this, $target) { + var $length = $target.next('.' + extClassName).find('.' + tools.countClass); + $length.text($target.val().length); + if ($target.val().length > $target.attr('maxlength')) { + $length.addClass(tools.lengthOverClass); + } else if ($length.hasClass(tools.lengthOverClass)) { + $length.removeClass(tools.lengthOverClass); + } + } + + /** 设置工具条事件 */ + Class.prototype.setEvent = function (_this, $target, $extPane) { + _this.setValLength(_this, $target); + var initValue = $target.val(); + // 文本工具条按钮点击事件 + $extPane.on('click', 'a', function (e) { + var $this = $(this), $icon = $this.children('i.layui-icon'); + if ($this.hasClass(tools.hideClass)) { + // 收起展开按钮事件 + $this.nextAll().toggle('fast'); + $this.prevAll().toggle('fast'); + if ($icon.hasClass('layui-icon-more')) { + $icon.removeClass('layui-icon-more').addClass('layui-icon-more-vertical'); + $this.attr(_this.tipsAttr, '展开'); + typeof _this.options.hideEnd === 'function' && _this.options.hideEnd($target, $extPane); + } else { + $icon.removeClass('layui-icon-more-vertical').addClass('layui-icon-more'); + $this.attr(_this.tipsAttr, '收起'); + typeof _this.options.showEnd === 'function' && _this.options.showEnd($target, $extPane); + } + _this.tipsAttr === tools.laytips && _this.showTip(_this, $this, $this.attr(_this.tipsAttr)); + } + if ($this.hasClass(tools.copyClass)) { + // 复制按钮事件 + _this.copyText(_this, $target); + } + if ($this.hasClass(tools.resetClass)) { + // 重置按钮事件 + $target.val(initValue); + _this.setValLength(_this, $target); + } + if ($this.hasClass(tools.clearClass)) { + // 清空按钮事件 + $target.val(''); + _this.setValLength(_this, $target); + } + + layui.stope(e); + return false; + }); + // 字数统计事件 + $target.on('keyup input', function (e) { + _this.setValLength(_this, $target); + + layui.stope(e); + return false; + }); + }; + + /** 获取工具条节点 */ + Class.prototype.getToolsNode = function (_this, $target) { + if (!$target) return false; + // 总是显示收起展开按钮 + tools.hide = [''].join(''); + // 至少显示一个工具模块 + _this.options.tools = _this.options.tools || [tools.countName]; + // 字数统计 + tools.countIndex = _this.options.tools.indexOf(tools.countName); + if (tools.countIndex > -1) { + var maxlength = $target.attr('maxlength') || -1; + tools.count = ['0', + (maxlength < 0 ? '' : ['/', maxlength, ''].join('')), ''].join(''); + } + // 复制内容 + tools.copyIndex = _this.options.tools.indexOf(tools.copyName); + if (tools.copyIndex > -1) { + tools.copy = [''].join(''); + } + // 重置内容 + tools.resetIndex = _this.options.tools.indexOf(tools.resetName); + if (tools.resetIndex > -1) { + tools.reset = [''].join(''); + } + // 清空内容 + tools.clearIndex = _this.options.tools.indexOf(tools.clearName); + if (tools.clearIndex > -1) { + tools.clear = [''].join(''); + } + + if (_this.options.align === 'left') { + // 居左对齐 + alignRight = false; + alignClass = extClassName + '-l'; + } else { + // 居右对其 + alignRight = true; + alignClass = extClassName + '-r'; + } + // 处理工具条节点 + nodes = ['']; + !alignRight && nodes.push(tools.hide); + for (var i = 0; i < _this.options.tools.length; i++) { + nodes.push(tools[_this.options.tools[i]] || ''); + } + alignRight && nodes.push(tools.hide); + nodes.push(''); + + return nodes.join(''); + }; + + /** 获取提示信息属性 */ + Class.prototype.getTips = function (_this, msg) { + switch (_this.options.tipType) { + case 'title': + _this.tipsAttr = 'title'; + break; + case 'laytips': + _this.tipsAttr = tools.laytips; + break; + default: + return ''; + } + + return [' ', _this.tipsAttr, '=', msg, ' '].join(''); + }; + + /** 初始化吸附提示 */ + Class.prototype.initTips = function (_this) { + $('[' + tools.laytips + ']').each(function (i, n) { + var $target = $(n); + if ($.trim($target.attr(_this.tipsAttr)) !== '') { + $target.hover(function () { + _this.showTip(_this, $target, $target.attr(_this.tipsAttr)); + }, _this.hideTip); + } + }); + }; + + /** 显示吸附提示 */ + Class.prototype.showTip = function (_this, $target, msg) { + _this.hideTip(); + layui.layer.tips(msg, $target, { tips: [1, _this.options.tipColor || '#01AAED'], time: 2e3, anim: 5, zIndex: (_this.options.zIndex || 0) + 2 }); + }; + + /** 隐藏吸附提示 */ + Class.prototype.hideTip = function () { + layui.layer.closeAll('tips'); + }; + + exports('textool', { + /** 初始化入口方法 */ + init: function (custom) { + return new Class(custom); + } + }); + +}); diff --git a/public/js/lay-module/textool/textool.min.js b/public/js/lay-module/textool/textool.min.js new file mode 100644 index 0000000..66af883 --- /dev/null +++ b/public/js/lay-module/textool/textool.min.js @@ -0,0 +1 @@ +layui.define(["jquery"],function(t){var e,o,n=layui.$,i="layext-text-tool",s="layext-textool-pane",l=!0,a=[],r={hide:null,count:null,copy:null,reset:null,clear:null,hideIndex:-1,countIndex:-1,copyIndex:-1,resetIndex:-1,clearIndex:-1,hideName:"hide",countName:"count",copyName:"copy",resetName:"reset",clearName:"clear",hideClass:"layext-textool-minmax",countClass:"layext-textool-count",maxClass:"layext-textool-max",copyClass:"layext-textool-copy",resetClass:"layext-textool-reset",clearClass:"layext-textool-clear",copyTextId:i+"-copy-text",lengthClass:"layext-textool-length",lengthOverClass:i+"-length-over",laytips:"layext-textool-laytips"},p={eleId:null,maxlength:-1,initEnd:n.noop,showEnd:n.noop,hideEnd:n.noop,initShow:!0,inner:!1,align:"right",tools:["count","copy","reset","clear"],tipType:"title",tipColor:"#01AAED",color:"#666666",bgColor:"#FFFFFF",borderColor:"#E6E6E6",className:"",zIndex:19891014},c=function(t){var e=this;e.tipsAttr=null,e.selector=null,e.init(e,t||{})};c.prototype.init=function(t,e){t.options=n.extend({},p,e),t.selector=""===n.trim(t.options.eleId)?"."+i:"#"+t.options.eleId,t.initStyle(t),t.initPrototype(),n(t.selector).each(function(e,o){var i=n(this),s=t.options.maxlength;!isNaN(s)&&s>-1&&i.attr("maxlength",s),t.addTextool(t,i)}),t.initTips(t),"function"==typeof t.options.initEnd&&t.options.initEnd()},c.prototype.initStyle=function(t){t.options.zIndex=isNaN(t.options.zIndex)?0:t.options.zIndex||0,e=['"].join(""),n("head link:last")[0]&&n("head link:last").after(e)||n("head").append(e)},c.prototype.initPrototype=function(){!Array.prototype.indexOf&&(Array.prototype.indexOf=function(t,e){t=t||[];for(var o=t.length;o--;)if(t[o]==e)return o;return-1})},c.prototype.addTextool=function(t,e){var o=e.next("."+s);o&&o.length&&o.remove(),e.after(t.getToolsNode(t,e)),o=e.next("."+s),t.setEvent(t,e,o),o.fadeIn(200,function(){!t.options.initShow&&o.find("."+r.hideClass).trigger("click")})},c.prototype.copyText=function(t,e){if(!e)return!1;!n("#"+r.copyTextId).length&&n("body").append("');var o=n("#"+r.copyTextId),i=e.val();o.val(""===i?" ":i).select(),document.execCommand("copy"),t.showTip(t,e,"已复制!")},c.prototype.setValLength=function(t,e){var o=e.next("."+s).find("."+r.countClass);o.text(e.val().length),e.val().length>e.attr("maxlength")?o.addClass(r.lengthOverClass):o.hasClass(r.lengthOverClass)&&o.removeClass(r.lengthOverClass)},c.prototype.setEvent=function(t,e,o){t.setValLength(t,e);var i=e.val();o.on("click","a",function(s){var l=n(this),a=l.children("i.layui-icon");return l.hasClass(r.hideClass)&&(l.nextAll().toggle("fast"),l.prevAll().toggle("fast"),a.hasClass("layui-icon-more")?(a.removeClass("layui-icon-more").addClass("layui-icon-more-vertical"),l.attr(t.tipsAttr,"展开"),"function"==typeof t.options.hideEnd&&t.options.hideEnd(e,o)):(a.removeClass("layui-icon-more-vertical").addClass("layui-icon-more"),l.attr(t.tipsAttr,"收起"),"function"==typeof t.options.showEnd&&t.options.showEnd(e,o)),t.tipsAttr===r.laytips&&t.showTip(t,l,l.attr(t.tipsAttr))),l.hasClass(r.copyClass)&&t.copyText(t,e),l.hasClass(r.resetClass)&&(e.val(i),t.setValLength(t,e)),l.hasClass(r.clearClass)&&(e.val(""),t.setValLength(t,e)),layui.stope(s),!1}),e.on("keyup input",function(o){return t.setValLength(t,e),layui.stope(o),!1})},c.prototype.getToolsNode=function(t,e){if(!e)return!1;if(r.hide=[''].join(""),t.options.tools=t.options.tools||[r.countName],r.countIndex=t.options.tools.indexOf(r.countName),r.countIndex>-1){var i=e.attr("maxlength")||-1;r.count=['0",i<0?"":['/",i,""].join(""),""].join("")}r.copyIndex=t.options.tools.indexOf(r.copyName),r.copyIndex>-1&&(r.copy=[''].join("")),r.resetIndex=t.options.tools.indexOf(r.resetName),r.resetIndex>-1&&(r.reset=[''].join("")),r.clearIndex=t.options.tools.indexOf(r.clearName),r.clearIndex>-1&&(r.clear=[''].join("")),"left"===t.options.align?(l=!1,o=s+"-l"):(l=!0,o=s+"-r"),a=[''],!l&&a.push(r.hide);for(var p=0;p"),a.join("")},c.prototype.getTips=function(t,e){switch(t.options.tipType){case"title":t.tipsAttr="title";break;case"laytips":t.tipsAttr=r.laytips;break;default:return""}return[" ",t.tipsAttr,"=",e," "].join("")},c.prototype.initTips=function(t){n("["+r.laytips+"]").each(function(e,o){var i=n(o);""!==n.trim(i.attr(t.tipsAttr))&&i.hover(function(){t.showTip(t,i,i.attr(t.tipsAttr))},t.hideTip)})},c.prototype.showTip=function(t,e,o){t.hideTip(),layui.layer.tips(o,e,{tips:[1,t.options.tipColor||"#01AAED"],time:2e3,anim:5,zIndex:(t.options.zIndex||0)+2})},c.prototype.hideTip=function(){layui.layer.closeAll("tips")},t("textool",{init:function(t){return new c(t)}})}); \ No newline at end of file diff --git a/public/js/lay-module/transferTable/transferTable.js b/public/js/lay-module/transferTable/transferTable.js new file mode 100644 index 0000000..bd4cfa7 --- /dev/null +++ b/public/js/lay-module/transferTable/transferTable.js @@ -0,0 +1,271 @@ +layui.define('table', function (exports) { + var $ = layui.$ + , table = layui.table + //字符常量 + , MOD_NAME = 'transferTable', ELEM = '.layui-transferTable' + + //外部接口 + , transferTable = { + index: layui.transferTable ? (layui.transferTable.index + 10000) : 0 + //设置全局项 + , set: function (options) { + var that = this; + that.config = $.extend({}, that.config, options); + return that; + } + , get: function (id) { + if (this.config && this.config[id]) { + return this.config[id].data + } else { + return [] + } + } + , reload: function (id, options) { + table.reload(id, options) + } + + } + //操作当前实例 + , transfer = function () { + var that = this + , options = that.config + , id = options.id || options.index; + return { + reload: function (options) { + that.reload.call(that, options); + } + , config: options + } + } + //构造器 + , Class = function (options) { + var that = this; + that.index = ++transferTable.index; + that.left_table_id = "left_table_" + that.index; + that.rigth_table_id = "rigth_table_" + that.index; + //表格重载ID 如果配置里面没有 自动分配一个ID + that.reload_left = that.left_table_id; + that.reload_right = that.rigth_table_id; + if (options.id && options.id.length) { + that.reload_left = options.id[0] + that.reload_right = options.id[1] || options.id[0] + '_2' + } else { + //没有配置ID 默认给表格中添加ID 用于重载 + options.id = [that.reload_left, that.reload_right] + } + //全局设置 + if (options.id_name) { + idName = options.id_name; + if (options.where && options.where[idName]) { + var d = []; + var tableid = that.reload_right; + d[tableid] = {data: options.where[idName]} + transferTable.set(d) + } + } + + that.config = $.extend({}, that.config, transferTable.config, options); + that.render(); + }; + + + Class.prototype.render = function () { + //ID里面放表格样式 + this.tableHtml() + // 配置表格参数 + this.loadTable() + // 移动数据 + this.moveData() + // 监听双击事件 并移动数据 + this.doubleData() + } + + Class.prototype.tableHtml = function () { + var that = this, + options = that.config, + left_table = '
                ', + rigth_table = '
                ', + html = '
                ' + + '
                ' + left_table + '
                ' + + '
                ' + + '
                ' + + '
                ' + + '' + + '
                ' + + '
                ' + + '
                ' + rigth_table + '
                ' + + '
                '; + $(options.elem).append(html) + } + Class.prototype.loadTable = function () { + //传递的参数 table.render 表格 + var that = this, + options = that.config, + lt = [that.left_table_id, that.rigth_table_id] + $.each(lt, function (k, id) { + var config = {elem: '#' + id} + $.each(options, function (key, val) { + + if (val[k] || val[k] === false) { + var value = val[k] + } else { + var value = val[0] + } + if (typeof val == 'function') { + config[key] = options[key] + } else if (key !== 'elem' && key !== 'id_name' && key !== 'where') { + config[key] = value + } + }) + if (options.where) { + config.where = options.where + } + table.render(config); + }) + //计算表格高度 居中中间按钮 + var th = $('#' + lt[0]).next('div').height() / 2; + var bh = $('.btns').height() / 2; + $('.btns').css('margin-top', th - bh) + //监听表格选中 + table.on('checkbox(' + that.left_table_id + ')', function (obj) { + //左边表格点击触发 + var checkStatus = table.checkStatus(that.reload_left) + , data = checkStatus.data; + if (data.length > 0) { + $('.' + that.left_table_id).removeClass('layui-btn-disabled') + } else { + $('.' + that.left_table_id).addClass('layui-btn-disabled') + } + + }); + table.on('checkbox(' + that.rigth_table_id + ')', function (obj) { + //右边表格点击触发 + var checkStatus = table.checkStatus(that.reload_right) + , data = checkStatus.data; + if (data.length > 0) { + $('.' + that.rigth_table_id).removeClass('layui-btn-disabled') + } else { + $('.' + that.rigth_table_id).addClass('layui-btn-disabled') + } + }); + + } + Class.prototype.moveData = function () { + //绑定点击事件 + var that = this, + idName = that.config.id_name; + $('.' + that.left_table_id).on('click', function () { + if (!$(this).hasClass('layui-btn-disabled')) { + var checkStatus = table.checkStatus(that.reload_left) + , data = checkStatus.data; + that.leftReload(that, data) + } + $(this).addClass('layui-btn-disabled') + }) + $('.' + that.rigth_table_id).on('click', function () { + if (!$(this).hasClass('layui-btn-disabled')) { + var checkStatus = table.checkStatus(that.reload_right) + , data = checkStatus.data; + that.rigthReload(that, data) + } + $(this).addClass('layui-btn-disabled') + }) + + } + Class.prototype.doubleData = function () { + var that = this; + + table.on('rowDouble(' + that.left_table_id + ')', function (obj) { + //左边移动到右边 + var data = []; + data.push(obj.data) + that.leftReload(that, data) + }); + table.on('rowDouble(' + that.rigth_table_id + ')', function (obj) { + //右边移动到左边 + var data = []; + data.push(obj.data) + that.rigthReload(that, data) + }); + } + + //重载表格 + Class.prototype.leftReload = function (that, data) { + + if (data && data.length > 0) { + if (that.config.where && that.config.where[idName]) { + var id_data = that.config.where[idName] + ""; + id_data = id_data.split(',') + } else { + var id_data = []; + } + + $.each(data, function (k, v) { + id_data.push('' + v[idName]) + }) + + //全局存储 + id_data = $.unique(id_data); + var ids_str = id_data.join(',') + var d = []; + var tableid = that.reload_right; + d[tableid] = {data: ids_str} + transferTable.set(d) + //配置存储ID + if (!that.config.where) { + that.config.where = {} + } + this.config.where[idName] = ids_str + //重载表格 + var reload_config = { + page: {curr: 1}, + where: {} + } + if (that.config.done) { + delete reload_config.page; + } + reload_config.where[idName] = ids_str + table.reload(that.reload_left, reload_config) + table.reload(that.reload_right, reload_config) + } + } + Class.prototype.rigthReload = function (that, data) { + if (data && data.length) { + var sel_data = []; + $.each(data, function (k, v) { + sel_data.push('' + v[idName]) + }) + var id_data = that.config.where[idName] + ""; + id_data = id_data.split(','); + var moveD = []; //移除后保留的ID集合 + $.each(id_data, function (k, v) { + if ($.inArray(v, sel_data) == -1) moveD.push(v) + }) + id_data = $.unique(moveD); + var ids_str = id_data.join(',') + var d = []; + var tableid = that.reload_right; + d[tableid] = {data: ids_str} + transferTable.set(d) + //配置存储ID + this.config.where[idName] = ids_str + //重载表格 + var reload_config = { + page: {curr: 1}, + where: {} + } + if (that.config.done) { + delete reload_config.page; + } + reload_config.where[idName] = ids_str + table.reload(that.reload_left, reload_config) + table.reload(that.reload_right, reload_config) + } + } + + transferTable.render = function (options) { + var ins = new Class(options); + return transfer.call(ins); + }; + exports('transferTable', transferTable); +}); diff --git a/public/js/lay-module/treetable-lay/treetable.css b/public/js/lay-module/treetable-lay/treetable.css new file mode 100644 index 0000000..584b2f8 --- /dev/null +++ b/public/js/lay-module/treetable-lay/treetable.css @@ -0,0 +1,18 @@ +.treeTable-empty { + width: 20px; + display: inline-block; +} + +.treeTable-icon { + cursor: pointer; +} + +.treeTable-icon .layui-icon-triangle-d:before { + content: "\e623"; +} + +.treeTable-icon.open .layui-icon-triangle-d:before { + content: "\e625"; + background-color: transparent; +} + diff --git a/public/js/lay-module/treetable-lay/treetable.js b/public/js/lay-module/treetable-lay/treetable.js new file mode 100644 index 0000000..70f987e --- /dev/null +++ b/public/js/lay-module/treetable-lay/treetable.js @@ -0,0 +1,206 @@ +layui.define(['layer', 'table'], function (exports) { + var $ = layui.jquery; + var layer = layui.layer; + var table = layui.table; + + var treetable = { + // 渲染树形表格 + render: function (param) { + // 检查参数 + if (!treetable.checkParam(param)) { + return; + } + // 获取数据 + if (param.data) { + treetable.init(param, param.data); + } else { + $.getJSON(param.url, param.where, function (res) { + treetable.init(param, res.data); + }); + } + }, + // 渲染表格 + init: function (param, data) { + var mData = []; + var doneCallback = param.done; + var tNodes = data; + // 补上id和pid字段 + for (var i = 0; i < tNodes.length; i++) { + var tt = tNodes[i]; + if (!tt.id) { + if (!param.treeIdName) { + layer.msg('参数treeIdName不能为空', {icon: 5}); + return; + } + tt.id = tt[param.treeIdName]; + } + if (!tt.pid) { + if (!param.treePidName) { + layer.msg('参数treePidName不能为空', {icon: 5}); + return; + } + tt.pid = tt[param.treePidName]; + } + } + + // 对数据进行排序 + var sort = function (s_pid, data) { + for (var i = 0; i < data.length; i++) { + if (data[i].pid == s_pid) { + var len = mData.length; + if (len > 0 && mData[len - 1].id == s_pid) { + mData[len - 1].isParent = true; + } + mData.push(data[i]); + sort(data[i].id, data); + } + } + }; + sort(param.treeSpid, tNodes); + + // 重写参数 + param.url = undefined; + param.data = mData; + param.page = { + count: param.data.length, + limit: param.data.length + }; + param.cols[0][param.treeColIndex].templet = function (d) { + var mId = d.id; + var mPid = d.pid; + var isDir = d.isParent; + var emptyNum = treetable.getEmptyNum(mPid, mData); + var iconHtml = ''; + for (var i = 0; i < emptyNum; i++) { + iconHtml += ''; + } + if (isDir) { + iconHtml += ' '; + } else { + iconHtml += ''; + } + iconHtml += '  '; + var ttype = isDir ? 'dir' : 'file'; + var vg = ''; + return vg + iconHtml + d[param.cols[0][param.treeColIndex].field] + '' + }; + + param.done = function (res, curr, count) { + $(param.elem).next().addClass('treeTable'); + $('.treeTable .layui-table-page').css('display', 'none'); + $(param.elem).next().attr('treeLinkage', param.treeLinkage); + // 绑定事件换成对body绑定 + /*$('.treeTable .treeTable-icon').click(function () { + treetable.toggleRows($(this), param.treeLinkage); + });*/ + if (param.treeDefaultClose) { + treetable.foldAll(param.elem); + } + if (doneCallback) { + doneCallback(res, curr, count); + } + }; + + // 渲染表格 + table.render(param); + }, + // 计算缩进的数量 + getEmptyNum: function (pid, data) { + var num = 0; + if (!pid) { + return num; + } + var tPid; + for (var i = 0; i < data.length; i++) { + if (pid == data[i].id) { + num += 1; + tPid = data[i].pid; + break; + } + } + return num + treetable.getEmptyNum(tPid, data); + }, + // 展开/折叠行 + toggleRows: function ($dom, linkage) { + var type = $dom.attr('lay-ttype'); + if ('file' == type) { + return; + } + var mId = $dom.attr('lay-tid'); + var isOpen = $dom.hasClass('open'); + if (isOpen) { + $dom.removeClass('open'); + } else { + $dom.addClass('open'); + } + $dom.closest('tbody').find('tr').each(function () { + var $ti = $(this).find('.treeTable-icon'); + var pid = $ti.attr('lay-tpid'); + var ttype = $ti.attr('lay-ttype'); + var tOpen = $ti.hasClass('open'); + if (mId == pid) { + if (isOpen) { + $(this).hide(); + if ('dir' == ttype && tOpen == isOpen) { + $ti.trigger('click'); + } + } else { + $(this).show(); + if (linkage && 'dir' == ttype && tOpen == isOpen) { + $ti.trigger('click'); + } + } + } + }); + }, + // 检查参数 + checkParam: function (param) { + if (!param.treeSpid && param.treeSpid != 0) { + layer.msg('参数treeSpid不能为空', {icon: 5}); + return false; + } + + if (!param.treeColIndex && param.treeColIndex != 0) { + layer.msg('参数treeColIndex不能为空', {icon: 5}); + return false; + } + return true; + }, + // 展开所有 + expandAll: function (dom) { + $(dom).next('.treeTable').find('.layui-table-body tbody tr').each(function () { + var $ti = $(this).find('.treeTable-icon'); + var ttype = $ti.attr('lay-ttype'); + var tOpen = $ti.hasClass('open'); + if ('dir' == ttype && !tOpen) { + $ti.trigger('click'); + } + }); + }, + // 折叠所有 + foldAll: function (dom) { + $(dom).next('.treeTable').find('.layui-table-body tbody tr').each(function () { + var $ti = $(this).find('.treeTable-icon'); + var ttype = $ti.attr('lay-ttype'); + var tOpen = $ti.hasClass('open'); + if ('dir' == ttype && tOpen) { + $ti.trigger('click'); + } + }); + } + }; + + layui.link(layui.cache.base + "treetable-lay/treetable.css?v="+(new Date).getTime()); + + // 给图标列绑定事件 + $('body').on('click', '.treeTable .treeTable-icon', function () { + var treeLinkage = $(this).parents('.treeTable').attr('treeLinkage'); + if ('true' == treeLinkage) { + treetable.toggleRows($(this), true); + } else { + treetable.toggleRows($(this), false); + } + }); + + exports('treetable', treetable); +}); diff --git a/public/js/lay-module/wangEditor/fonts/w-e-icon.woff b/public/js/lay-module/wangEditor/fonts/w-e-icon.woff new file mode 100644 index 0000000..6dc5b5a Binary files /dev/null and b/public/js/lay-module/wangEditor/fonts/w-e-icon.woff differ diff --git a/public/js/lay-module/wangEditor/wangEditor.css b/public/js/lay-module/wangEditor/wangEditor.css new file mode 100644 index 0000000..5502c55 --- /dev/null +++ b/public/js/lay-module/wangEditor/wangEditor.css @@ -0,0 +1,411 @@ +.w-e-toolbar, +.w-e-text-container, +.w-e-menu-panel { + padding: 0; + margin: 0; + box-sizing: border-box; +} +.w-e-toolbar *, +.w-e-text-container *, +.w-e-menu-panel * { + padding: 0; + margin: 0; + box-sizing: border-box; +} +.w-e-clear-fix:after { + content: ""; + display: table; + clear: both; +} + +.w-e-toolbar .w-e-droplist { + position: absolute; + left: 0; + top: 0; + background-color: #fff; + border: 1px solid #f1f1f1; + border-right-color: #ccc; + border-bottom-color: #ccc; +} +.w-e-toolbar .w-e-droplist .w-e-dp-title { + text-align: center; + color: #999; + line-height: 2; + border-bottom: 1px solid #f1f1f1; + font-size: 13px; +} +.w-e-toolbar .w-e-droplist ul.w-e-list { + list-style: none; + line-height: 1; +} +.w-e-toolbar .w-e-droplist ul.w-e-list li.w-e-item { + color: #333; + padding: 5px 0; +} +.w-e-toolbar .w-e-droplist ul.w-e-list li.w-e-item:hover { + background-color: #f1f1f1; +} +.w-e-toolbar .w-e-droplist ul.w-e-block { + list-style: none; + text-align: left; + padding: 5px; +} +.w-e-toolbar .w-e-droplist ul.w-e-block li.w-e-item { + display: inline-block; + *display: inline; + *zoom: 1; + padding: 3px 5px; +} +.w-e-toolbar .w-e-droplist ul.w-e-block li.w-e-item:hover { + background-color: #f1f1f1; +} + +@font-face { + font-family: 'w-e-icon'; + src: url(data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAABhQAAsAAAAAGAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABCAAAAGAAAABgDxIPBGNtYXAAAAFoAAABBAAAAQQrSf4BZ2FzcAAAAmwAAAAIAAAACAAAABBnbHlmAAACdAAAEvAAABLwfpUWUWhlYWQAABVkAAAANgAAADYQp00kaGhlYQAAFZwAAAAkAAAAJAfEA+FobXR4AAAVwAAAAIQAAACEeAcD7GxvY2EAABZEAAAARAAAAERBSEX+bWF4cAAAFogAAAAgAAAAIAAsALZuYW1lAAAWqAAAAYYAAAGGmUoJ+3Bvc3QAABgwAAAAIAAAACAAAwAAAAMD3gGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA8fwDwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEAOgAAAA2ACAABAAWAAEAIOkG6Q3pEulH6Wbpd+m56bvpxunL6d/qDepc6l/qZepo6nHqefAN8BTxIPHc8fz//f//AAAAAAAg6QbpDekS6UfpZel36bnpu+nG6cvp3+oN6lzqX+pi6mjqcep38A3wFPEg8dzx/P/9//8AAf/jFv4W+Bb0FsAWoxaTFlIWURZHFkMWMBYDFbUVsxWxFa8VpxWiEA8QCQ7+DkMOJAADAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAACAAD/wAQAA8AABAATAAABNwEnAQMuAScTNwEjAQMlATUBBwGAgAHAQP5Anxc7MmOAAYDA/oDAAoABgP6ATgFAQAHAQP5A/p0yOxcBEU4BgP6A/YDAAYDA/oCAAAQAAAAABAADgAAQACEALQA0AAABOAExETgBMSE4ATEROAExITUhIgYVERQWMyEyNjURNCYjBxQGIyImNTQ2MzIWEyE1EwEzNwPA/IADgPyAGiYmGgOAGiYmGoA4KCg4OCgoOED9AOABAEDgA0D9AAMAQCYa/QAaJiYaAwAaJuAoODgoKDg4/biAAYD+wMAAAAIAAABABAADQAA4ADwAAAEmJy4BJyYjIgcOAQcGBwYHDgEHBhUUFx4BFxYXFhceARcWMzI3PgE3Njc2Nz4BNzY1NCcuAScmJwERDQED1TY4OXY8PT8/PTx2OTg2CwcICwMDAwMLCAcLNjg5djw9Pz89PHY5ODYLBwgLAwMDAwsIBwv9qwFA/sADIAgGBggCAgICCAYGCCkqKlktLi8vLi1ZKiopCAYGCAICAgIIBgYIKSoqWS0uLy8uLVkqKin94AGAwMAAAAAAAgDA/8ADQAPAABsAJwAAASIHDgEHBhUUFx4BFxYxMDc+ATc2NTQnLgEnJgMiJjU0NjMyFhUUBgIAQjs6VxkZMjJ4MjIyMngyMhkZVzo7QlBwcFBQcHADwBkZVzo7Qnh9fcxBQUFBzH19eEI7OlcZGf4AcFBQcHBQUHAAAAEAAAAABAADgAArAAABIgcOAQcGBycRISc+ATMyFx4BFxYVFAcOAQcGBxc2Nz4BNzY1NCcuAScmIwIANTIyXCkpI5YBgJA1i1BQRUZpHh4JCSIYGB5VKCAgLQwMKCiLXl1qA4AKCycbHCOW/oCQNDweHmlGRVArKClJICEaYCMrK2I2NjlqXV6LKCgAAQAAAAAEAAOAACoAABMUFx4BFxYXNyYnLgEnJjU0Nz4BNzYzMhYXByERByYnLgEnJiMiBw4BBwYADAwtICAoVR4YGCIJCR4eaUZFUFCLNZABgJYjKSlcMjI1al1eiygoAYA5NjZiKysjYBohIEkpKCtQRUZpHh48NJABgJYjHBsnCwooKIteXQAAAAACAAAAQAQBAwAAJgBNAAATMhceARcWFRQHDgEHBiMiJy4BJyY1JzQ3PgE3NjMVIgYHDgEHPgEhMhceARcWFRQHDgEHBiMiJy4BJyY1JzQ3PgE3NjMVIgYHDgEHPgHhLikpPRESEhE9KSkuLikpPRESASMjelJRXUB1LQkQBwgSAkkuKSk9ERISET0pKS4uKSk9ERIBIyN6UlFdQHUtCRAHCBICABIRPSkpLi4pKT0REhIRPSkpLiBdUVJ6IyOAMC4IEwoCARIRPSkpLi4pKT0REhIRPSkpLiBdUVJ6IyOAMC4IEwoCAQAABgBA/8AEAAPAAAMABwALABEAHQApAAAlIRUhESEVIREhFSEnESM1IzUTFTMVIzU3NSM1MxUVESM1MzUjNTM1IzUBgAKA/YACgP2AAoD9gMBAQECAwICAwMCAgICAgIACAIACAIDA/wDAQP3yMkCSPDJAku7+wEBAQEBAAAYAAP/ABAADwAADAAcACwAXACMALwAAASEVIREhFSERIRUhATQ2MzIWFRQGIyImETQ2MzIWFRQGIyImETQ2MzIWFRQGIyImAYACgP2AAoD9gAKA/YD+gEs1NUtLNTVLSzU1S0s1NUtLNTVLSzU1SwOAgP8AgP8AgANANUtLNTVLS/61NUtLNTVLS/61NUtLNTVLSwADAAAAAAQAA6AAAwANABQAADchFSElFSE1EyEVITUhJQkBIxEjEQAEAPwABAD8AIABAAEAAQD9YAEgASDggEBAwEBAAQCAgMABIP7g/wABAAAAAAACAB7/zAPiA7QAMwBkAAABIiYnJicmNDc2PwE+ATMyFhcWFxYUBwYPAQYiJyY0PwE2NCcuASMiBg8BBhQXFhQHDgEjAyImJyYnJjQ3Nj8BNjIXFhQPAQYUFx4BMzI2PwE2NCcmNDc2MhcWFxYUBwYPAQ4BIwG4ChMIIxISEhIjwCNZMTFZIyMSEhISI1gPLA8PD1gpKRQzHBwzFMApKQ8PCBMKuDFZIyMSEhISI1gPLA8PD1gpKRQzHBwzFMApKQ8PDysQIxISEhIjwCNZMQFECAckLS1eLS0kwCIlJSIkLS1eLS0kVxAQDysPWCl0KRQVFRTAKXQpDysQBwj+iCUiJC0tXi0tJFcQEA8rD1gpdCkUFRUUwCl0KQ8rEA8PJC0tXi0tJMAiJQAAAAAFAAD/wAQAA8AAGwA3AFMAXwBrAAAFMjc+ATc2NTQnLgEnJiMiBw4BBwYVFBceARcWEzIXHgEXFhUUBw4BBwYjIicuAScmNTQ3PgE3NhMyNz4BNzY3BgcOAQcGIyInLgEnJicWFx4BFxYnNDYzMhYVFAYjIiYlNDYzMhYVFAYjIiYCAGpdXosoKCgoi15dampdXosoKCgoi15dalZMTHEgISEgcUxMVlZMTHEgISEgcUxMVisrKlEmJiMFHBtWODc/Pzc4VhscBSMmJlEqK9UlGxslJRsbJQGAJRsbJSUbGyVAKCiLXl1qal1eiygoKCiLXl1qal1eiygoA6AhIHFMTFZWTExxICEhIHFMTFZWTExxICH+CQYGFRAQFEM6OlYYGRkYVjo6QxQQEBUGBvcoODgoKDg4KCg4OCgoODgAAAMAAP/ABAADwAAbADcAQwAAASIHDgEHBhUUFx4BFxYzMjc+ATc2NTQnLgEnJgMiJy4BJyY1NDc+ATc2MzIXHgEXFhUUBw4BBwYTBycHFwcXNxc3JzcCAGpdXosoKCgoi15dampdXosoKCgoi15dalZMTHEgISEgcUxMVlZMTHEgISEgcUxMSqCgYKCgYKCgYKCgA8AoKIteXWpqXV6LKCgoKIteXWpqXV6LKCj8YCEgcUxMVlZMTHEgISEgcUxMVlZMTHEgIQKgoKBgoKBgoKBgoKAAAQBl/8ADmwPAACkAAAEiJiMiBw4BBwYVFBYzLgE1NDY3MAcGAgcGBxUhEzM3IzceATMyNjcOAQMgRGhGcVNUbRobSUgGDWVKEBBLPDxZAT1sxizXNC1VJi5QGB09A7AQHh1hPj9BTTsLJjeZbwN9fv7Fj5AjGQIAgPYJDzdrCQcAAAAAAgAAAAAEAAOAAAkAFwAAJTMHJzMRIzcXIyURJyMRMxUhNTMRIwcRA4CAoKCAgKCggP8AQMCA/oCAwEDAwMACAMDAwP8AgP1AQEACwIABAAADAMAAAANAA4AAFgAfACgAAAE+ATU0Jy4BJyYjIREhMjc+ATc2NTQmATMyFhUUBisBEyMRMzIWFRQGAsQcIBQURi4vNf7AAYA1Ly5GFBRE/oRlKjw8KWafn58sPj4B2yJULzUvLkYUFPyAFBRGLi81RnQBRks1NUv+gAEASzU1SwAAAAACAMAAAANAA4AAHwAjAAABMxEUBw4BBwYjIicuAScmNREzERQWFx4BMzI2Nz4BNQEhFSECwIAZGVc6O0JCOzpXGRmAGxgcSSgoSRwYG/4AAoD9gAOA/mA8NDVOFhcXFk41NDwBoP5gHjgXGBsbGBc4Hv6ggAAAAAABAIAAAAOAA4AACwAAARUjATMVITUzASM1A4CA/sCA/kCAAUCAA4BA/QBAQAMAQAABAAAAAAQAA4AAPQAAARUjHgEVFAYHDgEjIiYnLgE1MxQWMzI2NTQmIyE1IS4BJy4BNTQ2Nz4BMzIWFx4BFSM0JiMiBhUUFjMyFhcEAOsVFjUwLHE+PnEsMDWAck5OcnJO/gABLAIEATA1NTAscT4+cSwwNYByTk5yck47bisBwEAdQSI1YiQhJCQhJGI1NExMNDRMQAEDASRiNTViJCEkJCEkYjU0TEw0NEwhHwAAAAcAAP/ABAADwAADAAcACwAPABMAGwAjAAATMxUjNzMVIyUzFSM3MxUjJTMVIwMTIRMzEyETAQMhAyMDIQMAgIDAwMABAICAwMDAAQCAgBAQ/QAQIBACgBD9QBADABAgEP2AEAHAQEBAQEBAQEBAAkD+QAHA/oABgPwAAYD+gAFA/sAAAAoAAAAABAADgAADAAcACwAPABMAFwAbAB8AIwAnAAATESERATUhFR0BITUBFSE1IxUhNREhFSElIRUhETUhFQEhFSEhNSEVAAQA/YABAP8AAQD/AED/AAEA/wACgAEA/wABAPyAAQD/AAKAAQADgPyAA4D9wMDAQMDAAgDAwMDA/wDAwMABAMDA/sDAwMAAAAUAAAAABAADgAADAAcACwAPABMAABMhFSEVIRUhESEVIREhFSERIRUhAAQA/AACgP2AAoD9gAQA/AAEAPwAA4CAQID/AIABQID/AIAAAAAABQAAAAAEAAOAAAMABwALAA8AEwAAEyEVIRchFSERIRUhAyEVIREhFSEABAD8AMACgP2AAoD9gMAEAPwABAD8AAOAgECA/wCAAUCA/wCAAAAFAAAAAAQAA4AAAwAHAAsADwATAAATIRUhBSEVIREhFSEBIRUhESEVIQAEAPwAAYACgP2AAoD9gP6ABAD8AAQA/AADgIBAgP8AgAFAgP8AgAAAAAABAD8APwLmAuYALAAAJRQPAQYjIi8BBwYjIi8BJjU0PwEnJjU0PwE2MzIfATc2MzIfARYVFA8BFxYVAuYQThAXFxCoqBAXFhBOEBCoqBAQThAWFxCoqBAXFxBOEBCoqBDDFhBOEBCoqBAQThAWFxCoqBAXFxBOEBCoqBAQThAXFxCoqBAXAAAABgAAAAADJQNuABQAKAA8AE0AVQCCAAABERQHBisBIicmNRE0NzY7ATIXFhUzERQHBisBIicmNRE0NzY7ATIXFhcRFAcGKwEiJyY1ETQ3NjsBMhcWExEhERQXFhcWMyEyNzY3NjUBIScmJyMGBwUVFAcGKwERFAcGIyEiJyY1ESMiJyY9ATQ3NjsBNzY3NjsBMhcWHwEzMhcWFQElBgUIJAgFBgYFCCQIBQaSBQUIJQgFBQUFCCUIBQWSBQUIJQgFBQUFCCUIBQVJ/gAEBAUEAgHbAgQEBAT+gAEAGwQGtQYEAfcGBQg3Ghsm/iUmGxs3CAUFBQUIsSgIFxYXtxcWFgkosAgFBgIS/rcIBQUFBQgBSQgFBgYFCP63CAUFBQUIAUkIBQYGBQj+twgFBQUFCAFJCAUGBgX+WwId/eMNCwoFBQUFCgsNAmZDBQICBVUkCAYF/eMwIiMhIi8CIAUGCCQIBQVgFQ8PDw8VYAUFCAACAAcASQO3Aq8AGgAuAAAJAQYjIi8BJjU0PwEnJjU0PwE2MzIXARYVFAcBFRQHBiMhIicmPQE0NzYzITIXFgFO/vYGBwgFHQYG4eEGBh0FCAcGAQoGBgJpBQUI/dsIBQUFBQgCJQgFBQGF/vYGBhwGCAcG4OEGBwcGHQUF/vUFCAcG/vslCAUFBQUIJQgFBQUFAAAAAQAjAAAD3QNuALMAACUiJyYjIgcGIyInJjU0NzY3Njc2NzY9ATQnJiMhIgcGHQEUFxYXFjMWFxYVFAcGIyInJiMiBwYjIicmNTQ3Njc2NzY3Nj0BETQ1NDU0JzQnJicmJyYnJicmIyInJjU0NzYzMhcWMzI3NjMyFxYVFAcGIwYHBgcGHQEUFxYzITI3Nj0BNCcmJyYnJjU0NzYzMhcWMzI3NjMyFxYVFAcGByIHBgcGFREUFxYXFhcyFxYVFAcGIwPBGTMyGhkyMxkNCAcJCg0MERAKEgEHFf5+FgcBFQkSEw4ODAsHBw4bNTUaGDExGA0HBwkJCwwQDwkSAQIBAgMEBAUIEhENDQoLBwcOGjU1GhgwMRgOBwcJCgwNEBAIFAEHDwGQDgcBFAoXFw8OBwcOGTMyGRkxMRkOBwcKCg0NEBEIFBQJEREODQoLBwcOAAICAgIMCw8RCQkBAQMDBQxE4AwFAwMFDNRRDQYBAgEICBIPDA0CAgICDAwOEQgJAQIDAwUNRSEB0AINDQgIDg4KCgsLBwcDBgEBCAgSDwwNAgICAg0MDxEICAECAQYMULYMBwEBBwy2UAwGAQEGBxYPDA0CAgICDQwPEQgIAQECBg1P/eZEDAYCAgEJCBEPDA0AAAIAAP+3A/8DtwATADkAAAEyFxYVFAcCBwYjIicmNTQ3ATYzARYXFh8BFgcGIyInJicmJyY1FhcWFxYXFjMyNzY3Njc2NzY3NjcDmygeHhq+TDdFSDQ0NQFtISn9+BcmJy8BAkxMe0c2NiEhEBEEExQQEBIRCRcIDxITFRUdHR4eKQO3GxooJDP+mUY0NTRJSTABSx/9sSsfHw0oek1MGhsuLzo6RAMPDgsLCgoWJRsaEREKCwQEAgABAAAAAAAA9evv618PPPUACwQAAAAAANbEBFgAAAAA1sQEWAAA/7cEAQPAAAAACAACAAAAAAAAAAEAAAPA/8AAAAQAAAD//wQBAAEAAAAAAAAAAAAAAAAAAAAhBAAAAAAAAAAAAAAAAgAAAAQAAAAEAAAABAAAAAQAAMAEAAAABAAAAAQAAAAEAABABAAAAAQAAAAEAAAeBAAAAAQAAAAEAABlBAAAAAQAAMAEAADABAAAgAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAMlAD8DJQAAA74ABwQAACMD/wAAAAAAAAAKABQAHgBMAJQA+AE2AXwBwgI2AnQCvgLoA34EHgSIBMoE8gU0BXAFiAXgBiIGagaSBroG5AcoB+AIKgkcCXgAAQAAACEAtAAKAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAcAAAABAAAAAAACAAcAYAABAAAAAAADAAcANgABAAAAAAAEAAcAdQABAAAAAAAFAAsAFQABAAAAAAAGAAcASwABAAAAAAAKABoAigADAAEECQABAA4ABwADAAEECQACAA4AZwADAAEECQADAA4APQADAAEECQAEAA4AfAADAAEECQAFABYAIAADAAEECQAGAA4AUgADAAEECQAKADQApGljb21vb24AaQBjAG8AbQBvAG8AblZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMGljb21vb24AaQBjAG8AbQBvAG8Abmljb21vb24AaQBjAG8AbQBvAG8AblJlZ3VsYXIAUgBlAGcAdQBsAGEAcmljb21vb24AaQBjAG8AbQBvAG8AbkZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=) format('truetype'); + font-weight: normal; + font-style: normal; +} +[class^="w-e-icon-"], +[class*=" w-e-icon-"] { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: 'w-e-icon' !important; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.w-e-icon-close:before { + content: "\f00d"; +} +.w-e-icon-upload2:before { + content: "\e9c6"; +} +.w-e-icon-trash-o:before { + content: "\f014"; +} +.w-e-icon-header:before { + content: "\f1dc"; +} +.w-e-icon-pencil2:before { + content: "\e906"; +} +.w-e-icon-paint-brush:before { + content: "\f1fc"; +} +.w-e-icon-image:before { + content: "\e90d"; +} +.w-e-icon-play:before { + content: "\e912"; +} +.w-e-icon-location:before { + content: "\e947"; +} +.w-e-icon-undo:before { + content: "\e965"; +} +.w-e-icon-redo:before { + content: "\e966"; +} +.w-e-icon-quotes-left:before { + content: "\e977"; +} +.w-e-icon-list-numbered:before { + content: "\e9b9"; +} +.w-e-icon-list2:before { + content: "\e9bb"; +} +.w-e-icon-link:before { + content: "\e9cb"; +} +.w-e-icon-happy:before { + content: "\e9df"; +} +.w-e-icon-bold:before { + content: "\ea62"; +} +.w-e-icon-underline:before { + content: "\ea63"; +} +.w-e-icon-italic:before { + content: "\ea64"; +} +.w-e-icon-strikethrough:before { + content: "\ea65"; +} +.w-e-icon-table2:before { + content: "\ea71"; +} +.w-e-icon-paragraph-left:before { + content: "\ea77"; +} +.w-e-icon-paragraph-center:before { + content: "\ea78"; +} +.w-e-icon-paragraph-right:before { + content: "\ea79"; +} +.w-e-icon-terminal:before { + content: "\f120"; +} +.w-e-icon-page-break:before { + content: "\ea68"; +} +.w-e-icon-cancel-circle:before { + content: "\ea0d"; +} +.w-e-icon-font:before { + content: "\ea5c"; +} +.w-e-icon-text-heigh:before { + content: "\ea5f"; +} + +.w-e-toolbar { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + padding: 0 5px; + /* flex-wrap: wrap; */ + /* 单个菜单 */ +} +.w-e-toolbar .w-e-menu { + position: relative; + text-align: center; + padding: 5px 10px; + cursor: pointer; +} +.w-e-toolbar .w-e-menu i { + color: #999; +} +.w-e-toolbar .w-e-menu:hover i { + color: #333; +} +.w-e-toolbar .w-e-active i { + color: #1e88e5; +} +.w-e-toolbar .w-e-active:hover i { + color: #1e88e5; +} + +.w-e-text-container .w-e-panel-container { + position: absolute; + top: 0; + left: 50%; + border: 1px solid #ccc; + border-top: 0; + box-shadow: 1px 1px 2px #ccc; + color: #333; + background-color: #fff; + /* 为 emotion panel 定制的样式 */ + /* 上传图片的 panel 定制样式 */ +} +.w-e-text-container .w-e-panel-container .w-e-panel-close { + position: absolute; + right: 0; + top: 0; + padding: 5px; + margin: 2px 5px 0 0; + cursor: pointer; + color: #999; +} +.w-e-text-container .w-e-panel-container .w-e-panel-close:hover { + color: #333; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-title { + list-style: none; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + font-size: 14px; + margin: 2px 10px 0 10px; + border-bottom: 1px solid #f1f1f1; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-title .w-e-item { + padding: 3px 5px; + color: #999; + cursor: pointer; + margin: 0 3px; + position: relative; + top: 1px; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-title .w-e-active { + color: #333; + border-bottom: 1px solid #333; + cursor: default; + font-weight: 700; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content { + padding: 10px 15px 10px 15px; + font-size: 16px; + /* 输入框的样式 */ + /* 按钮的样式 */ +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input:focus, +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content textarea:focus, +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content button:focus { + outline: none; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content textarea { + width: 100%; + border: 1px solid #ccc; + padding: 5px; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content textarea:focus { + border-color: #1e88e5; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input[type=text] { + border: none; + border-bottom: 1px solid #ccc; + font-size: 14px; + height: 20px; + color: #333; + text-align: left; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input[type=text].small { + width: 30px; + text-align: center; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input[type=text].block { + display: block; + width: 100%; + margin: 10px 0; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input[type=text]:focus { + border-bottom: 2px solid #1e88e5; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button { + font-size: 14px; + color: #1e88e5; + border: none; + padding: 5px 10px; + background-color: #fff; + cursor: pointer; + border-radius: 3px; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button.left { + float: left; + margin-right: 10px; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button.right { + float: right; + margin-left: 10px; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button.gray { + color: #999; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button.red { + color: #c24f4a; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button:hover { + background-color: #f1f1f1; +} +.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container:after { + content: ""; + display: table; + clear: both; +} +.w-e-text-container .w-e-panel-container .w-e-emoticon-container .w-e-item { + cursor: pointer; + font-size: 18px; + padding: 0 3px; + display: inline-block; + *display: inline; + *zoom: 1; +} +.w-e-text-container .w-e-panel-container .w-e-up-img-container { + text-align: center; +} +.w-e-text-container .w-e-panel-container .w-e-up-img-container .w-e-up-btn { + display: inline-block; + *display: inline; + *zoom: 1; + color: #999; + cursor: pointer; + font-size: 60px; + line-height: 1; +} +.w-e-text-container .w-e-panel-container .w-e-up-img-container .w-e-up-btn:hover { + color: #333; +} + +.w-e-text-container { + position: relative; +} +.w-e-text-container .w-e-progress { + position: absolute; + background-color: #1e88e5; + bottom: 0; + left: 0; + height: 1px; +} +.w-e-text { + padding: 0 10px; + overflow-y: scroll; +} +.w-e-text p, +.w-e-text h1, +.w-e-text h2, +.w-e-text h3, +.w-e-text h4, +.w-e-text h5, +.w-e-text table, +.w-e-text pre { + margin: 10px 0; + line-height: 1.5; +} +.w-e-text ul, +.w-e-text ol { + margin: 10px 0 10px 20px; +} +.w-e-text blockquote { + display: block; + border-left: 8px solid #d0e5f2; + padding: 5px 10px; + margin: 10px 0; + line-height: 1.4; + font-size: 100%; + background-color: #f1f1f1; +} +.w-e-text code { + display: inline-block; + *display: inline; + *zoom: 1; + background-color: #f1f1f1; + border-radius: 3px; + padding: 3px 5px; + margin: 0 3px; +} +.w-e-text pre code { + display: block; +} +.w-e-text table { + border-top: 1px solid #ccc; + border-left: 1px solid #ccc; +} +.w-e-text table td, +.w-e-text table th { + border-bottom: 1px solid #ccc; + border-right: 1px solid #ccc; + padding: 3px 5px; +} +.w-e-text table th { + border-bottom: 2px solid #ccc; + text-align: center; +} +.w-e-text:focus { + outline: none; +} +.w-e-text img { + cursor: pointer; +} +.w-e-text img:hover { + box-shadow: 0 0 5px #333; +} diff --git a/public/js/lay-module/wangEditor/wangEditor.js b/public/js/lay-module/wangEditor/wangEditor.js new file mode 100644 index 0000000..fe909c8 --- /dev/null +++ b/public/js/lay-module/wangEditor/wangEditor.js @@ -0,0 +1,4678 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.wangEditor = factory()); +}(this, (function () { 'use strict'; + +/* + poly-fill +*/ + +var polyfill = function () { + + // Object.assign + if (typeof Object.assign != 'function') { + Object.assign = function (target, varArgs) { + // .length of function is 2 + if (target == null) { + // TypeError if undefined or null + throw new TypeError('Cannot convert undefined or null to object'); + } + + var to = Object(target); + + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + + if (nextSource != null) { + // Skip over if undefined or null + for (var nextKey in nextSource) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; + }; + } + + // IE 中兼容 Element.prototype.matches + if (!Element.prototype.matches) { + Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector || function (s) { + var matches = (this.document || this.ownerDocument).querySelectorAll(s), + i = matches.length; + while (--i >= 0 && matches.item(i) !== this) {} + return i > -1; + }; + } +}; + +/* + DOM 操作 API +*/ + +// 根据 html 代码片段创建 dom 对象 +function createElemByHTML(html) { + var div = void 0; + div = document.createElement('div'); + div.innerHTML = html; + return div.children; +} + +// 是否是 DOM List +function isDOMList(selector) { + if (!selector) { + return false; + } + if (selector instanceof HTMLCollection || selector instanceof NodeList) { + return true; + } + return false; +} + +// 封装 document.querySelectorAll +function querySelectorAll(selector) { + var result = document.querySelectorAll(selector); + if (isDOMList(result)) { + return result; + } else { + return [result]; + } +} + +// 记录所有的事件绑定 +var eventList = []; + +// 创建构造函数 +function DomElement(selector) { + if (!selector) { + return; + } + + // selector 本来就是 DomElement 对象,直接返回 + if (selector instanceof DomElement) { + return selector; + } + + this.selector = selector; + var nodeType = selector.nodeType; + + // 根据 selector 得出的结果(如 DOM,DOM List) + var selectorResult = []; + if (nodeType === 9) { + // document 节点 + selectorResult = [selector]; + } else if (nodeType === 1) { + // 单个 DOM 节点 + selectorResult = [selector]; + } else if (isDOMList(selector) || selector instanceof Array) { + // DOM List 或者数组 + selectorResult = selector; + } else if (typeof selector === 'string') { + // 字符串 + selector = selector.replace('/\n/mg', '').trim(); + if (selector.indexOf('<') === 0) { + // 如
                + selectorResult = createElemByHTML(selector); + } else { + // 如 #id .class + selectorResult = querySelectorAll(selector); + } + } + + var length = selectorResult.length; + if (!length) { + // 空数组 + return this; + } + + // 加入 DOM 节点 + var i = void 0; + for (i = 0; i < length; i++) { + this[i] = selectorResult[i]; + } + this.length = length; +} + +// 修改原型 +DomElement.prototype = { + constructor: DomElement, + + // 类数组,forEach + forEach: function forEach(fn) { + var i = void 0; + for (i = 0; i < this.length; i++) { + var elem = this[i]; + var result = fn.call(elem, elem, i); + if (result === false) { + break; + } + } + return this; + }, + + // clone + clone: function clone(deep) { + var cloneList = []; + this.forEach(function (elem) { + cloneList.push(elem.cloneNode(!!deep)); + }); + return $(cloneList); + }, + + // 获取第几个元素 + get: function get(index) { + var length = this.length; + if (index >= length) { + index = index % length; + } + return $(this[index]); + }, + + // 第一个 + first: function first() { + return this.get(0); + }, + + // 最后一个 + last: function last() { + var length = this.length; + return this.get(length - 1); + }, + + // 绑定事件 + on: function on(type, selector, fn) { + // selector 不为空,证明绑定事件要加代理 + if (!fn) { + fn = selector; + selector = null; + } + + // type 是否有多个 + var types = []; + types = type.split(/\s+/); + + return this.forEach(function (elem) { + types.forEach(function (type) { + if (!type) { + return; + } + + // 记录下,方便后面解绑 + eventList.push({ + elem: elem, + type: type, + fn: fn + }); + + if (!selector) { + // 无代理 + elem.addEventListener(type, fn); + return; + } + + // 有代理 + elem.addEventListener(type, function (e) { + var target = e.target; + if (target.matches(selector)) { + fn.call(target, e); + } + }); + }); + }); + }, + + // 取消事件绑定 + off: function off(type, fn) { + return this.forEach(function (elem) { + elem.removeEventListener(type, fn); + }); + }, + + // 获取/设置 属性 + attr: function attr(key, val) { + if (val == null) { + // 获取值 + return this[0].getAttribute(key); + } else { + // 设置值 + return this.forEach(function (elem) { + elem.setAttribute(key, val); + }); + } + }, + + // 添加 class + addClass: function addClass(className) { + if (!className) { + return this; + } + return this.forEach(function (elem) { + var arr = void 0; + if (elem.className) { + // 解析当前 className 转换为数组 + arr = elem.className.split(/\s/); + arr = arr.filter(function (item) { + return !!item.trim(); + }); + // 添加 class + if (arr.indexOf(className) < 0) { + arr.push(className); + } + // 修改 elem.class + elem.className = arr.join(' '); + } else { + elem.className = className; + } + }); + }, + + // 删除 class + removeClass: function removeClass(className) { + if (!className) { + return this; + } + return this.forEach(function (elem) { + var arr = void 0; + if (elem.className) { + // 解析当前 className 转换为数组 + arr = elem.className.split(/\s/); + arr = arr.filter(function (item) { + item = item.trim(); + // 删除 class + if (!item || item === className) { + return false; + } + return true; + }); + // 修改 elem.class + elem.className = arr.join(' '); + } + }); + }, + + // 修改 css + css: function css(key, val) { + var currentStyle = key + ':' + val + ';'; + return this.forEach(function (elem) { + var style = (elem.getAttribute('style') || '').trim(); + var styleArr = void 0, + resultArr = []; + if (style) { + // 将 style 按照 ; 拆分为数组 + styleArr = style.split(';'); + styleArr.forEach(function (item) { + // 对每项样式,按照 : 拆分为 key 和 value + var arr = item.split(':').map(function (i) { + return i.trim(); + }); + if (arr.length === 2) { + resultArr.push(arr[0] + ':' + arr[1]); + } + }); + // 替换或者新增 + resultArr = resultArr.map(function (item) { + if (item.indexOf(key) === 0) { + return currentStyle; + } else { + return item; + } + }); + if (resultArr.indexOf(currentStyle) < 0) { + resultArr.push(currentStyle); + } + // 结果 + elem.setAttribute('style', resultArr.join('; ')); + } else { + // style 无值 + elem.setAttribute('style', currentStyle); + } + }); + }, + + // 显示 + show: function show() { + return this.css('display', 'block'); + }, + + // 隐藏 + hide: function hide() { + return this.css('display', 'none'); + }, + + // 获取子节点 + children: function children() { + var elem = this[0]; + if (!elem) { + return null; + } + + return $(elem.children); + }, + + // 获取子节点(包括文本节点) + childNodes: function childNodes() { + var elem = this[0]; + if (!elem) { + return null; + } + + return $(elem.childNodes); + }, + + // 增加子节点 + append: function append($children) { + return this.forEach(function (elem) { + $children.forEach(function (child) { + elem.appendChild(child); + }); + }); + }, + + // 移除当前节点 + remove: function remove() { + return this.forEach(function (elem) { + if (elem.remove) { + elem.remove(); + } else { + var parent = elem.parentElement; + parent && parent.removeChild(elem); + } + }); + }, + + // 是否包含某个子节点 + isContain: function isContain($child) { + var elem = this[0]; + var child = $child[0]; + return elem.contains(child); + }, + + // 尺寸数据 + getSizeData: function getSizeData() { + var elem = this[0]; + return elem.getBoundingClientRect(); // 可得到 bottom height left right top width 的数据 + }, + + // 封装 nodeName + getNodeName: function getNodeName() { + var elem = this[0]; + return elem.nodeName; + }, + + // 从当前元素查找 + find: function find(selector) { + var elem = this[0]; + return $(elem.querySelectorAll(selector)); + }, + + // 获取当前元素的 text + text: function text(val) { + if (!val) { + // 获取 text + var elem = this[0]; + return elem.innerHTML.replace(/<.*?>/g, function () { + return ''; + }); + } else { + // 设置 text + return this.forEach(function (elem) { + elem.innerHTML = val; + }); + } + }, + + // 获取 html + html: function html(value) { + var elem = this[0]; + if (value == null) { + return elem.innerHTML; + } else { + elem.innerHTML = value; + return this; + } + }, + + // 获取 value + val: function val() { + var elem = this[0]; + return elem.value.trim(); + }, + + // focus + focus: function focus() { + return this.forEach(function (elem) { + elem.focus(); + }); + }, + + // parent + parent: function parent() { + var elem = this[0]; + return $(elem.parentElement); + }, + + // parentUntil 找到符合 selector 的父节点 + parentUntil: function parentUntil(selector, _currentElem) { + var results = document.querySelectorAll(selector); + var length = results.length; + if (!length) { + // 传入的 selector 无效 + return null; + } + + var elem = _currentElem || this[0]; + if (elem.nodeName === 'BODY') { + return null; + } + + var parent = elem.parentElement; + var i = void 0; + for (i = 0; i < length; i++) { + if (parent === results[i]) { + // 找到,并返回 + return $(parent); + } + } + + // 继续查找 + return this.parentUntil(selector, parent); + }, + + // 判断两个 elem 是否相等 + equal: function equal($elem) { + if ($elem.nodeType === 1) { + return this[0] === $elem; + } else { + return this[0] === $elem[0]; + } + }, + + // 将该元素插入到某个元素前面 + insertBefore: function insertBefore(selector) { + var $referenceNode = $(selector); + var referenceNode = $referenceNode[0]; + if (!referenceNode) { + return this; + } + return this.forEach(function (elem) { + var parent = referenceNode.parentNode; + parent.insertBefore(elem, referenceNode); + }); + }, + + // 将该元素插入到某个元素后面 + insertAfter: function insertAfter(selector) { + var $referenceNode = $(selector); + var referenceNode = $referenceNode[0]; + if (!referenceNode) { + return this; + } + return this.forEach(function (elem) { + var parent = referenceNode.parentNode; + if (parent.lastChild === referenceNode) { + // 最后一个元素 + parent.appendChild(elem); + } else { + // 不是最后一个元素 + parent.insertBefore(elem, referenceNode.nextSibling); + } + }); + } +}; + +// new 一个对象 +function $(selector) { + return new DomElement(selector); +} + +// 解绑所有事件,用于销毁编辑器 +$.offAll = function () { + eventList.forEach(function (item) { + var elem = item.elem; + var type = item.type; + var fn = item.fn; + // 解绑 + elem.removeEventListener(type, fn); + }); +}; + +/* + 配置信息 +*/ + +var config = { + + // 默认菜单配置 + menus: ['head', 'bold', 'fontSize', 'fontName', 'italic', 'underline', 'strikeThrough', 'foreColor', 'backColor', 'link', 'list', 'justify', 'quote', 'emoticon', 'image', 'table', 'video', 'code', 'undo', 'redo'], + + fontNames: ['宋体', '微软雅黑', 'Arial', 'Tahoma', 'Verdana'], + + colors: ['#000000', '#eeece0', '#1c487f', '#4d80bf', '#c24f4a', '#8baa4a', '#7b5ba1', '#46acc8', '#f9963b', '#ffffff'], + + // // 语言配置 + // lang: { + // '设置标题': 'title', + // '正文': 'p', + // '链接文字': 'link text', + // '链接': 'link', + // '插入': 'insert', + // '创建': 'init' + // }, + + // 表情 + emotions: [{ + // tab 的标题 + title: '默认', + // type -> 'emoji' / 'image' + type: 'image', + // content -> 数组 + content: [{ + alt: '[坏笑]', + src: 'http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/50/pcmoren_huaixiao_org.png' + }, { + alt: '[舔屏]', + src: 'http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/40/pcmoren_tian_org.png' + }, { + alt: '[污]', + src: 'http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/3c/pcmoren_wu_org.png' + }] + }, { + // tab 的标题 + title: '新浪', + // type -> 'emoji' / 'image' + type: 'image', + // content -> 数组 + content: [{ + src: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/7a/shenshou_thumb.gif', + alt: '[草泥马]' + }, { + src: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/60/horse2_thumb.gif', + alt: '[神马]' + }, { + src: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/bc/fuyun_thumb.gif', + alt: '[浮云]' + }] + }, { + // tab 的标题 + title: 'emoji', + // type -> 'emoji' / 'image' + type: 'emoji', + // content -> 数组 + content: '😀 😃 😄 😁 😆 😅 😂 😊 😇 🙂 🙃 😉 😓 😪 😴 🙄 🤔 😬 🤐'.split(/\s/) + }], + + // 编辑区域的 z-index + zIndex: 10000, + + // 是否开启 debug 模式(debug 模式下错误会 throw error 形式抛出) + debug: false, + + // 插入链接时候的格式校验 + linkCheck: function linkCheck(text, link) { + // text 是插入的文字 + // link 是插入的链接 + return true; // 返回 true 即表示成功 + // return '校验失败' // 返回字符串即表示失败的提示信息 + }, + + // 插入网络图片的校验 + linkImgCheck: function linkImgCheck(src) { + // src 即图片的地址 + return true; // 返回 true 即表示成功 + // return '校验失败' // 返回字符串即表示失败的提示信息 + }, + + // 粘贴过滤样式,默认开启 + pasteFilterStyle: true, + + // 粘贴内容时,忽略图片。默认关闭 + pasteIgnoreImg: false, + + // 对粘贴的文字进行自定义处理,返回处理后的结果。编辑器会将处理后的结果粘贴到编辑区域中。 + // IE 暂时不支持 + pasteTextHandle: function pasteTextHandle(content) { + // content 即粘贴过来的内容(html 或 纯文本),可进行自定义处理然后返回 + return content; + }, + + // onchange 事件 + // onchange: function (html) { + // // html 即变化之后的内容 + // console.log(html) + // }, + + // 是否显示添加网络图片的 tab + showLinkImg: true, + + // 插入网络图片的回调 + linkImgCallback: function linkImgCallback(url) { + // console.log(url) // url 即插入图片的地址 + }, + + // 默认上传图片 max size: 5M + uploadImgMaxSize: 5 * 1024 * 1024, + + // 配置一次最多上传几个图片 + // uploadImgMaxLength: 5, + + // 上传图片,是否显示 base64 格式 + uploadImgShowBase64: false, + + // 上传图片,server 地址(如果有值,则 base64 格式的配置则失效) + // uploadImgServer: '/upload', + + // 自定义配置 filename + uploadFileName: '', + + // 上传图片的自定义参数 + uploadImgParams: { + // token: 'abcdef12345' + }, + + // 上传图片的自定义header + uploadImgHeaders: { + // 'Accept': 'text/x-json' + }, + + // 配置 XHR withCredentials + withCredentials: false, + + // 自定义上传图片超时时间 ms + uploadImgTimeout: 10000, + + // 上传图片 hook + uploadImgHooks: { + // customInsert: function (insertLinkImg, result, editor) { + // console.log('customInsert') + // // 图片上传并返回结果,自定义插入图片的事件,而不是编辑器自动插入图片 + // const data = result.data1 || [] + // data.forEach(link => { + // insertLinkImg(link) + // }) + // }, + before: function before(xhr, editor, files) { + // 图片上传之前触发 + + // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传 + // return { + // prevent: true, + // msg: '放弃上传' + // } + }, + success: function success(xhr, editor, result) { + // 图片上传并返回结果,图片插入成功之后触发 + }, + fail: function fail(xhr, editor, result) { + // 图片上传并返回结果,但图片插入错误时触发 + }, + error: function error(xhr, editor) { + // 图片上传出错时触发 + }, + timeout: function timeout(xhr, editor) { + // 图片上传超时时触发 + } + }, + + // 是否上传七牛云,默认为 false + qiniu: false + +}; + +/* + 工具 +*/ + +// 和 UA 相关的属性 +var UA = { + _ua: navigator.userAgent, + + // 是否 webkit + isWebkit: function isWebkit() { + var reg = /webkit/i; + return reg.test(this._ua); + }, + + // 是否 IE + isIE: function isIE() { + return 'ActiveXObject' in window; + } +}; + +// 遍历对象 +function objForEach(obj, fn) { + var key = void 0, + result = void 0; + for (key in obj) { + if (obj.hasOwnProperty(key)) { + result = fn.call(obj, key, obj[key]); + if (result === false) { + break; + } + } + } +} + +// 遍历类数组 +function arrForEach(fakeArr, fn) { + var i = void 0, + item = void 0, + result = void 0; + var length = fakeArr.length || 0; + for (i = 0; i < length; i++) { + item = fakeArr[i]; + result = fn.call(fakeArr, item, i); + if (result === false) { + break; + } + } +} + +// 获取随机数 +function getRandom(prefix) { + return prefix + Math.random().toString().slice(2); +} + +// 替换 html 特殊字符 +function replaceHtmlSymbol(html) { + if (html == null) { + return ''; + } + return html.replace(//gm, '>').replace(/"/gm, '"').replace(/(\r\n|\r|\n)/g, '
                '); +} + +// 返回百分比的格式 + + +// 判断是不是 function +function isFunction(fn) { + return typeof fn === 'function'; +} + +/* + bold-menu +*/ +// 构造函数 +function Bold(editor) { + this.editor = editor; + this.$elem = $('
                \n \n
                '); + this.type = 'click'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Bold.prototype = { + constructor: Bold, + + // 点击事件 + onClick: function onClick(e) { + // 点击菜单将触发这里 + + var editor = this.editor; + var isSeleEmpty = editor.selection.isSelectionEmpty(); + + if (isSeleEmpty) { + // 选区是空的,插入并选中一个“空白” + editor.selection.createEmptyRange(); + } + + // 执行 bold 命令 + editor.cmd.do('bold'); + + if (isSeleEmpty) { + // 需要将选取折叠起来 + editor.selection.collapseRange(); + editor.selection.restoreSelection(); + } + }, + + // 试图改变 active 状态 + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + if (editor.cmd.queryCommandState('bold')) { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + 替换多语言 + */ + +var replaceLang = function (editor, str) { + var langArgs = editor.config.langArgs || []; + var result = str; + + langArgs.forEach(function (item) { + var reg = item.reg; + var val = item.val; + + if (reg.test(result)) { + result = result.replace(reg, function () { + return val; + }); + } + }); + + return result; +}; + +/* + droplist +*/ +var _emptyFn = function _emptyFn() {}; + +// 构造函数 +function DropList(menu, opt) { + var _this = this; + + // droplist 所依附的菜单 + var editor = menu.editor; + this.menu = menu; + this.opt = opt; + // 容器 + var $container = $('
                '); + + // 标题 + var $title = opt.$title; + var titleHtml = void 0; + if ($title) { + // 替换多语言 + titleHtml = $title.html(); + titleHtml = replaceLang(editor, titleHtml); + $title.html(titleHtml); + + $title.addClass('w-e-dp-title'); + $container.append($title); + } + + var list = opt.list || []; + var type = opt.type || 'list'; // 'list' 列表形式(如“标题”菜单) / 'inline-block' 块状形式(如“颜色”菜单) + var onClick = opt.onClick || _emptyFn; + + // 加入 DOM 并绑定事件 + var $list = $('
                  '); + $container.append($list); + list.forEach(function (item) { + var $elem = item.$elem; + + // 替换多语言 + var elemHtml = $elem.html(); + elemHtml = replaceLang(editor, elemHtml); + $elem.html(elemHtml); + + var value = item.value; + var $li = $('
                • '); + if ($elem) { + $li.append($elem); + $list.append($li); + $li.on('click', function (e) { + onClick(value); + + // 隐藏 + _this.hideTimeoutId = setTimeout(function () { + _this.hide(); + }, 0); + }); + } + }); + + // 绑定隐藏事件 + $container.on('mouseleave', function (e) { + _this.hideTimeoutId = setTimeout(function () { + _this.hide(); + }, 0); + }); + + // 记录属性 + this.$container = $container; + + // 基本属性 + this._rendered = false; + this._show = false; +} + +// 原型 +DropList.prototype = { + constructor: DropList, + + // 显示(插入DOM) + show: function show() { + if (this.hideTimeoutId) { + // 清除之前的定时隐藏 + clearTimeout(this.hideTimeoutId); + } + + var menu = this.menu; + var $menuELem = menu.$elem; + var $container = this.$container; + if (this._show) { + return; + } + if (this._rendered) { + // 显示 + $container.show(); + } else { + // 加入 DOM 之前先定位位置 + var menuHeight = $menuELem.getSizeData().height || 0; + var width = this.opt.width || 100; // 默认为 100 + $container.css('margin-top', menuHeight + 'px').css('width', width + 'px'); + + // 加入到 DOM + $menuELem.append($container); + this._rendered = true; + } + + // 修改属性 + this._show = true; + }, + + // 隐藏(移除DOM) + hide: function hide() { + if (this.showTimeoutId) { + // 清除之前的定时显示 + clearTimeout(this.showTimeoutId); + } + + var $container = this.$container; + if (!this._show) { + return; + } + // 隐藏并需改属性 + $container.hide(); + this._show = false; + } +}; + +/* + menu - header +*/ +// 构造函数 +function Head(editor) { + var _this = this; + + this.editor = editor; + this.$elem = $('
                  '); + this.type = 'droplist'; + + // 当前是否 active 状态 + this._active = false; + + // 初始化 droplist + this.droplist = new DropList(this, { + width: 100, + $title: $('

                  设置标题

                  '), + type: 'list', // droplist 以列表形式展示 + list: [{ $elem: $('

                  H1

                  '), value: '

                  ' }, { $elem: $('

                  H2

                  '), value: '

                  ' }, { $elem: $('

                  H3

                  '), value: '

                  ' }, { $elem: $('

                  H4

                  '), value: '

                  ' }, { $elem: $('

                  H5
                  '), value: '
                  ' }, { $elem: $('

                  正文

                  '), value: '

                  ' }], + onClick: function onClick(value) { + // 注意 this 是指向当前的 Head 对象 + _this._command(value); + } + }); +} + +// 原型 +Head.prototype = { + constructor: Head, + + // 执行命令 + _command: function _command(value) { + var editor = this.editor; + + var $selectionElem = editor.selection.getSelectionContainerElem(); + if (editor.$textElem.equal($selectionElem)) { + // 不能选中多行来设置标题,否则会出现问题 + // 例如选中的是

                  xxx

                  yyy

                  来设置标题,设置之后会成为

                  xxx
                  yyy

                  不符合预期 + return; + } + + editor.cmd.do('formatBlock', value); + }, + + // 试图改变 active 状态 + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + var reg = /^h/i; + var cmdValue = editor.cmd.queryCommandValue('formatBlock'); + if (reg.test(cmdValue)) { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + menu - fontSize +*/ + +// 构造函数 +function FontSize(editor) { + var _this = this; + + this.editor = editor; + this.$elem = $('
                  '); + this.type = 'droplist'; + + // 当前是否 active 状态 + this._active = false; + + // 初始化 droplist + this.droplist = new DropList(this, { + width: 160, + $title: $('

                  字号

                  '), + type: 'list', // droplist 以列表形式展示 + list: [{ $elem: $('x-small'), value: '1' }, { $elem: $('small'), value: '2' }, { $elem: $('normal'), value: '3' }, { $elem: $('large'), value: '4' }, { $elem: $('x-large'), value: '5' }, { $elem: $('xx-large'), value: '6' }], + onClick: function onClick(value) { + // 注意 this 是指向当前的 FontSize 对象 + _this._command(value); + } + }); +} + +// 原型 +FontSize.prototype = { + constructor: FontSize, + + // 执行命令 + _command: function _command(value) { + var editor = this.editor; + editor.cmd.do('fontSize', value); + } +}; + +/* + menu - fontName +*/ + +// 构造函数 +function FontName(editor) { + var _this = this; + + this.editor = editor; + this.$elem = $('
                  '); + this.type = 'droplist'; + + // 当前是否 active 状态 + this._active = false; + + // 获取配置的字体 + var config = editor.config; + var fontNames = config.fontNames || []; + + // 初始化 droplist + this.droplist = new DropList(this, { + width: 100, + $title: $('

                  字体

                  '), + type: 'list', // droplist 以列表形式展示 + list: fontNames.map(function (fontName) { + return { $elem: $('' + fontName + ''), value: fontName }; + }), + onClick: function onClick(value) { + // 注意 this 是指向当前的 FontName 对象 + _this._command(value); + } + }); +} + +// 原型 +FontName.prototype = { + constructor: FontName, + + _command: function _command(value) { + var editor = this.editor; + editor.cmd.do('fontName', value); + } +}; + +/* + panel +*/ + +var emptyFn = function emptyFn() {}; + +// 记录已经显示 panel 的菜单 +var _isCreatedPanelMenus = []; + +// 构造函数 +function Panel(menu, opt) { + this.menu = menu; + this.opt = opt; +} + +// 原型 +Panel.prototype = { + constructor: Panel, + + // 显示(插入DOM) + show: function show() { + var _this = this; + + var menu = this.menu; + if (_isCreatedPanelMenus.indexOf(menu) >= 0) { + // 该菜单已经创建了 panel 不能再创建 + return; + } + + var editor = menu.editor; + var $body = $('body'); + var $textContainerElem = editor.$textContainerElem; + var opt = this.opt; + + // panel 的容器 + var $container = $('
                  '); + var width = opt.width || 300; // 默认 300px + $container.css('width', width + 'px').css('margin-left', (0 - width) / 2 + 'px'); + + // 添加关闭按钮 + var $closeBtn = $(''); + $container.append($closeBtn); + $closeBtn.on('click', function () { + _this.hide(); + }); + + // 准备 tabs 容器 + var $tabTitleContainer = $('
                    '); + var $tabContentContainer = $('
                    '); + $container.append($tabTitleContainer).append($tabContentContainer); + + // 设置高度 + var height = opt.height; + if (height) { + $tabContentContainer.css('height', height + 'px').css('overflow-y', 'auto'); + } + + // tabs + var tabs = opt.tabs || []; + var tabTitleArr = []; + var tabContentArr = []; + tabs.forEach(function (tab, tabIndex) { + if (!tab) { + return; + } + var title = tab.title || ''; + var tpl = tab.tpl || ''; + + // 替换多语言 + title = replaceLang(editor, title); + tpl = replaceLang(editor, tpl); + + // 添加到 DOM + var $title = $('
                  • ' + title + '
                  • '); + $tabTitleContainer.append($title); + var $content = $(tpl); + $tabContentContainer.append($content); + + // 记录到内存 + $title._index = tabIndex; + tabTitleArr.push($title); + tabContentArr.push($content); + + // 设置 active 项 + if (tabIndex === 0) { + $title._active = true; + $title.addClass('w-e-active'); + } else { + $content.hide(); + } + + // 绑定 tab 的事件 + $title.on('click', function (e) { + if ($title._active) { + return; + } + // 隐藏所有的 tab + tabTitleArr.forEach(function ($title) { + $title._active = false; + $title.removeClass('w-e-active'); + }); + tabContentArr.forEach(function ($content) { + $content.hide(); + }); + + // 显示当前的 tab + $title._active = true; + $title.addClass('w-e-active'); + $content.show(); + }); + }); + + // 绑定关闭事件 + $container.on('click', function (e) { + // 点击时阻止冒泡 + e.stopPropagation(); + }); + $body.on('click', function (e) { + _this.hide(); + }); + + // 添加到 DOM + $textContainerElem.append($container); + + // 绑定 opt 的事件,只有添加到 DOM 之后才能绑定成功 + tabs.forEach(function (tab, index) { + if (!tab) { + return; + } + var events = tab.events || []; + events.forEach(function (event) { + var selector = event.selector; + var type = event.type; + var fn = event.fn || emptyFn; + var $content = tabContentArr[index]; + $content.find(selector).on(type, function (e) { + e.stopPropagation(); + var needToHide = fn(e); + // 执行完事件之后,是否要关闭 panel + if (needToHide) { + _this.hide(); + } + }); + }); + }); + + // focus 第一个 elem + var $inputs = $container.find('input[type=text],textarea'); + if ($inputs.length) { + $inputs.get(0).focus(); + } + + // 添加到属性 + this.$container = $container; + + // 隐藏其他 panel + this._hideOtherPanels(); + // 记录该 menu 已经创建了 panel + _isCreatedPanelMenus.push(menu); + }, + + // 隐藏(移除DOM) + hide: function hide() { + var menu = this.menu; + var $container = this.$container; + if ($container) { + $container.remove(); + } + + // 将该 menu 记录中移除 + _isCreatedPanelMenus = _isCreatedPanelMenus.filter(function (item) { + if (item === menu) { + return false; + } else { + return true; + } + }); + }, + + // 一个 panel 展示时,隐藏其他 panel + _hideOtherPanels: function _hideOtherPanels() { + if (!_isCreatedPanelMenus.length) { + return; + } + _isCreatedPanelMenus.forEach(function (menu) { + var panel = menu.panel || {}; + if (panel.hide) { + panel.hide(); + } + }); + } +}; + +/* + menu - link +*/ +// 构造函数 +function Link(editor) { + this.editor = editor; + this.$elem = $('
                    '); + this.type = 'panel'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Link.prototype = { + constructor: Link, + + // 点击事件 + onClick: function onClick(e) { + var editor = this.editor; + var $linkelem = void 0; + + if (this._active) { + // 当前选区在链接里面 + $linkelem = editor.selection.getSelectionContainerElem(); + if (!$linkelem) { + return; + } + // 将该元素都包含在选取之内,以便后面整体替换 + editor.selection.createRangeByElem($linkelem); + editor.selection.restoreSelection(); + // 显示 panel + this._createPanel($linkelem.text(), $linkelem.attr('href')); + } else { + // 当前选区不在链接里面 + if (editor.selection.isSelectionEmpty()) { + // 选区是空的,未选中内容 + this._createPanel('', ''); + } else { + // 选中内容了 + this._createPanel(editor.selection.getSelectionText(), ''); + } + } + }, + + // 创建 panel + _createPanel: function _createPanel(text, link) { + var _this = this; + + // panel 中需要用到的id + var inputLinkId = getRandom('input-link'); + var inputTextId = getRandom('input-text'); + var btnOkId = getRandom('btn-ok'); + var btnDelId = getRandom('btn-del'); + + // 是否显示“删除链接” + var delBtnDisplay = this._active ? 'inline-block' : 'none'; + + // 初始化并显示 panel + var panel = new Panel(this, { + width: 300, + // panel 中可包含多个 tab + tabs: [{ + // tab 的标题 + title: '链接', + // 模板 + tpl: '
                    \n \n \n
                    \n \n \n
                    \n
                    ', + // 事件绑定 + events: [ + // 插入链接 + { + selector: '#' + btnOkId, + type: 'click', + fn: function fn() { + // 执行插入链接 + var $link = $('#' + inputLinkId); + var $text = $('#' + inputTextId); + var link = $link.val(); + var text = $text.val(); + _this._insertLink(text, link); + + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, + // 删除链接 + { + selector: '#' + btnDelId, + type: 'click', + fn: function fn() { + // 执行删除链接 + _this._delLink(); + + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }] + } // tab end + ] // tabs end + }); + + // 显示 panel + panel.show(); + + // 记录属性 + this.panel = panel; + }, + + // 删除当前链接 + _delLink: function _delLink() { + if (!this._active) { + return; + } + var editor = this.editor; + var $selectionELem = editor.selection.getSelectionContainerElem(); + if (!$selectionELem) { + return; + } + var selectionText = editor.selection.getSelectionText(); + editor.cmd.do('insertHTML', '' + selectionText + ''); + }, + + // 插入链接 + _insertLink: function _insertLink(text, link) { + var editor = this.editor; + var config = editor.config; + var linkCheck = config.linkCheck; + var checkResult = true; // 默认为 true + if (linkCheck && typeof linkCheck === 'function') { + checkResult = linkCheck(text, link); + } + if (checkResult === true) { + editor.cmd.do('insertHTML', '' + text + ''); + } else { + alert(checkResult); + } + }, + + // 试图改变 active 状态 + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + var $selectionELem = editor.selection.getSelectionContainerElem(); + if (!$selectionELem) { + return; + } + if ($selectionELem.getNodeName() === 'A') { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + italic-menu +*/ +// 构造函数 +function Italic(editor) { + this.editor = editor; + this.$elem = $('
                    \n \n
                    '); + this.type = 'click'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Italic.prototype = { + constructor: Italic, + + // 点击事件 + onClick: function onClick(e) { + // 点击菜单将触发这里 + + var editor = this.editor; + var isSeleEmpty = editor.selection.isSelectionEmpty(); + + if (isSeleEmpty) { + // 选区是空的,插入并选中一个“空白” + editor.selection.createEmptyRange(); + } + + // 执行 italic 命令 + editor.cmd.do('italic'); + + if (isSeleEmpty) { + // 需要将选取折叠起来 + editor.selection.collapseRange(); + editor.selection.restoreSelection(); + } + }, + + // 试图改变 active 状态 + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + if (editor.cmd.queryCommandState('italic')) { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + redo-menu +*/ +// 构造函数 +function Redo(editor) { + this.editor = editor; + this.$elem = $('
                    \n \n
                    '); + this.type = 'click'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Redo.prototype = { + constructor: Redo, + + // 点击事件 + onClick: function onClick(e) { + // 点击菜单将触发这里 + + var editor = this.editor; + + // 执行 redo 命令 + editor.cmd.do('redo'); + } +}; + +/* + strikeThrough-menu +*/ +// 构造函数 +function StrikeThrough(editor) { + this.editor = editor; + this.$elem = $('
                    \n \n
                    '); + this.type = 'click'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +StrikeThrough.prototype = { + constructor: StrikeThrough, + + // 点击事件 + onClick: function onClick(e) { + // 点击菜单将触发这里 + + var editor = this.editor; + var isSeleEmpty = editor.selection.isSelectionEmpty(); + + if (isSeleEmpty) { + // 选区是空的,插入并选中一个“空白” + editor.selection.createEmptyRange(); + } + + // 执行 strikeThrough 命令 + editor.cmd.do('strikeThrough'); + + if (isSeleEmpty) { + // 需要将选取折叠起来 + editor.selection.collapseRange(); + editor.selection.restoreSelection(); + } + }, + + // 试图改变 active 状态 + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + if (editor.cmd.queryCommandState('strikeThrough')) { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + underline-menu +*/ +// 构造函数 +function Underline(editor) { + this.editor = editor; + this.$elem = $('
                    \n \n
                    '); + this.type = 'click'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Underline.prototype = { + constructor: Underline, + + // 点击事件 + onClick: function onClick(e) { + // 点击菜单将触发这里 + + var editor = this.editor; + var isSeleEmpty = editor.selection.isSelectionEmpty(); + + if (isSeleEmpty) { + // 选区是空的,插入并选中一个“空白” + editor.selection.createEmptyRange(); + } + + // 执行 underline 命令 + editor.cmd.do('underline'); + + if (isSeleEmpty) { + // 需要将选取折叠起来 + editor.selection.collapseRange(); + editor.selection.restoreSelection(); + } + }, + + // 试图改变 active 状态 + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + if (editor.cmd.queryCommandState('underline')) { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + undo-menu +*/ +// 构造函数 +function Undo(editor) { + this.editor = editor; + this.$elem = $('
                    \n \n
                    '); + this.type = 'click'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Undo.prototype = { + constructor: Undo, + + // 点击事件 + onClick: function onClick(e) { + // 点击菜单将触发这里 + + var editor = this.editor; + + // 执行 undo 命令 + editor.cmd.do('undo'); + } +}; + +/* + menu - list +*/ +// 构造函数 +function List(editor) { + var _this = this; + + this.editor = editor; + this.$elem = $('
                    '); + this.type = 'droplist'; + + // 当前是否 active 状态 + this._active = false; + + // 初始化 droplist + this.droplist = new DropList(this, { + width: 120, + $title: $('

                    设置列表

                    '), + type: 'list', // droplist 以列表形式展示 + list: [{ $elem: $(' 有序列表'), value: 'insertOrderedList' }, { $elem: $(' 无序列表'), value: 'insertUnorderedList' }], + onClick: function onClick(value) { + // 注意 this 是指向当前的 List 对象 + _this._command(value); + } + }); +} + +// 原型 +List.prototype = { + constructor: List, + + // 执行命令 + _command: function _command(value) { + var editor = this.editor; + var $textElem = editor.$textElem; + editor.selection.restoreSelection(); + if (editor.cmd.queryCommandState(value)) { + return; + } + editor.cmd.do(value); + + // 验证列表是否被包裹在

                    之内 + var $selectionElem = editor.selection.getSelectionContainerElem(); + if ($selectionElem.getNodeName() === 'LI') { + $selectionElem = $selectionElem.parent(); + } + if (/^ol|ul$/i.test($selectionElem.getNodeName()) === false) { + return; + } + if ($selectionElem.equal($textElem)) { + // 证明是顶级标签,没有被

                    包裹 + return; + } + var $parent = $selectionElem.parent(); + if ($parent.equal($textElem)) { + // $parent 是顶级标签,不能删除 + return; + } + + $selectionElem.insertAfter($parent); + $parent.remove(); + }, + + // 试图改变 active 状态 + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + if (editor.cmd.queryCommandState('insertUnOrderedList') || editor.cmd.queryCommandState('insertOrderedList')) { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + menu - justify +*/ +// 构造函数 +function Justify(editor) { + var _this = this; + + this.editor = editor; + this.$elem = $('

                    '); + this.type = 'droplist'; + + // 当前是否 active 状态 + this._active = false; + + // 初始化 droplist + this.droplist = new DropList(this, { + width: 100, + $title: $('

                    对齐方式

                    '), + type: 'list', // droplist 以列表形式展示 + list: [{ $elem: $(' 靠左'), value: 'justifyLeft' }, { $elem: $(' 居中'), value: 'justifyCenter' }, { $elem: $(' 靠右'), value: 'justifyRight' }], + onClick: function onClick(value) { + // 注意 this 是指向当前的 List 对象 + _this._command(value); + } + }); +} + +// 原型 +Justify.prototype = { + constructor: Justify, + + // 执行命令 + _command: function _command(value) { + var editor = this.editor; + editor.cmd.do(value); + } +}; + +/* + menu - Forecolor +*/ +// 构造函数 +function ForeColor(editor) { + var _this = this; + + this.editor = editor; + this.$elem = $('
                    '); + this.type = 'droplist'; + + // 获取配置的颜色 + var config = editor.config; + var colors = config.colors || []; + + // 当前是否 active 状态 + this._active = false; + + // 初始化 droplist + this.droplist = new DropList(this, { + width: 120, + $title: $('

                    文字颜色

                    '), + type: 'inline-block', // droplist 内容以 block 形式展示 + list: colors.map(function (color) { + return { $elem: $(''), value: color }; + }), + onClick: function onClick(value) { + // 注意 this 是指向当前的 ForeColor 对象 + _this._command(value); + } + }); +} + +// 原型 +ForeColor.prototype = { + constructor: ForeColor, + + // 执行命令 + _command: function _command(value) { + var editor = this.editor; + editor.cmd.do('foreColor', value); + } +}; + +/* + menu - BackColor +*/ +// 构造函数 +function BackColor(editor) { + var _this = this; + + this.editor = editor; + this.$elem = $('
                    '); + this.type = 'droplist'; + + // 获取配置的颜色 + var config = editor.config; + var colors = config.colors || []; + + // 当前是否 active 状态 + this._active = false; + + // 初始化 droplist + this.droplist = new DropList(this, { + width: 120, + $title: $('

                    背景色

                    '), + type: 'inline-block', // droplist 内容以 block 形式展示 + list: colors.map(function (color) { + return { $elem: $(''), value: color }; + }), + onClick: function onClick(value) { + // 注意 this 是指向当前的 BackColor 对象 + _this._command(value); + } + }); +} + +// 原型 +BackColor.prototype = { + constructor: BackColor, + + // 执行命令 + _command: function _command(value) { + var editor = this.editor; + editor.cmd.do('backColor', value); + } +}; + +/* + menu - quote +*/ +// 构造函数 +function Quote(editor) { + this.editor = editor; + this.$elem = $('
                    \n \n
                    '); + this.type = 'click'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Quote.prototype = { + constructor: Quote, + + onClick: function onClick(e) { + var editor = this.editor; + var $selectionElem = editor.selection.getSelectionContainerElem(); + var nodeName = $selectionElem.getNodeName(); + + if (!UA.isIE()) { + if (nodeName === 'BLOCKQUOTE') { + // 撤销 quote + editor.cmd.do('formatBlock', '

                    '); + } else { + // 转换为 quote + editor.cmd.do('formatBlock', '

                    '); + } + return; + } + + // IE 中不支持 formatBlock
                    ,要用其他方式兼容 + var content = void 0, + $targetELem = void 0; + if (nodeName === 'P') { + // 将 P 转换为 quote + content = $selectionElem.text(); + $targetELem = $('
                    ' + content + '
                    '); + $targetELem.insertAfter($selectionElem); + $selectionElem.remove(); + return; + } + if (nodeName === 'BLOCKQUOTE') { + // 撤销 quote + content = $selectionElem.text(); + $targetELem = $('

                    ' + content + '

                    '); + $targetELem.insertAfter($selectionElem); + $selectionElem.remove(); + } + }, + + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + var reg = /^BLOCKQUOTE$/i; + var cmdValue = editor.cmd.queryCommandValue('formatBlock'); + if (reg.test(cmdValue)) { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + menu - code +*/ +// 构造函数 +function Code(editor) { + this.editor = editor; + this.$elem = $('
                    \n \n
                    '); + this.type = 'panel'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Code.prototype = { + constructor: Code, + + onClick: function onClick(e) { + var editor = this.editor; + var $startElem = editor.selection.getSelectionStartElem(); + var $endElem = editor.selection.getSelectionEndElem(); + var isSeleEmpty = editor.selection.isSelectionEmpty(); + var selectionText = editor.selection.getSelectionText(); + var $code = void 0; + + if (!$startElem.equal($endElem)) { + // 跨元素选择,不做处理 + editor.selection.restoreSelection(); + return; + } + if (!isSeleEmpty) { + // 选取不是空,用 包裹即可 + $code = $('' + selectionText + ''); + editor.cmd.do('insertElem', $code); + editor.selection.createRangeByElem($code, false); + editor.selection.restoreSelection(); + return; + } + + // 选取是空,且没有夸元素选择,则插入
                    
                    +        if (this._active) {
                    +            // 选中状态,将编辑内容
                    +            this._createPanel($startElem.html());
                    +        } else {
                    +            // 未选中状态,将创建内容
                    +            this._createPanel();
                    +        }
                    +    },
                    +
                    +    _createPanel: function _createPanel(value) {
                    +        var _this = this;
                    +
                    +        // value - 要编辑的内容
                    +        value = value || '';
                    +        var type = !value ? 'new' : 'edit';
                    +        var textId = getRandom('texxt');
                    +        var btnId = getRandom('btn');
                    +
                    +        var panel = new Panel(this, {
                    +            width: 500,
                    +            // 一个 Panel 包含多个 tab
                    +            tabs: [{
                    +                // 标题
                    +                title: '插入代码',
                    +                // 模板
                    +                tpl: '
                    \n \n
                    \n \n
                    \n
                    ', + // 事件绑定 + events: [ + // 插入代码 + { + selector: '#' + btnId, + type: 'click', + fn: function fn() { + var $text = $('#' + textId); + var text = $text.val() || $text.html(); + text = replaceHtmlSymbol(text); + if (type === 'new') { + // 新插入 + _this._insertCode(text); + } else { + // 编辑更新 + _this._updateCode(text); + } + + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }] + } // first tab end + ] // tabs end + }); // new Panel end + + // 显示 panel + panel.show(); + + // 记录属性 + this.panel = panel; + }, + + // 插入代码 + _insertCode: function _insertCode(value) { + var editor = this.editor; + editor.cmd.do('insertHTML', '
                    ' + value + '


                    '); + }, + + // 更新代码 + _updateCode: function _updateCode(value) { + var editor = this.editor; + var $selectionELem = editor.selection.getSelectionContainerElem(); + if (!$selectionELem) { + return; + } + $selectionELem.html(value); + editor.selection.restoreSelection(); + }, + + // 试图改变 active 状态 + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + var $selectionELem = editor.selection.getSelectionContainerElem(); + if (!$selectionELem) { + return; + } + var $parentElem = $selectionELem.parent(); + if ($selectionELem.getNodeName() === 'CODE' && $parentElem.getNodeName() === 'PRE') { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + menu - emoticon +*/ +// 构造函数 +function Emoticon(editor) { + this.editor = editor; + this.$elem = $('
                    \n \n
                    '); + this.type = 'panel'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Emoticon.prototype = { + constructor: Emoticon, + + onClick: function onClick() { + this._createPanel(); + }, + + _createPanel: function _createPanel() { + var _this = this; + + var editor = this.editor; + var config = editor.config; + // 获取表情配置 + var emotions = config.emotions || []; + + // 创建表情 dropPanel 的配置 + var tabConfig = []; + emotions.forEach(function (emotData) { + var emotType = emotData.type; + var content = emotData.content || []; + + // 这一组表情最终拼接出来的 html + var faceHtml = ''; + + // emoji 表情 + if (emotType === 'emoji') { + content.forEach(function (item) { + if (item) { + faceHtml += '' + item + ''; + } + }); + } + // 图片表情 + if (emotType === 'image') { + content.forEach(function (item) { + var src = item.src; + var alt = item.alt; + if (src) { + // 加一个 data-w-e 属性,点击图片的时候不再提示编辑图片 + faceHtml += '' + alt + ''; + } + }); + } + + tabConfig.push({ + title: emotData.title, + tpl: '
                    ' + faceHtml + '
                    ', + events: [{ + selector: 'span.w-e-item', + type: 'click', + fn: function fn(e) { + var target = e.target; + var $target = $(target); + var nodeName = $target.getNodeName(); + + var insertHtml = void 0; + if (nodeName === 'IMG') { + // 插入图片 + insertHtml = $target.parent().html(); + } else { + // 插入 emoji + insertHtml = '' + $target.html() + ''; + } + + _this._insert(insertHtml); + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }] + }); + }); + + var panel = new Panel(this, { + width: 300, + height: 200, + // 一个 Panel 包含多个 tab + tabs: tabConfig + }); + + // 显示 panel + panel.show(); + + // 记录属性 + this.panel = panel; + }, + + // 插入表情 + _insert: function _insert(emotHtml) { + var editor = this.editor; + editor.cmd.do('insertHTML', emotHtml); + } +}; + +/* + menu - table +*/ +// 构造函数 +function Table(editor) { + this.editor = editor; + this.$elem = $('
                    '); + this.type = 'panel'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Table.prototype = { + constructor: Table, + + onClick: function onClick() { + if (this._active) { + // 编辑现有表格 + this._createEditPanel(); + } else { + // 插入新表格 + this._createInsertPanel(); + } + }, + + // 创建插入新表格的 panel + _createInsertPanel: function _createInsertPanel() { + var _this = this; + + // 用到的 id + var btnInsertId = getRandom('btn'); + var textRowNum = getRandom('row'); + var textColNum = getRandom('col'); + + var panel = new Panel(this, { + width: 250, + // panel 包含多个 tab + tabs: [{ + // 标题 + title: '插入表格', + // 模板 + tpl: '
                    \n

                    \n \u521B\u5EFA\n \n \u884C\n \n \u5217\u7684\u8868\u683C\n

                    \n
                    \n \n
                    \n
                    ', + // 事件绑定 + events: [{ + // 点击按钮,插入表格 + selector: '#' + btnInsertId, + type: 'click', + fn: function fn() { + var rowNum = parseInt($('#' + textRowNum).val()); + var colNum = parseInt($('#' + textColNum).val()); + + if (rowNum && colNum && rowNum > 0 && colNum > 0) { + // form 数据有效 + _this._insert(rowNum, colNum); + } + + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }] + } // first tab end + ] // tabs end + }); // panel end + + // 展示 panel + panel.show(); + + // 记录属性 + this.panel = panel; + }, + + // 插入表格 + _insert: function _insert(rowNum, colNum) { + // 拼接 table 模板 + var r = void 0, + c = void 0; + var html = ''; + for (r = 0; r < rowNum; r++) { + html += ''; + if (r === 0) { + for (c = 0; c < colNum; c++) { + html += ''; + } + } else { + for (c = 0; c < colNum; c++) { + html += ''; + } + } + html += ''; + } + html += '
                      


                    '; + + // 执行命令 + var editor = this.editor; + editor.cmd.do('insertHTML', html); + + // 防止 firefox 下出现 resize 的控制点 + editor.cmd.do('enableObjectResizing', false); + editor.cmd.do('enableInlineTableEditing', false); + }, + + // 创建编辑表格的 panel + _createEditPanel: function _createEditPanel() { + var _this2 = this; + + // 可用的 id + var addRowBtnId = getRandom('add-row'); + var addColBtnId = getRandom('add-col'); + var delRowBtnId = getRandom('del-row'); + var delColBtnId = getRandom('del-col'); + var delTableBtnId = getRandom('del-table'); + + // 创建 panel 对象 + var panel = new Panel(this, { + width: 320, + // panel 包含多个 tab + tabs: [{ + // 标题 + title: '编辑表格', + // 模板 + tpl: '
                    \n
                    \n \n \n \n \n
                    \n
                    \n \n \n
                    ', + // 事件绑定 + events: [{ + // 增加行 + selector: '#' + addRowBtnId, + type: 'click', + fn: function fn() { + _this2._addRow(); + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, { + // 增加列 + selector: '#' + addColBtnId, + type: 'click', + fn: function fn() { + _this2._addCol(); + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, { + // 删除行 + selector: '#' + delRowBtnId, + type: 'click', + fn: function fn() { + _this2._delRow(); + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, { + // 删除列 + selector: '#' + delColBtnId, + type: 'click', + fn: function fn() { + _this2._delCol(); + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, { + // 删除表格 + selector: '#' + delTableBtnId, + type: 'click', + fn: function fn() { + _this2._delTable(); + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }] + }] + }); + // 显示 panel + panel.show(); + }, + + // 获取选中的单元格的位置信息 + _getLocationData: function _getLocationData() { + var result = {}; + var editor = this.editor; + var $selectionELem = editor.selection.getSelectionContainerElem(); + if (!$selectionELem) { + return; + } + var nodeName = $selectionELem.getNodeName(); + if (nodeName !== 'TD' && nodeName !== 'TH') { + return; + } + + // 获取 td index + var $tr = $selectionELem.parent(); + var $tds = $tr.children(); + var tdLength = $tds.length; + $tds.forEach(function (td, index) { + if (td === $selectionELem[0]) { + // 记录并跳出循环 + result.td = { + index: index, + elem: td, + length: tdLength + }; + return false; + } + }); + + // 获取 tr index + var $tbody = $tr.parent(); + var $trs = $tbody.children(); + var trLength = $trs.length; + $trs.forEach(function (tr, index) { + if (tr === $tr[0]) { + // 记录并跳出循环 + result.tr = { + index: index, + elem: tr, + length: trLength + }; + return false; + } + }); + + // 返回结果 + return result; + }, + + // 增加行 + _addRow: function _addRow() { + // 获取当前单元格的位置信息 + var locationData = this._getLocationData(); + if (!locationData) { + return; + } + var trData = locationData.tr; + var $currentTr = $(trData.elem); + var tdData = locationData.td; + var tdLength = tdData.length; + + // 拼接即将插入的字符串 + var newTr = document.createElement('tr'); + var tpl = '', + i = void 0; + for (i = 0; i < tdLength; i++) { + tpl += ' '; + } + newTr.innerHTML = tpl; + // 插入 + $(newTr).insertAfter($currentTr); + }, + + // 增加列 + _addCol: function _addCol() { + // 获取当前单元格的位置信息 + var locationData = this._getLocationData(); + if (!locationData) { + return; + } + var trData = locationData.tr; + var tdData = locationData.td; + var tdIndex = tdData.index; + var $currentTr = $(trData.elem); + var $trParent = $currentTr.parent(); + var $trs = $trParent.children(); + + // 遍历所有行 + $trs.forEach(function (tr) { + var $tr = $(tr); + var $tds = $tr.children(); + var $currentTd = $tds.get(tdIndex); + var name = $currentTd.getNodeName().toLowerCase(); + + // new 一个 td,并插入 + var newTd = document.createElement(name); + $(newTd).insertAfter($currentTd); + }); + }, + + // 删除行 + _delRow: function _delRow() { + // 获取当前单元格的位置信息 + var locationData = this._getLocationData(); + if (!locationData) { + return; + } + var trData = locationData.tr; + var $currentTr = $(trData.elem); + $currentTr.remove(); + }, + + // 删除列 + _delCol: function _delCol() { + // 获取当前单元格的位置信息 + var locationData = this._getLocationData(); + if (!locationData) { + return; + } + var trData = locationData.tr; + var tdData = locationData.td; + var tdIndex = tdData.index; + var $currentTr = $(trData.elem); + var $trParent = $currentTr.parent(); + var $trs = $trParent.children(); + + // 遍历所有行 + $trs.forEach(function (tr) { + var $tr = $(tr); + var $tds = $tr.children(); + var $currentTd = $tds.get(tdIndex); + // 删除 + $currentTd.remove(); + }); + }, + + // 删除表格 + _delTable: function _delTable() { + var editor = this.editor; + var $selectionELem = editor.selection.getSelectionContainerElem(); + if (!$selectionELem) { + return; + } + var $table = $selectionELem.parentUntil('table'); + if (!$table) { + return; + } + $table.remove(); + }, + + // 试图改变 active 状态 + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + var $selectionELem = editor.selection.getSelectionContainerElem(); + if (!$selectionELem) { + return; + } + var nodeName = $selectionELem.getNodeName(); + if (nodeName === 'TD' || nodeName === 'TH') { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + menu - video +*/ +// 构造函数 +function Video(editor) { + this.editor = editor; + this.$elem = $('
                    '); + this.type = 'panel'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Video.prototype = { + constructor: Video, + + onClick: function onClick() { + this._createPanel(); + }, + + _createPanel: function _createPanel() { + var _this = this; + + // 创建 id + var textValId = getRandom('text-val'); + var btnId = getRandom('btn'); + + // 创建 panel + var panel = new Panel(this, { + width: 350, + // 一个 panel 多个 tab + tabs: [{ + // 标题 + title: '插入视频', + // 模板 + tpl: '
                    \n \n
                    \n \n
                    \n
                    ', + // 事件绑定 + events: [{ + selector: '#' + btnId, + type: 'click', + fn: function fn() { + var $text = $('#' + textValId); + var val = $text.val().trim(); + + // 测试用视频地址 + // + + if (val) { + // 插入视频 + _this._insert(val); + } + + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }] + } // first tab end + ] // tabs end + }); // panel end + + // 显示 panel + panel.show(); + + // 记录属性 + this.panel = panel; + }, + + // 插入视频 + _insert: function _insert(val) { + var editor = this.editor; + editor.cmd.do('insertHTML', val + '


                    '); + } +}; + +/* + menu - img +*/ +// 构造函数 +function Image(editor) { + this.editor = editor; + var imgMenuId = getRandom('w-e-img'); + this.$elem = $('
                    '); + editor.imgMenuId = imgMenuId; + this.type = 'panel'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Image.prototype = { + constructor: Image, + + onClick: function onClick() { + var editor = this.editor; + var config = editor.config; + if (config.qiniu) { + return; + } + if (this._active) { + this._createEditPanel(); + } else { + this._createInsertPanel(); + } + }, + + _createEditPanel: function _createEditPanel() { + var editor = this.editor; + + // id + var width30 = getRandom('width-30'); + var width50 = getRandom('width-50'); + var width100 = getRandom('width-100'); + var delBtn = getRandom('del-btn'); + + // tab 配置 + var tabsConfig = [{ + title: '编辑图片', + tpl: '
                    \n
                    \n \u6700\u5927\u5BBD\u5EA6\uFF1A\n \n \n \n
                    \n
                    \n \n \n
                    ', + events: [{ + selector: '#' + width30, + type: 'click', + fn: function fn() { + var $img = editor._selectedImg; + if ($img) { + $img.css('max-width', '30%'); + } + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, { + selector: '#' + width50, + type: 'click', + fn: function fn() { + var $img = editor._selectedImg; + if ($img) { + $img.css('max-width', '50%'); + } + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, { + selector: '#' + width100, + type: 'click', + fn: function fn() { + var $img = editor._selectedImg; + if ($img) { + $img.css('max-width', '100%'); + } + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, { + selector: '#' + delBtn, + type: 'click', + fn: function fn() { + var $img = editor._selectedImg; + if ($img) { + $img.remove(); + } + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }] + }]; + + // 创建 panel 并显示 + var panel = new Panel(this, { + width: 300, + tabs: tabsConfig + }); + panel.show(); + + // 记录属性 + this.panel = panel; + }, + + _createInsertPanel: function _createInsertPanel() { + var editor = this.editor; + var uploadImg = editor.uploadImg; + var config = editor.config; + + // id + var upTriggerId = getRandom('up-trigger'); + var upFileId = getRandom('up-file'); + var linkUrlId = getRandom('link-url'); + var linkBtnId = getRandom('link-btn'); + + // tabs 的配置 + var tabsConfig = [{ + title: '上传图片', + tpl: '
                    \n
                    \n \n
                    \n
                    \n \n
                    \n
                    ', + events: [{ + // 触发选择图片 + selector: '#' + upTriggerId, + type: 'click', + fn: function fn() { + var $file = $('#' + upFileId); + var fileElem = $file[0]; + if (fileElem) { + fileElem.click(); + } else { + // 返回 true 可关闭 panel + return true; + } + } + }, { + // 选择图片完毕 + selector: '#' + upFileId, + type: 'change', + fn: function fn() { + var $file = $('#' + upFileId); + var fileElem = $file[0]; + if (!fileElem) { + // 返回 true 可关闭 panel + return true; + } + + // 获取选中的 file 对象列表 + var fileList = fileElem.files; + if (fileList.length) { + uploadImg.uploadImg(fileList); + } + + // 返回 true 可关闭 panel + return true; + } + }] + }, // first tab end + { + title: '网络图片', + tpl: '
                    \n \n
                    \n \n
                    \n
                    ', + events: [{ + selector: '#' + linkBtnId, + type: 'click', + fn: function fn() { + var $linkUrl = $('#' + linkUrlId); + var url = $linkUrl.val().trim(); + + if (url) { + uploadImg.insertLinkImg(url); + } + + // 返回 true 表示函数执行结束之后关闭 panel + return true; + } + }] + } // second tab end + ]; // tabs end + + // 判断 tabs 的显示 + var tabsConfigResult = []; + if ((config.uploadImgShowBase64 || config.uploadImgServer || config.customUploadImg) && window.FileReader) { + // 显示“上传图片” + tabsConfigResult.push(tabsConfig[0]); + } + if (config.showLinkImg) { + // 显示“网络图片” + tabsConfigResult.push(tabsConfig[1]); + } + + // 创建 panel 并显示 + var panel = new Panel(this, { + width: 300, + tabs: tabsConfigResult + }); + panel.show(); + + // 记录属性 + this.panel = panel; + }, + + // 试图改变 active 状态 + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + if (editor._selectedImg) { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + 所有菜单的汇总 +*/ + +// 存储菜单的构造函数 +var MenuConstructors = {}; + +MenuConstructors.bold = Bold; + +MenuConstructors.head = Head; + +MenuConstructors.fontSize = FontSize; + +MenuConstructors.fontName = FontName; + +MenuConstructors.link = Link; + +MenuConstructors.italic = Italic; + +MenuConstructors.redo = Redo; + +MenuConstructors.strikeThrough = StrikeThrough; + +MenuConstructors.underline = Underline; + +MenuConstructors.undo = Undo; + +MenuConstructors.list = List; + +MenuConstructors.justify = Justify; + +MenuConstructors.foreColor = ForeColor; + +MenuConstructors.backColor = BackColor; + +MenuConstructors.quote = Quote; + +MenuConstructors.code = Code; + +MenuConstructors.emoticon = Emoticon; + +MenuConstructors.table = Table; + +MenuConstructors.video = Video; + +MenuConstructors.image = Image; + +/* + 菜单集合 +*/ +// 构造函数 +function Menus(editor) { + this.editor = editor; + this.menus = {}; +} + +// 修改原型 +Menus.prototype = { + constructor: Menus, + + // 初始化菜单 + init: function init() { + var _this = this; + + var editor = this.editor; + var config = editor.config || {}; + var configMenus = config.menus || []; // 获取配置中的菜单 + + // 根据配置信息,创建菜单 + configMenus.forEach(function (menuKey) { + var MenuConstructor = MenuConstructors[menuKey]; + if (MenuConstructor && typeof MenuConstructor === 'function') { + // 创建单个菜单 + _this.menus[menuKey] = new MenuConstructor(editor); + } + }); + + // 添加到菜单栏 + this._addToToolbar(); + + // 绑定事件 + this._bindEvent(); + }, + + // 添加到菜单栏 + _addToToolbar: function _addToToolbar() { + var editor = this.editor; + var $toolbarElem = editor.$toolbarElem; + var menus = this.menus; + var config = editor.config; + // config.zIndex 是配置的编辑区域的 z-index,菜单的 z-index 得在其基础上 +1 + var zIndex = config.zIndex + 1; + objForEach(menus, function (key, menu) { + var $elem = menu.$elem; + if ($elem) { + // 设置 z-index + $elem.css('z-index', zIndex); + $toolbarElem.append($elem); + } + }); + }, + + // 绑定菜单 click mouseenter 事件 + _bindEvent: function _bindEvent() { + var menus = this.menus; + var editor = this.editor; + objForEach(menus, function (key, menu) { + var type = menu.type; + if (!type) { + return; + } + var $elem = menu.$elem; + var droplist = menu.droplist; + var panel = menu.panel; + + // 点击类型,例如 bold + if (type === 'click' && menu.onClick) { + $elem.on('click', function (e) { + if (editor.selection.getRange() == null) { + return; + } + menu.onClick(e); + }); + } + + // 下拉框,例如 head + if (type === 'droplist' && droplist) { + $elem.on('mouseenter', function (e) { + if (editor.selection.getRange() == null) { + return; + } + // 显示 + droplist.showTimeoutId = setTimeout(function () { + droplist.show(); + }, 200); + }).on('mouseleave', function (e) { + // 隐藏 + droplist.hideTimeoutId = setTimeout(function () { + droplist.hide(); + }, 0); + }); + } + + // 弹框类型,例如 link + if (type === 'panel' && menu.onClick) { + $elem.on('click', function (e) { + e.stopPropagation(); + if (editor.selection.getRange() == null) { + return; + } + // 在自定义事件中显示 panel + menu.onClick(e); + }); + } + }); + }, + + // 尝试修改菜单状态 + changeActive: function changeActive() { + var menus = this.menus; + objForEach(menus, function (key, menu) { + if (menu.tryChangeActive) { + setTimeout(function () { + menu.tryChangeActive(); + }, 100); + } + }); + } +}; + +/* + 粘贴信息的处理 +*/ + +// 获取粘贴的纯文本 +function getPasteText(e) { + var clipboardData = e.clipboardData || e.originalEvent && e.originalEvent.clipboardData; + var pasteText = void 0; + if (clipboardData == null) { + pasteText = window.clipboardData && window.clipboardData.getData('text'); + } else { + pasteText = clipboardData.getData('text/plain'); + } + + return replaceHtmlSymbol(pasteText); +} + +// 获取粘贴的html +function getPasteHtml(e, filterStyle, ignoreImg) { + var clipboardData = e.clipboardData || e.originalEvent && e.originalEvent.clipboardData; + var pasteText = void 0, + pasteHtml = void 0; + if (clipboardData == null) { + pasteText = window.clipboardData && window.clipboardData.getData('text'); + } else { + pasteText = clipboardData.getData('text/plain'); + pasteHtml = clipboardData.getData('text/html'); + } + if (!pasteHtml && pasteText) { + pasteHtml = '

                    ' + replaceHtmlSymbol(pasteText) + '

                    '; + } + if (!pasteHtml) { + return; + } + + // 过滤word中状态过来的无用字符 + var docSplitHtml = pasteHtml.split(''); + if (docSplitHtml.length === 2) { + pasteHtml = docSplitHtml[0]; + } + + // 过滤无用标签 + pasteHtml = pasteHtml.replace(/<(meta|script|link).+?>/igm, ''); + // 去掉注释 + pasteHtml = pasteHtml.replace(//mg, ''); + // 过滤 data-xxx 属性 + pasteHtml = pasteHtml.replace(/\s?data-.+?=('|").+?('|")/igm, ''); + + if (ignoreImg) { + // 忽略图片 + pasteHtml = pasteHtml.replace(//igm, ''); + } + + if (filterStyle) { + // 过滤样式 + pasteHtml = pasteHtml.replace(/\s?(class|style)=('|").*?('|")/igm, ''); + } else { + // 保留样式 + pasteHtml = pasteHtml.replace(/\s?class=('|").*?('|")/igm, ''); + } + + return pasteHtml; +} + +// 获取粘贴的图片文件 +function getPasteImgs(e) { + var result = []; + var txt = getPasteText(e); + if (txt) { + // 有文字,就忽略图片 + return result; + } + + var clipboardData = e.clipboardData || e.originalEvent && e.originalEvent.clipboardData || {}; + var items = clipboardData.items; + if (!items) { + return result; + } + + objForEach(items, function (key, value) { + var type = value.type; + if (/image/i.test(type)) { + result.push(value.getAsFile()); + } + }); + + return result; +} + +/* + 编辑区域 +*/ + +// 获取一个 elem.childNodes 的 JSON 数据 +function getChildrenJSON($elem) { + var result = []; + var $children = $elem.childNodes() || []; // 注意 childNodes() 可以获取文本节点 + $children.forEach(function (curElem) { + var elemResult = void 0; + var nodeType = curElem.nodeType; + + // 文本节点 + if (nodeType === 3) { + elemResult = curElem.textContent; + elemResult = replaceHtmlSymbol(elemResult); + } + + // 普通 DOM 节点 + if (nodeType === 1) { + elemResult = {}; + + // tag + elemResult.tag = curElem.nodeName.toLowerCase(); + // attr + var attrData = []; + var attrList = curElem.attributes || {}; + var attrListLength = attrList.length || 0; + for (var i = 0; i < attrListLength; i++) { + var attr = attrList[i]; + attrData.push({ + name: attr.name, + value: attr.value + }); + } + elemResult.attrs = attrData; + // children(递归) + elemResult.children = getChildrenJSON($(curElem)); + } + + result.push(elemResult); + }); + return result; +} + +// 构造函数 +function Text(editor) { + this.editor = editor; +} + +// 修改原型 +Text.prototype = { + constructor: Text, + + // 初始化 + init: function init() { + // 绑定事件 + this._bindEvent(); + }, + + // 清空内容 + clear: function clear() { + this.html('


                    '); + }, + + // 获取 设置 html + html: function html(val) { + var editor = this.editor; + var $textElem = editor.$textElem; + var html = void 0; + if (val == null) { + html = $textElem.html(); + // 未选中任何内容的时候点击“加粗”或者“斜体”等按钮,就得需要一个空的占位符 ​ ,这里替换掉 + html = html.replace(/\u200b/gm, ''); + return html; + } else { + $textElem.html(val); + + // 初始化选取,将光标定位到内容尾部 + editor.initSelection(); + } + }, + + // 获取 JSON + getJSON: function getJSON() { + var editor = this.editor; + var $textElem = editor.$textElem; + return getChildrenJSON($textElem); + }, + + // 获取 设置 text + text: function text(val) { + var editor = this.editor; + var $textElem = editor.$textElem; + var text = void 0; + if (val == null) { + text = $textElem.text(); + // 未选中任何内容的时候点击“加粗”或者“斜体”等按钮,就得需要一个空的占位符 ​ ,这里替换掉 + text = text.replace(/\u200b/gm, ''); + return text; + } else { + $textElem.text('

                    ' + val + '

                    '); + + // 初始化选取,将光标定位到内容尾部 + editor.initSelection(); + } + }, + + // 追加内容 + append: function append(html) { + var editor = this.editor; + var $textElem = editor.$textElem; + $textElem.append($(html)); + + // 初始化选取,将光标定位到内容尾部 + editor.initSelection(); + }, + + // 绑定事件 + _bindEvent: function _bindEvent() { + // 实时保存选取 + this._saveRangeRealTime(); + + // 按回车建时的特殊处理 + this._enterKeyHandle(); + + // 清空时保留


                    + this._clearHandle(); + + // 粘贴事件(粘贴文字,粘贴图片) + this._pasteHandle(); + + // tab 特殊处理 + this._tabHandle(); + + // img 点击 + this._imgHandle(); + + // 拖拽事件 + this._dragHandle(); + }, + + // 实时保存选取 + _saveRangeRealTime: function _saveRangeRealTime() { + var editor = this.editor; + var $textElem = editor.$textElem; + + // 保存当前的选区 + function saveRange(e) { + // 随时保存选区 + editor.selection.saveRange(); + // 更新按钮 ative 状态 + editor.menus.changeActive(); + } + // 按键后保存 + $textElem.on('keyup', saveRange); + $textElem.on('mousedown', function (e) { + // mousedown 状态下,鼠标滑动到编辑区域外面,也需要保存选区 + $textElem.on('mouseleave', saveRange); + }); + $textElem.on('mouseup', function (e) { + saveRange(); + // 在编辑器区域之内完成点击,取消鼠标滑动到编辑区外面的事件 + $textElem.off('mouseleave', saveRange); + }); + }, + + // 按回车键时的特殊处理 + _enterKeyHandle: function _enterKeyHandle() { + var editor = this.editor; + var $textElem = editor.$textElem; + + function insertEmptyP($selectionElem) { + var $p = $('


                    '); + $p.insertBefore($selectionElem); + editor.selection.createRangeByElem($p, true); + editor.selection.restoreSelection(); + $selectionElem.remove(); + } + + // 将回车之后生成的非

                    的顶级标签,改为

                    + function pHandle(e) { + var $selectionElem = editor.selection.getSelectionContainerElem(); + var $parentElem = $selectionElem.parent(); + + if ($parentElem.html() === '
                    ') { + // 回车之前光标所在一个

                    .....

                    ,忽然回车生成一个空的


                    + // 而且继续回车跳不出去,因此只能特殊处理 + insertEmptyP($selectionElem); + return; + } + + if (!$parentElem.equal($textElem)) { + // 不是顶级标签 + return; + } + + var nodeName = $selectionElem.getNodeName(); + if (nodeName === 'P') { + // 当前的标签是 P ,不用做处理 + return; + } + + if ($selectionElem.text()) { + // 有内容,不做处理 + return; + } + + // 插入

                    ,并将选取定位到

                    ,删除当前标签 + insertEmptyP($selectionElem); + } + + $textElem.on('keyup', function (e) { + if (e.keyCode !== 13) { + // 不是回车键 + return; + } + // 将回车之后生成的非

                    的顶级标签,改为

                    + pHandle(e); + }); + + //

                    回车时 特殊处理 + function codeHandle(e) { + var $selectionElem = editor.selection.getSelectionContainerElem(); + if (!$selectionElem) { + return; + } + var $parentElem = $selectionElem.parent(); + var selectionNodeName = $selectionElem.getNodeName(); + var parentNodeName = $parentElem.getNodeName(); + + if (selectionNodeName !== 'CODE' || parentNodeName !== 'PRE') { + // 不符合要求 忽略 + return; + } + + if (!editor.cmd.queryCommandSupported('insertHTML')) { + // 必须原生支持 insertHTML 命令 + return; + } + + // 处理:光标定位到代码末尾,联系点击两次回车,即跳出代码块 + if (editor._willBreakCode === true) { + // 此时可以跳出代码块 + // 插入

                    ,并将选取定位到

                    + var $p = $('


                    '); + $p.insertAfter($parentElem); + editor.selection.createRangeByElem($p, true); + editor.selection.restoreSelection(); + + // 修改状态 + editor._willBreakCode = false; + + e.preventDefault(); + return; + } + + var _startOffset = editor.selection.getRange().startOffset; + + // 处理:回车时,不能插入
                    而是插入 \n ,因为是在 pre 标签里面 + editor.cmd.do('insertHTML', '\n'); + editor.selection.saveRange(); + if (editor.selection.getRange().startOffset === _startOffset) { + // 没起作用,再来一遍 + editor.cmd.do('insertHTML', '\n'); + } + + var codeLength = $selectionElem.html().length; + if (editor.selection.getRange().startOffset + 1 === codeLength) { + // 说明光标在代码最后的位置,执行了回车操作 + // 记录下来,以便下次回车时候跳出 code + editor._willBreakCode = true; + } + + // 阻止默认行为 + e.preventDefault(); + } + + $textElem.on('keydown', function (e) { + if (e.keyCode !== 13) { + // 不是回车键 + // 取消即将跳转代码块的记录 + editor._willBreakCode = false; + return; + } + //
                    回车时 特殊处理 + codeHandle(e); + }); + }, + + // 清空时保留


                    + _clearHandle: function _clearHandle() { + var editor = this.editor; + var $textElem = editor.$textElem; + + $textElem.on('keydown', function (e) { + if (e.keyCode !== 8) { + return; + } + var txtHtml = $textElem.html().toLowerCase().trim(); + if (txtHtml === '


                    ') { + // 最后剩下一个空行,就不再删除了 + e.preventDefault(); + return; + } + }); + + $textElem.on('keyup', function (e) { + if (e.keyCode !== 8) { + return; + } + var $p = void 0; + var txtHtml = $textElem.html().toLowerCase().trim(); + + // firefox 时用 txtHtml === '
                    ' 判断,其他用 !txtHtml 判断 + if (!txtHtml || txtHtml === '
                    ') { + // 内容空了 + $p = $('


                    '); + $textElem.html(''); // 一定要先清空,否则在 firefox 下有问题 + $textElem.append($p); + editor.selection.createRangeByElem($p, false, true); + editor.selection.restoreSelection(); + } + }); + }, + + // 粘贴事件(粘贴文字 粘贴图片) + _pasteHandle: function _pasteHandle() { + var editor = this.editor; + var config = editor.config; + var pasteFilterStyle = config.pasteFilterStyle; + var pasteTextHandle = config.pasteTextHandle; + var ignoreImg = config.pasteIgnoreImg; + var $textElem = editor.$textElem; + + // 粘贴图片、文本的事件,每次只能执行一个 + // 判断该次粘贴事件是否可以执行 + var pasteTime = 0; + function canDo() { + var now = Date.now(); + var flag = false; + if (now - pasteTime >= 100) { + // 间隔大于 100 ms ,可以执行 + flag = true; + } + pasteTime = now; + return flag; + } + function resetTime() { + pasteTime = 0; + } + + // 粘贴文字 + $textElem.on('paste', function (e) { + if (UA.isIE()) { + return; + } else { + // 阻止默认行为,使用 execCommand 的粘贴命令 + e.preventDefault(); + } + + // 粘贴图片和文本,只能同时使用一个 + if (!canDo()) { + return; + } + + // 获取粘贴的文字 + var pasteHtml = getPasteHtml(e, pasteFilterStyle, ignoreImg); + var pasteText = getPasteText(e); + pasteText = pasteText.replace(/\n/gm, '
                    '); + + var $selectionElem = editor.selection.getSelectionContainerElem(); + if (!$selectionElem) { + return; + } + var nodeName = $selectionElem.getNodeName(); + + // code 中只能粘贴纯文本 + if (nodeName === 'CODE' || nodeName === 'PRE') { + if (pasteTextHandle && isFunction(pasteTextHandle)) { + // 用户自定义过滤处理粘贴内容 + pasteText = '' + (pasteTextHandle(pasteText) || ''); + } + editor.cmd.do('insertHTML', '

                    ' + pasteText + '

                    '); + return; + } + + // 先放开注释,有问题再追查 ———— + // // 表格中忽略,可能会出现异常问题 + // if (nodeName === 'TD' || nodeName === 'TH') { + // return + // } + + if (!pasteHtml) { + // 没有内容,可继续执行下面的图片粘贴 + resetTime(); + return; + } + try { + // firefox 中,获取的 pasteHtml 可能是没有
                      包裹的
                    • + // 因此执行 insertHTML 会报错 + if (pasteTextHandle && isFunction(pasteTextHandle)) { + // 用户自定义过滤处理粘贴内容 + pasteHtml = '' + (pasteTextHandle(pasteHtml) || ''); + } + editor.cmd.do('insertHTML', pasteHtml); + } catch (ex) { + // 此时使用 pasteText 来兼容一下 + if (pasteTextHandle && isFunction(pasteTextHandle)) { + // 用户自定义过滤处理粘贴内容 + pasteText = '' + (pasteTextHandle(pasteText) || ''); + } + editor.cmd.do('insertHTML', '

                      ' + pasteText + '

                      '); + } + }); + + // 粘贴图片 + $textElem.on('paste', function (e) { + if (UA.isIE()) { + return; + } else { + e.preventDefault(); + } + + // 粘贴图片和文本,只能同时使用一个 + if (!canDo()) { + return; + } + + // 获取粘贴的图片 + var pasteFiles = getPasteImgs(e); + if (!pasteFiles || !pasteFiles.length) { + return; + } + + // 获取当前的元素 + var $selectionElem = editor.selection.getSelectionContainerElem(); + if (!$selectionElem) { + return; + } + var nodeName = $selectionElem.getNodeName(); + + // code 中粘贴忽略 + if (nodeName === 'CODE' || nodeName === 'PRE') { + return; + } + + // 上传图片 + var uploadImg = editor.uploadImg; + uploadImg.uploadImg(pasteFiles); + }); + }, + + // tab 特殊处理 + _tabHandle: function _tabHandle() { + var editor = this.editor; + var $textElem = editor.$textElem; + + $textElem.on('keydown', function (e) { + if (e.keyCode !== 9) { + return; + } + if (!editor.cmd.queryCommandSupported('insertHTML')) { + // 必须原生支持 insertHTML 命令 + return; + } + var $selectionElem = editor.selection.getSelectionContainerElem(); + if (!$selectionElem) { + return; + } + var $parentElem = $selectionElem.parent(); + var selectionNodeName = $selectionElem.getNodeName(); + var parentNodeName = $parentElem.getNodeName(); + + if (selectionNodeName === 'CODE' && parentNodeName === 'PRE') { + //
                       里面
                      +                editor.cmd.do('insertHTML', '    ');
                      +            } else {
                      +                // 普通文字
                      +                editor.cmd.do('insertHTML', '    ');
                      +            }
                      +
                      +            e.preventDefault();
                      +        });
                      +    },
                      +
                      +    // img 点击
                      +    _imgHandle: function _imgHandle() {
                      +        var editor = this.editor;
                      +        var $textElem = editor.$textElem;
                      +
                      +        // 为图片增加 selected 样式
                      +        $textElem.on('click', 'img', function (e) {
                      +            var img = this;
                      +            var $img = $(img);
                      +
                      +            if ($img.attr('data-w-e') === '1') {
                      +                // 是表情图片,忽略
                      +                return;
                      +            }
                      +
                      +            // 记录当前点击过的图片
                      +            editor._selectedImg = $img;
                      +
                      +            // 修改选区并 restore ,防止用户此时点击退格键,会删除其他内容
                      +            editor.selection.createRangeByElem($img);
                      +            editor.selection.restoreSelection();
                      +        });
                      +
                      +        // 去掉图片的 selected 样式
                      +        $textElem.on('click  keyup', function (e) {
                      +            if (e.target.matches('img')) {
                      +                // 点击的是图片,忽略
                      +                return;
                      +            }
                      +            // 删除记录
                      +            editor._selectedImg = null;
                      +        });
                      +    },
                      +
                      +    // 拖拽事件
                      +    _dragHandle: function _dragHandle() {
                      +        var editor = this.editor;
                      +
                      +        // 禁用 document 拖拽事件
                      +        var $document = $(document);
                      +        $document.on('dragleave drop dragenter dragover', function (e) {
                      +            e.preventDefault();
                      +        });
                      +
                      +        // 添加编辑区域拖拽事件
                      +        var $textElem = editor.$textElem;
                      +        $textElem.on('drop', function (e) {
                      +            e.preventDefault();
                      +            var files = e.dataTransfer && e.dataTransfer.files;
                      +            if (!files || !files.length) {
                      +                return;
                      +            }
                      +
                      +            // 上传图片
                      +            var uploadImg = editor.uploadImg;
                      +            uploadImg.uploadImg(files);
                      +        });
                      +    }
                      +};
                      +
                      +/*
                      +    命令,封装 document.execCommand
                      +*/
                      +
                      +// 构造函数
                      +function Command(editor) {
                      +    this.editor = editor;
                      +}
                      +
                      +// 修改原型
                      +Command.prototype = {
                      +    constructor: Command,
                      +
                      +    // 执行命令
                      +    do: function _do(name, value) {
                      +        var editor = this.editor;
                      +
                      +        // 使用 styleWithCSS
                      +        if (!editor._useStyleWithCSS) {
                      +            document.execCommand('styleWithCSS', null, true);
                      +            editor._useStyleWithCSS = true;
                      +        }
                      +
                      +        // 如果无选区,忽略
                      +        if (!editor.selection.getRange()) {
                      +            return;
                      +        }
                      +
                      +        // 恢复选取
                      +        editor.selection.restoreSelection();
                      +
                      +        // 执行
                      +        var _name = '_' + name;
                      +        if (this[_name]) {
                      +            // 有自定义事件
                      +            this[_name](value);
                      +        } else {
                      +            // 默认 command
                      +            this._execCommand(name, value);
                      +        }
                      +
                      +        // 修改菜单状态
                      +        editor.menus.changeActive();
                      +
                      +        // 最后,恢复选取保证光标在原来的位置闪烁
                      +        editor.selection.saveRange();
                      +        editor.selection.restoreSelection();
                      +
                      +        // 触发 onchange
                      +        editor.change && editor.change();
                      +    },
                      +
                      +    // 自定义 insertHTML 事件
                      +    _insertHTML: function _insertHTML(html) {
                      +        var editor = this.editor;
                      +        var range = editor.selection.getRange();
                      +
                      +        if (this.queryCommandSupported('insertHTML')) {
                      +            // W3C
                      +            this._execCommand('insertHTML', html);
                      +        } else if (range.insertNode) {
                      +            // IE
                      +            range.deleteContents();
                      +            range.insertNode($(html)[0]);
                      +        } else if (range.pasteHTML) {
                      +            // IE <= 10
                      +            range.pasteHTML(html);
                      +        }
                      +    },
                      +
                      +    // 插入 elem
                      +    _insertElem: function _insertElem($elem) {
                      +        var editor = this.editor;
                      +        var range = editor.selection.getRange();
                      +
                      +        if (range.insertNode) {
                      +            range.deleteContents();
                      +            range.insertNode($elem[0]);
                      +        }
                      +    },
                      +
                      +    // 封装 execCommand
                      +    _execCommand: function _execCommand(name, value) {
                      +        document.execCommand(name, false, value);
                      +    },
                      +
                      +    // 封装 document.queryCommandValue
                      +    queryCommandValue: function queryCommandValue(name) {
                      +        return document.queryCommandValue(name);
                      +    },
                      +
                      +    // 封装 document.queryCommandState
                      +    queryCommandState: function queryCommandState(name) {
                      +        return document.queryCommandState(name);
                      +    },
                      +
                      +    // 封装 document.queryCommandSupported
                      +    queryCommandSupported: function queryCommandSupported(name) {
                      +        return document.queryCommandSupported(name);
                      +    }
                      +};
                      +
                      +/*
                      +    selection range API
                      +*/
                      +
                      +// 构造函数
                      +function API(editor) {
                      +    this.editor = editor;
                      +    this._currentRange = null;
                      +}
                      +
                      +// 修改原型
                      +API.prototype = {
                      +    constructor: API,
                      +
                      +    // 获取 range 对象
                      +    getRange: function getRange() {
                      +        return this._currentRange;
                      +    },
                      +
                      +    // 保存选区
                      +    saveRange: function saveRange(_range) {
                      +        if (_range) {
                      +            // 保存已有选区
                      +            this._currentRange = _range;
                      +            return;
                      +        }
                      +
                      +        // 获取当前的选区
                      +        var selection = window.getSelection();
                      +        if (selection.rangeCount === 0) {
                      +            return;
                      +        }
                      +        var range = selection.getRangeAt(0);
                      +
                      +        // 判断选区内容是否在编辑内容之内
                      +        var $containerElem = this.getSelectionContainerElem(range);
                      +        if (!$containerElem) {
                      +            return;
                      +        }
                      +
                      +        // 判断选区内容是否在不可编辑区域之内
                      +        if ($containerElem.attr('contenteditable') === 'false' || $containerElem.parentUntil('[contenteditable=false]')) {
                      +            return;
                      +        }
                      +
                      +        var editor = this.editor;
                      +        var $textElem = editor.$textElem;
                      +        if ($textElem.isContain($containerElem)) {
                      +            // 是编辑内容之内的
                      +            this._currentRange = range;
                      +        }
                      +    },
                      +
                      +    // 折叠选区
                      +    collapseRange: function collapseRange(toStart) {
                      +        if (toStart == null) {
                      +            // 默认为 false
                      +            toStart = false;
                      +        }
                      +        var range = this._currentRange;
                      +        if (range) {
                      +            range.collapse(toStart);
                      +        }
                      +    },
                      +
                      +    // 选中区域的文字
                      +    getSelectionText: function getSelectionText() {
                      +        var range = this._currentRange;
                      +        if (range) {
                      +            return this._currentRange.toString();
                      +        } else {
                      +            return '';
                      +        }
                      +    },
                      +
                      +    // 选区的 $Elem
                      +    getSelectionContainerElem: function getSelectionContainerElem(range) {
                      +        range = range || this._currentRange;
                      +        var elem = void 0;
                      +        if (range) {
                      +            elem = range.commonAncestorContainer;
                      +            return $(elem.nodeType === 1 ? elem : elem.parentNode);
                      +        }
                      +    },
                      +    getSelectionStartElem: function getSelectionStartElem(range) {
                      +        range = range || this._currentRange;
                      +        var elem = void 0;
                      +        if (range) {
                      +            elem = range.startContainer;
                      +            return $(elem.nodeType === 1 ? elem : elem.parentNode);
                      +        }
                      +    },
                      +    getSelectionEndElem: function getSelectionEndElem(range) {
                      +        range = range || this._currentRange;
                      +        var elem = void 0;
                      +        if (range) {
                      +            elem = range.endContainer;
                      +            return $(elem.nodeType === 1 ? elem : elem.parentNode);
                      +        }
                      +    },
                      +
                      +    // 选区是否为空
                      +    isSelectionEmpty: function isSelectionEmpty() {
                      +        var range = this._currentRange;
                      +        if (range && range.startContainer) {
                      +            if (range.startContainer === range.endContainer) {
                      +                if (range.startOffset === range.endOffset) {
                      +                    return true;
                      +                }
                      +            }
                      +        }
                      +        return false;
                      +    },
                      +
                      +    // 恢复选区
                      +    restoreSelection: function restoreSelection() {
                      +        var selection = window.getSelection();
                      +        selection.removeAllRanges();
                      +        selection.addRange(this._currentRange);
                      +    },
                      +
                      +    // 创建一个空白(即 ​ 字符)选区
                      +    createEmptyRange: function createEmptyRange() {
                      +        var editor = this.editor;
                      +        var range = this.getRange();
                      +        var $elem = void 0;
                      +
                      +        if (!range) {
                      +            // 当前无 range
                      +            return;
                      +        }
                      +        if (!this.isSelectionEmpty()) {
                      +            // 当前选区必须没有内容才可以
                      +            return;
                      +        }
                      +
                      +        try {
                      +            // 目前只支持 webkit 内核
                      +            if (UA.isWebkit()) {
                      +                // 插入 ​
                      +                editor.cmd.do('insertHTML', '​');
                      +                // 修改 offset 位置
                      +                range.setEnd(range.endContainer, range.endOffset + 1);
                      +                // 存储
                      +                this.saveRange(range);
                      +            } else {
                      +                $elem = $('');
                      +                editor.cmd.do('insertElem', $elem);
                      +                this.createRangeByElem($elem, true);
                      +            }
                      +        } catch (ex) {
                      +            // 部分情况下会报错,兼容一下
                      +        }
                      +    },
                      +
                      +    // 根据 $Elem 设置选区
                      +    createRangeByElem: function createRangeByElem($elem, toStart, isContent) {
                      +        // $elem - 经过封装的 elem
                      +        // toStart - true 开始位置,false 结束位置
                      +        // isContent - 是否选中Elem的内容
                      +        if (!$elem.length) {
                      +            return;
                      +        }
                      +
                      +        var elem = $elem[0];
                      +        var range = document.createRange();
                      +
                      +        if (isContent) {
                      +            range.selectNodeContents(elem);
                      +        } else {
                      +            range.selectNode(elem);
                      +        }
                      +
                      +        if (typeof toStart === 'boolean') {
                      +            range.collapse(toStart);
                      +        }
                      +
                      +        // 存储 range
                      +        this.saveRange(range);
                      +    }
                      +};
                      +
                      +/*
                      +    上传进度条
                      +*/
                      +
                      +function Progress(editor) {
                      +    this.editor = editor;
                      +    this._time = 0;
                      +    this._isShow = false;
                      +    this._isRender = false;
                      +    this._timeoutId = 0;
                      +    this.$textContainer = editor.$textContainerElem;
                      +    this.$bar = $('
                      '); +} + +Progress.prototype = { + constructor: Progress, + + show: function show(progress) { + var _this = this; + + // 状态处理 + if (this._isShow) { + return; + } + this._isShow = true; + + // 渲染 + var $bar = this.$bar; + if (!this._isRender) { + var $textContainer = this.$textContainer; + $textContainer.append($bar); + } else { + this._isRender = true; + } + + // 改变进度(节流,100ms 渲染一次) + if (Date.now() - this._time > 100) { + if (progress <= 1) { + $bar.css('width', progress * 100 + '%'); + this._time = Date.now(); + } + } + + // 隐藏 + var timeoutId = this._timeoutId; + if (timeoutId) { + clearTimeout(timeoutId); + } + timeoutId = setTimeout(function () { + _this._hide(); + }, 500); + }, + + _hide: function _hide() { + var $bar = this.$bar; + $bar.remove(); + + // 修改状态 + this._time = 0; + this._isShow = false; + this._isRender = false; + } +}; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; +} : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; +}; + +/* + 上传图片 +*/ + +// 构造函数 +function UploadImg(editor) { + this.editor = editor; +} + +// 原型 +UploadImg.prototype = { + constructor: UploadImg, + + // 根据 debug 弹出不同的信息 + _alert: function _alert(alertInfo, debugInfo) { + var editor = this.editor; + var debug = editor.config.debug; + var customAlert = editor.config.customAlert; + + if (debug) { + throw new Error('wangEditor: ' + (debugInfo || alertInfo)); + } else { + if (customAlert && typeof customAlert === 'function') { + customAlert(alertInfo); + } else { + alert(alertInfo); + } + } + }, + + // 根据链接插入图片 + insertLinkImg: function insertLinkImg(link) { + var _this2 = this; + + if (!link) { + return; + } + var editor = this.editor; + var config = editor.config; + + // 校验格式 + var linkImgCheck = config.linkImgCheck; + var checkResult = void 0; + if (linkImgCheck && typeof linkImgCheck === 'function') { + checkResult = linkImgCheck(link); + if (typeof checkResult === 'string') { + // 校验失败,提示信息 + alert(checkResult); + return; + } + } + + editor.cmd.do('insertHTML', ''); + + // 验证图片 url 是否有效,无效的话给出提示 + var img = document.createElement('img'); + img.onload = function () { + var callback = config.linkImgCallback; + if (callback && typeof callback === 'function') { + callback(link); + } + + img = null; + }; + img.onerror = function () { + img = null; + // 无法成功下载图片 + _this2._alert('插入图片错误', 'wangEditor: \u63D2\u5165\u56FE\u7247\u51FA\u9519\uFF0C\u56FE\u7247\u94FE\u63A5\u662F "' + link + '"\uFF0C\u4E0B\u8F7D\u8BE5\u94FE\u63A5\u5931\u8D25'); + return; + }; + img.onabort = function () { + img = null; + }; + img.src = link; + }, + + // 上传图片 + uploadImg: function uploadImg(files) { + var _this3 = this; + + if (!files || !files.length) { + return; + } + + // ------------------------------ 获取配置信息 ------------------------------ + var editor = this.editor; + var config = editor.config; + var uploadImgServer = config.uploadImgServer; + var uploadImgShowBase64 = config.uploadImgShowBase64; + + var maxSize = config.uploadImgMaxSize; + var maxSizeM = maxSize / 1024 / 1024; + var maxLength = config.uploadImgMaxLength || 10000; + var uploadFileName = config.uploadFileName || ''; + var uploadImgParams = config.uploadImgParams || {}; + var uploadImgParamsWithUrl = config.uploadImgParamsWithUrl; + var uploadImgHeaders = config.uploadImgHeaders || {}; + var hooks = config.uploadImgHooks || {}; + var timeout = config.uploadImgTimeout || 3000; + var withCredentials = config.withCredentials; + if (withCredentials == null) { + withCredentials = false; + } + var customUploadImg = config.customUploadImg; + + if (!customUploadImg) { + // 没有 customUploadImg 的情况下,需要如下两个配置才能继续进行图片上传 + if (!uploadImgServer && !uploadImgShowBase64) { + return; + } + } + + // ------------------------------ 验证文件信息 ------------------------------ + var resultFiles = []; + var errInfo = []; + arrForEach(files, function (file) { + var name = file.name; + var size = file.size; + + // chrome 低版本 name === undefined + if (!name || !size) { + return; + } + + if (/\.(jpg|jpeg|png|bmp|gif|webp)$/i.test(name) === false) { + // 后缀名不合法,不是图片 + errInfo.push('\u3010' + name + '\u3011\u4E0D\u662F\u56FE\u7247'); + return; + } + if (maxSize < size) { + // 上传图片过大 + errInfo.push('\u3010' + name + '\u3011\u5927\u4E8E ' + maxSizeM + 'M'); + return; + } + + // 验证通过的加入结果列表 + resultFiles.push(file); + }); + // 抛出验证信息 + if (errInfo.length) { + this._alert('图片验证未通过: \n' + errInfo.join('\n')); + return; + } + if (resultFiles.length > maxLength) { + this._alert('一次最多上传' + maxLength + '张图片'); + return; + } + + // ------------------------------ 自定义上传 ------------------------------ + if (customUploadImg && typeof customUploadImg === 'function') { + customUploadImg(resultFiles, this.insertLinkImg.bind(this)); + + // 阻止以下代码执行 + return; + } + + // 添加图片数据 + var formdata = new FormData(); + arrForEach(resultFiles, function (file) { + var name = uploadFileName || file.name; + formdata.append(name, file); + }); + + // ------------------------------ 上传图片 ------------------------------ + if (uploadImgServer && typeof uploadImgServer === 'string') { + // 添加参数 + var uploadImgServerArr = uploadImgServer.split('#'); + uploadImgServer = uploadImgServerArr[0]; + var uploadImgServerHash = uploadImgServerArr[1] || ''; + objForEach(uploadImgParams, function (key, val) { + // 因使用者反应,自定义参数不能默认 encode ,由 v3.1.1 版本开始注释掉 + // val = encodeURIComponent(val) + + // 第一,将参数拼接到 url 中 + if (uploadImgParamsWithUrl) { + if (uploadImgServer.indexOf('?') > 0) { + uploadImgServer += '&'; + } else { + uploadImgServer += '?'; + } + uploadImgServer = uploadImgServer + key + '=' + val; + } + + // 第二,将参数添加到 formdata 中 + formdata.append(key, val); + }); + if (uploadImgServerHash) { + uploadImgServer += '#' + uploadImgServerHash; + } + + // 定义 xhr + var xhr = new XMLHttpRequest(); + xhr.open('POST', uploadImgServer); + + // 设置超时 + xhr.timeout = timeout; + xhr.ontimeout = function () { + // hook - timeout + if (hooks.timeout && typeof hooks.timeout === 'function') { + hooks.timeout(xhr, editor); + } + + _this3._alert('上传图片超时'); + }; + + // 监控 progress + if (xhr.upload) { + xhr.upload.onprogress = function (e) { + var percent = void 0; + // 进度条 + var progressBar = new Progress(editor); + if (e.lengthComputable) { + percent = e.loaded / e.total; + progressBar.show(percent); + } + }; + } + + // 返回数据 + xhr.onreadystatechange = function () { + var result = void 0; + if (xhr.readyState === 4) { + if (xhr.status < 200 || xhr.status >= 300) { + // hook - error + if (hooks.error && typeof hooks.error === 'function') { + hooks.error(xhr, editor); + } + + // xhr 返回状态错误 + _this3._alert('上传图片发生错误', '\u4E0A\u4F20\u56FE\u7247\u53D1\u751F\u9519\u8BEF\uFF0C\u670D\u52A1\u5668\u8FD4\u56DE\u72B6\u6001\u662F ' + xhr.status); + return; + } + + result = xhr.responseText; + if ((typeof result === 'undefined' ? 'undefined' : _typeof(result)) !== 'object') { + try { + result = JSON.parse(result); + } catch (ex) { + // hook - fail + if (hooks.fail && typeof hooks.fail === 'function') { + hooks.fail(xhr, editor, result); + } + + _this3._alert('上传图片失败', '上传图片返回结果错误,返回结果是: ' + result); + return; + } + } + if (!hooks.customInsert && result.errno != '0') { + // hook - fail + if (hooks.fail && typeof hooks.fail === 'function') { + hooks.fail(xhr, editor, result); + } + + // 数据错误 + _this3._alert('上传图片失败', '上传图片返回结果错误,返回结果 errno=' + result.errno); + } else { + if (hooks.customInsert && typeof hooks.customInsert === 'function') { + // 使用者自定义插入方法 + hooks.customInsert(_this3.insertLinkImg.bind(_this3), result, editor); + } else { + // 将图片插入编辑器 + var data = result.data || []; + data.forEach(function (link) { + _this3.insertLinkImg(link); + }); + } + + // hook - success + if (hooks.success && typeof hooks.success === 'function') { + hooks.success(xhr, editor, result); + } + } + } + }; + + // hook - before + if (hooks.before && typeof hooks.before === 'function') { + var beforeResult = hooks.before(xhr, editor, resultFiles); + if (beforeResult && (typeof beforeResult === 'undefined' ? 'undefined' : _typeof(beforeResult)) === 'object') { + if (beforeResult.prevent) { + // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传 + this._alert(beforeResult.msg); + return; + } + } + } + + // 自定义 headers + objForEach(uploadImgHeaders, function (key, val) { + xhr.setRequestHeader(key, val); + }); + + // 跨域传 cookie + xhr.withCredentials = withCredentials; + + // 发送请求 + xhr.send(formdata); + + // 注意,要 return 。不去操作接下来的 base64 显示方式 + return; + } + + // ------------------------------ 显示 base64 格式 ------------------------------ + if (uploadImgShowBase64) { + arrForEach(files, function (file) { + var _this = _this3; + var reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = function () { + _this.insertLinkImg(this.result); + }; + }); + } + } +}; + +/* + 编辑器构造函数 +*/ + +// id,累加 +var editorId = 1; + +// 构造函数 +function Editor(toolbarSelector, textSelector) { + if (toolbarSelector == null) { + // 没有传入任何参数,报错 + throw new Error('错误:初始化编辑器时候未传入任何参数,请查阅文档'); + } + // id,用以区分单个页面不同的编辑器对象 + this.id = 'wangEditor-' + editorId++; + + this.toolbarSelector = toolbarSelector; + this.textSelector = textSelector; + + // 自定义配置 + this.customConfig = {}; +} + +// 修改原型 +Editor.prototype = { + constructor: Editor, + + // 初始化配置 + _initConfig: function _initConfig() { + // _config 是默认配置,this.customConfig 是用户自定义配置,将它们 merge 之后再赋值 + var target = {}; + this.config = Object.assign(target, config, this.customConfig); + + // 将语言配置,生成正则表达式 + var langConfig = this.config.lang || {}; + var langArgs = []; + objForEach(langConfig, function (key, val) { + // key 即需要生成正则表达式的规则,如“插入链接” + // val 即需要被替换成的语言,如“insert link” + langArgs.push({ + reg: new RegExp(key, 'img'), + val: val + + }); + }); + this.config.langArgs = langArgs; + }, + + // 初始化 DOM + _initDom: function _initDom() { + var _this = this; + + var toolbarSelector = this.toolbarSelector; + var $toolbarSelector = $(toolbarSelector); + var textSelector = this.textSelector; + + var config$$1 = this.config; + var zIndex = config$$1.zIndex; + + // 定义变量 + var $toolbarElem = void 0, + $textContainerElem = void 0, + $textElem = void 0, + $children = void 0; + + if (textSelector == null) { + // 只传入一个参数,即是容器的选择器或元素,toolbar 和 text 的元素自行创建 + $toolbarElem = $('
                      '); + $textContainerElem = $('
                      '); + + // 将编辑器区域原有的内容,暂存起来 + $children = $toolbarSelector.children(); + + // 添加到 DOM 结构中 + $toolbarSelector.append($toolbarElem).append($textContainerElem); + + // 自行创建的,需要配置默认的样式 + $toolbarElem.css('background-color', '#f1f1f1').css('border', '1px solid #ccc'); + $textContainerElem.css('border', '1px solid #ccc').css('border-top', 'none').css('height', '300px'); + } else { + // toolbar 和 text 的选择器都有值,记录属性 + $toolbarElem = $toolbarSelector; + $textContainerElem = $(textSelector); + // 将编辑器区域原有的内容,暂存起来 + $children = $textContainerElem.children(); + } + + // 编辑区域 + $textElem = $('
                      '); + $textElem.attr('contenteditable', 'true').css('width', '100%').css('height', '100%'); + + // 初始化编辑区域内容 + if ($children && $children.length) { + $textElem.append($children); + } else { + $textElem.append($('


                      ')); + } + + // 编辑区域加入DOM + $textContainerElem.append($textElem); + + // 设置通用的 class + $toolbarElem.addClass('w-e-toolbar'); + $textContainerElem.addClass('w-e-text-container'); + $textContainerElem.css('z-index', zIndex); + $textElem.addClass('w-e-text'); + + // 添加 ID + var toolbarElemId = getRandom('toolbar-elem'); + $toolbarElem.attr('id', toolbarElemId); + var textElemId = getRandom('text-elem'); + $textElem.attr('id', textElemId); + + // 记录属性 + this.$toolbarElem = $toolbarElem; + this.$textContainerElem = $textContainerElem; + this.$textElem = $textElem; + this.toolbarElemId = toolbarElemId; + this.textElemId = textElemId; + + // 记录输入法的开始和结束 + var compositionEnd = true; + $textContainerElem.on('compositionstart', function () { + // 输入法开始输入 + compositionEnd = false; + }); + $textContainerElem.on('compositionend', function () { + // 输入法结束输入 + compositionEnd = true; + }); + + // 绑定 onchange + $textContainerElem.on('click keyup', function () { + // 输入法结束才出发 onchange + compositionEnd && _this.change && _this.change(); + }); + $toolbarElem.on('click', function () { + this.change && this.change(); + }); + + //绑定 onfocus 与 onblur 事件 + if (config$$1.onfocus || config$$1.onblur) { + // 当前编辑器是否是焦点状态 + this.isFocus = false; + + $(document).on('click', function (e) { + //判断当前点击元素是否在编辑器内 + var isChild = $textElem.isContain($(e.target)); + + //判断当前点击元素是否为工具栏 + var isToolbar = $toolbarElem.isContain($(e.target)); + var isMenu = $toolbarElem[0] == e.target ? true : false; + + if (!isChild) { + //若为选择工具栏中的功能,则不视为成blur操作 + if (isToolbar && !isMenu) { + return; + } + + if (_this.isFocus) { + _this.onblur && _this.onblur(); + } + _this.isFocus = false; + } else { + if (!_this.isFocus) { + _this.onfocus && _this.onfocus(); + } + _this.isFocus = true; + } + }); + } + }, + + // 封装 command + _initCommand: function _initCommand() { + this.cmd = new Command(this); + }, + + // 封装 selection range API + _initSelectionAPI: function _initSelectionAPI() { + this.selection = new API(this); + }, + + // 添加图片上传 + _initUploadImg: function _initUploadImg() { + this.uploadImg = new UploadImg(this); + }, + + // 初始化菜单 + _initMenus: function _initMenus() { + this.menus = new Menus(this); + this.menus.init(); + }, + + // 添加 text 区域 + _initText: function _initText() { + this.txt = new Text(this); + this.txt.init(); + }, + + // 初始化选区,将光标定位到内容尾部 + initSelection: function initSelection(newLine) { + var $textElem = this.$textElem; + var $children = $textElem.children(); + if (!$children.length) { + // 如果编辑器区域无内容,添加一个空行,重新设置选区 + $textElem.append($('


                      ')); + this.initSelection(); + return; + } + + var $last = $children.last(); + + if (newLine) { + // 新增一个空行 + var html = $last.html().toLowerCase(); + var nodeName = $last.getNodeName(); + if (html !== '
                      ' && html !== '' || nodeName !== 'P') { + // 最后一个元素不是


                      ,添加一个空行,重新设置选区 + $textElem.append($('


                      ')); + this.initSelection(); + return; + } + } + + this.selection.createRangeByElem($last, false, true); + this.selection.restoreSelection(); + }, + + // 绑定事件 + _bindEvent: function _bindEvent() { + // -------- 绑定 onchange 事件 -------- + var onChangeTimeoutId = 0; + var beforeChangeHtml = this.txt.html(); + var config$$1 = this.config; + + // onchange 触发延迟时间 + var onchangeTimeout = config$$1.onchangeTimeout; + onchangeTimeout = parseInt(onchangeTimeout, 10); + if (!onchangeTimeout || onchangeTimeout <= 0) { + onchangeTimeout = 200; + } + + var onchange = config$$1.onchange; + if (onchange && typeof onchange === 'function') { + // 触发 change 的有三个场景: + // 1. $textContainerElem.on('click keyup') + // 2. $toolbarElem.on('click') + // 3. editor.cmd.do() + this.change = function () { + // 判断是否有变化 + var currentHtml = this.txt.html(); + + if (currentHtml.length === beforeChangeHtml.length) { + // 需要比较每一个字符 + if (currentHtml === beforeChangeHtml) { + return; + } + } + + // 执行,使用节流 + if (onChangeTimeoutId) { + clearTimeout(onChangeTimeoutId); + } + onChangeTimeoutId = setTimeout(function () { + // 触发配置的 onchange 函数 + onchange(currentHtml); + beforeChangeHtml = currentHtml; + }, onchangeTimeout); + }; + } + + // -------- 绑定 onblur 事件 -------- + var onblur = config$$1.onblur; + if (onblur && typeof onblur === 'function') { + this.onblur = function () { + var currentHtml = this.txt.html(); + onblur(currentHtml); + }; + } + + // -------- 绑定 onfocus 事件 -------- + var onfocus = config$$1.onfocus; + if (onfocus && typeof onfocus === 'function') { + this.onfocus = function () { + onfocus(); + }; + } + }, + + // 创建编辑器 + create: function create() { + // 初始化配置信息 + this._initConfig(); + + // 初始化 DOM + this._initDom(); + + // 封装 command API + this._initCommand(); + + // 封装 selection range API + this._initSelectionAPI(); + + // 添加 text + this._initText(); + + // 初始化菜单 + this._initMenus(); + + // 添加 图片上传 + this._initUploadImg(); + + // 初始化选区,将光标定位到内容尾部 + this.initSelection(true); + + // 绑定事件 + this._bindEvent(); + }, + + // 解绑所有事件(暂时不对外开放) + _offAllEvent: function _offAllEvent() { + $.offAll(); + } +}; + +// 检验是否浏览器环境 +try { + document; +} catch (ex) { + throw new Error('请在浏览器环境下运行'); +} + +// polyfill +polyfill(); + +// 这里的 `inlinecss` 将被替换成 css 代码的内容,详情可去 ./gulpfile.js 中搜索 `inlinecss` 关键字 +var inlinecss = '.w-e-toolbar,.w-e-text-container,.w-e-menu-panel { padding: 0; margin: 0; box-sizing: border-box;}.w-e-toolbar *,.w-e-text-container *,.w-e-menu-panel * { padding: 0; margin: 0; box-sizing: border-box;}.w-e-clear-fix:after { content: ""; display: table; clear: both;}.w-e-toolbar .w-e-droplist { position: absolute; left: 0; top: 0; background-color: #fff; border: 1px solid #f1f1f1; border-right-color: #ccc; border-bottom-color: #ccc;}.w-e-toolbar .w-e-droplist .w-e-dp-title { text-align: center; color: #999; line-height: 2; border-bottom: 1px solid #f1f1f1; font-size: 13px;}.w-e-toolbar .w-e-droplist ul.w-e-list { list-style: none; line-height: 1;}.w-e-toolbar .w-e-droplist ul.w-e-list li.w-e-item { color: #333; padding: 5px 0;}.w-e-toolbar .w-e-droplist ul.w-e-list li.w-e-item:hover { background-color: #f1f1f1;}.w-e-toolbar .w-e-droplist ul.w-e-block { list-style: none; text-align: left; padding: 5px;}.w-e-toolbar .w-e-droplist ul.w-e-block li.w-e-item { display: inline-block; *display: inline; *zoom: 1; padding: 3px 5px;}.w-e-toolbar .w-e-droplist ul.w-e-block li.w-e-item:hover { background-color: #f1f1f1;}@font-face { font-family: \'w-e-icon\'; src: url(data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAABhQAAsAAAAAGAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABCAAAAGAAAABgDxIPBGNtYXAAAAFoAAABBAAAAQQrSf4BZ2FzcAAAAmwAAAAIAAAACAAAABBnbHlmAAACdAAAEvAAABLwfpUWUWhlYWQAABVkAAAANgAAADYQp00kaGhlYQAAFZwAAAAkAAAAJAfEA+FobXR4AAAVwAAAAIQAAACEeAcD7GxvY2EAABZEAAAARAAAAERBSEX+bWF4cAAAFogAAAAgAAAAIAAsALZuYW1lAAAWqAAAAYYAAAGGmUoJ+3Bvc3QAABgwAAAAIAAAACAAAwAAAAMD3gGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA8fwDwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEAOgAAAA2ACAABAAWAAEAIOkG6Q3pEulH6Wbpd+m56bvpxunL6d/qDepc6l/qZepo6nHqefAN8BTxIPHc8fz//f//AAAAAAAg6QbpDekS6UfpZel36bnpu+nG6cvp3+oN6lzqX+pi6mjqcep38A3wFPEg8dzx/P/9//8AAf/jFv4W+Bb0FsAWoxaTFlIWURZHFkMWMBYDFbUVsxWxFa8VpxWiEA8QCQ7+DkMOJAADAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAACAAD/wAQAA8AABAATAAABNwEnAQMuAScTNwEjAQMlATUBBwGAgAHAQP5Anxc7MmOAAYDA/oDAAoABgP6ATgFAQAHAQP5A/p0yOxcBEU4BgP6A/YDAAYDA/oCAAAQAAAAABAADgAAQACEALQA0AAABOAExETgBMSE4ATEROAExITUhIgYVERQWMyEyNjURNCYjBxQGIyImNTQ2MzIWEyE1EwEzNwPA/IADgPyAGiYmGgOAGiYmGoA4KCg4OCgoOED9AOABAEDgA0D9AAMAQCYa/QAaJiYaAwAaJuAoODgoKDg4/biAAYD+wMAAAAIAAABABAADQAA4ADwAAAEmJy4BJyYjIgcOAQcGBwYHDgEHBhUUFx4BFxYXFhceARcWMzI3PgE3Njc2Nz4BNzY1NCcuAScmJwERDQED1TY4OXY8PT8/PTx2OTg2CwcICwMDAwMLCAcLNjg5djw9Pz89PHY5ODYLBwgLAwMDAwsIBwv9qwFA/sADIAgGBggCAgICCAYGCCkqKlktLi8vLi1ZKiopCAYGCAICAgIIBgYIKSoqWS0uLy8uLVkqKin94AGAwMAAAAAAAgDA/8ADQAPAABsAJwAAASIHDgEHBhUUFx4BFxYxMDc+ATc2NTQnLgEnJgMiJjU0NjMyFhUUBgIAQjs6VxkZMjJ4MjIyMngyMhkZVzo7QlBwcFBQcHADwBkZVzo7Qnh9fcxBQUFBzH19eEI7OlcZGf4AcFBQcHBQUHAAAAEAAAAABAADgAArAAABIgcOAQcGBycRISc+ATMyFx4BFxYVFAcOAQcGBxc2Nz4BNzY1NCcuAScmIwIANTIyXCkpI5YBgJA1i1BQRUZpHh4JCSIYGB5VKCAgLQwMKCiLXl1qA4AKCycbHCOW/oCQNDweHmlGRVArKClJICEaYCMrK2I2NjlqXV6LKCgAAQAAAAAEAAOAACoAABMUFx4BFxYXNyYnLgEnJjU0Nz4BNzYzMhYXByERByYnLgEnJiMiBw4BBwYADAwtICAoVR4YGCIJCR4eaUZFUFCLNZABgJYjKSlcMjI1al1eiygoAYA5NjZiKysjYBohIEkpKCtQRUZpHh48NJABgJYjHBsnCwooKIteXQAAAAACAAAAQAQBAwAAJgBNAAATMhceARcWFRQHDgEHBiMiJy4BJyY1JzQ3PgE3NjMVIgYHDgEHPgEhMhceARcWFRQHDgEHBiMiJy4BJyY1JzQ3PgE3NjMVIgYHDgEHPgHhLikpPRESEhE9KSkuLikpPRESASMjelJRXUB1LQkQBwgSAkkuKSk9ERISET0pKS4uKSk9ERIBIyN6UlFdQHUtCRAHCBICABIRPSkpLi4pKT0REhIRPSkpLiBdUVJ6IyOAMC4IEwoCARIRPSkpLi4pKT0REhIRPSkpLiBdUVJ6IyOAMC4IEwoCAQAABgBA/8AEAAPAAAMABwALABEAHQApAAAlIRUhESEVIREhFSEnESM1IzUTFTMVIzU3NSM1MxUVESM1MzUjNTM1IzUBgAKA/YACgP2AAoD9gMBAQECAwICAwMCAgICAgIACAIACAIDA/wDAQP3yMkCSPDJAku7+wEBAQEBAAAYAAP/ABAADwAADAAcACwAXACMALwAAASEVIREhFSERIRUhATQ2MzIWFRQGIyImETQ2MzIWFRQGIyImETQ2MzIWFRQGIyImAYACgP2AAoD9gAKA/YD+gEs1NUtLNTVLSzU1S0s1NUtLNTVLSzU1SwOAgP8AgP8AgANANUtLNTVLS/61NUtLNTVLS/61NUtLNTVLSwADAAAAAAQAA6AAAwANABQAADchFSElFSE1EyEVITUhJQkBIxEjEQAEAPwABAD8AIABAAEAAQD9YAEgASDggEBAwEBAAQCAgMABIP7g/wABAAAAAAACAB7/zAPiA7QAMwBkAAABIiYnJicmNDc2PwE+ATMyFhcWFxYUBwYPAQYiJyY0PwE2NCcuASMiBg8BBhQXFhQHDgEjAyImJyYnJjQ3Nj8BNjIXFhQPAQYUFx4BMzI2PwE2NCcmNDc2MhcWFxYUBwYPAQ4BIwG4ChMIIxISEhIjwCNZMTFZIyMSEhISI1gPLA8PD1gpKRQzHBwzFMApKQ8PCBMKuDFZIyMSEhISI1gPLA8PD1gpKRQzHBwzFMApKQ8PDysQIxISEhIjwCNZMQFECAckLS1eLS0kwCIlJSIkLS1eLS0kVxAQDysPWCl0KRQVFRTAKXQpDysQBwj+iCUiJC0tXi0tJFcQEA8rD1gpdCkUFRUUwCl0KQ8rEA8PJC0tXi0tJMAiJQAAAAAFAAD/wAQAA8AAGwA3AFMAXwBrAAAFMjc+ATc2NTQnLgEnJiMiBw4BBwYVFBceARcWEzIXHgEXFhUUBw4BBwYjIicuAScmNTQ3PgE3NhMyNz4BNzY3BgcOAQcGIyInLgEnJicWFx4BFxYnNDYzMhYVFAYjIiYlNDYzMhYVFAYjIiYCAGpdXosoKCgoi15dampdXosoKCgoi15dalZMTHEgISEgcUxMVlZMTHEgISEgcUxMVisrKlEmJiMFHBtWODc/Pzc4VhscBSMmJlEqK9UlGxslJRsbJQGAJRsbJSUbGyVAKCiLXl1qal1eiygoKCiLXl1qal1eiygoA6AhIHFMTFZWTExxICEhIHFMTFZWTExxICH+CQYGFRAQFEM6OlYYGRkYVjo6QxQQEBUGBvcoODgoKDg4KCg4OCgoODgAAAMAAP/ABAADwAAbADcAQwAAASIHDgEHBhUUFx4BFxYzMjc+ATc2NTQnLgEnJgMiJy4BJyY1NDc+ATc2MzIXHgEXFhUUBw4BBwYTBycHFwcXNxc3JzcCAGpdXosoKCgoi15dampdXosoKCgoi15dalZMTHEgISEgcUxMVlZMTHEgISEgcUxMSqCgYKCgYKCgYKCgA8AoKIteXWpqXV6LKCgoKIteXWpqXV6LKCj8YCEgcUxMVlZMTHEgISEgcUxMVlZMTHEgIQKgoKBgoKBgoKBgoKAAAQBl/8ADmwPAACkAAAEiJiMiBw4BBwYVFBYzLgE1NDY3MAcGAgcGBxUhEzM3IzceATMyNjcOAQMgRGhGcVNUbRobSUgGDWVKEBBLPDxZAT1sxizXNC1VJi5QGB09A7AQHh1hPj9BTTsLJjeZbwN9fv7Fj5AjGQIAgPYJDzdrCQcAAAAAAgAAAAAEAAOAAAkAFwAAJTMHJzMRIzcXIyURJyMRMxUhNTMRIwcRA4CAoKCAgKCggP8AQMCA/oCAwEDAwMACAMDAwP8AgP1AQEACwIABAAADAMAAAANAA4AAFgAfACgAAAE+ATU0Jy4BJyYjIREhMjc+ATc2NTQmATMyFhUUBisBEyMRMzIWFRQGAsQcIBQURi4vNf7AAYA1Ly5GFBRE/oRlKjw8KWafn58sPj4B2yJULzUvLkYUFPyAFBRGLi81RnQBRks1NUv+gAEASzU1SwAAAAACAMAAAANAA4AAHwAjAAABMxEUBw4BBwYjIicuAScmNREzERQWFx4BMzI2Nz4BNQEhFSECwIAZGVc6O0JCOzpXGRmAGxgcSSgoSRwYG/4AAoD9gAOA/mA8NDVOFhcXFk41NDwBoP5gHjgXGBsbGBc4Hv6ggAAAAAABAIAAAAOAA4AACwAAARUjATMVITUzASM1A4CA/sCA/kCAAUCAA4BA/QBAQAMAQAABAAAAAAQAA4AAPQAAARUjHgEVFAYHDgEjIiYnLgE1MxQWMzI2NTQmIyE1IS4BJy4BNTQ2Nz4BMzIWFx4BFSM0JiMiBhUUFjMyFhcEAOsVFjUwLHE+PnEsMDWAck5OcnJO/gABLAIEATA1NTAscT4+cSwwNYByTk5yck47bisBwEAdQSI1YiQhJCQhJGI1NExMNDRMQAEDASRiNTViJCEkJCEkYjU0TEw0NEwhHwAAAAcAAP/ABAADwAADAAcACwAPABMAGwAjAAATMxUjNzMVIyUzFSM3MxUjJTMVIwMTIRMzEyETAQMhAyMDIQMAgIDAwMABAICAwMDAAQCAgBAQ/QAQIBACgBD9QBADABAgEP2AEAHAQEBAQEBAQEBAAkD+QAHA/oABgPwAAYD+gAFA/sAAAAoAAAAABAADgAADAAcACwAPABMAFwAbAB8AIwAnAAATESERATUhFR0BITUBFSE1IxUhNREhFSElIRUhETUhFQEhFSEhNSEVAAQA/YABAP8AAQD/AED/AAEA/wACgAEA/wABAPyAAQD/AAKAAQADgPyAA4D9wMDAQMDAAgDAwMDA/wDAwMABAMDA/sDAwMAAAAUAAAAABAADgAADAAcACwAPABMAABMhFSEVIRUhESEVIREhFSERIRUhAAQA/AACgP2AAoD9gAQA/AAEAPwAA4CAQID/AIABQID/AIAAAAAABQAAAAAEAAOAAAMABwALAA8AEwAAEyEVIRchFSERIRUhAyEVIREhFSEABAD8AMACgP2AAoD9gMAEAPwABAD8AAOAgECA/wCAAUCA/wCAAAAFAAAAAAQAA4AAAwAHAAsADwATAAATIRUhBSEVIREhFSEBIRUhESEVIQAEAPwAAYACgP2AAoD9gP6ABAD8AAQA/AADgIBAgP8AgAFAgP8AgAAAAAABAD8APwLmAuYALAAAJRQPAQYjIi8BBwYjIi8BJjU0PwEnJjU0PwE2MzIfATc2MzIfARYVFA8BFxYVAuYQThAXFxCoqBAXFhBOEBCoqBAQThAWFxCoqBAXFxBOEBCoqBDDFhBOEBCoqBAQThAWFxCoqBAXFxBOEBCoqBAQThAXFxCoqBAXAAAABgAAAAADJQNuABQAKAA8AE0AVQCCAAABERQHBisBIicmNRE0NzY7ATIXFhUzERQHBisBIicmNRE0NzY7ATIXFhcRFAcGKwEiJyY1ETQ3NjsBMhcWExEhERQXFhcWMyEyNzY3NjUBIScmJyMGBwUVFAcGKwERFAcGIyEiJyY1ESMiJyY9ATQ3NjsBNzY3NjsBMhcWHwEzMhcWFQElBgUIJAgFBgYFCCQIBQaSBQUIJQgFBQUFCCUIBQWSBQUIJQgFBQUFCCUIBQVJ/gAEBAUEAgHbAgQEBAT+gAEAGwQGtQYEAfcGBQg3Ghsm/iUmGxs3CAUFBQUIsSgIFxYXtxcWFgkosAgFBgIS/rcIBQUFBQgBSQgFBgYFCP63CAUFBQUIAUkIBQYGBQj+twgFBQUFCAFJCAUGBgX+WwId/eMNCwoFBQUFCgsNAmZDBQICBVUkCAYF/eMwIiMhIi8CIAUGCCQIBQVgFQ8PDw8VYAUFCAACAAcASQO3Aq8AGgAuAAAJAQYjIi8BJjU0PwEnJjU0PwE2MzIXARYVFAcBFRQHBiMhIicmPQE0NzYzITIXFgFO/vYGBwgFHQYG4eEGBh0FCAcGAQoGBgJpBQUI/dsIBQUFBQgCJQgFBQGF/vYGBhwGCAcG4OEGBwcGHQUF/vUFCAcG/vslCAUFBQUIJQgFBQUFAAAAAQAjAAAD3QNuALMAACUiJyYjIgcGIyInJjU0NzY3Njc2NzY9ATQnJiMhIgcGHQEUFxYXFjMWFxYVFAcGIyInJiMiBwYjIicmNTQ3Njc2NzY3Nj0BETQ1NDU0JzQnJicmJyYnJicmIyInJjU0NzYzMhcWMzI3NjMyFxYVFAcGIwYHBgcGHQEUFxYzITI3Nj0BNCcmJyYnJjU0NzYzMhcWMzI3NjMyFxYVFAcGByIHBgcGFREUFxYXFhcyFxYVFAcGIwPBGTMyGhkyMxkNCAcJCg0MERAKEgEHFf5+FgcBFQkSEw4ODAsHBw4bNTUaGDExGA0HBwkJCwwQDwkSAQIBAgMEBAUIEhENDQoLBwcOGjU1GhgwMRgOBwcJCgwNEBAIFAEHDwGQDgcBFAoXFw8OBwcOGTMyGRkxMRkOBwcKCg0NEBEIFBQJEREODQoLBwcOAAICAgIMCw8RCQkBAQMDBQxE4AwFAwMFDNRRDQYBAgEICBIPDA0CAgICDAwOEQgJAQIDAwUNRSEB0AINDQgIDg4KCgsLBwcDBgEBCAgSDwwNAgICAg0MDxEICAECAQYMULYMBwEBBwy2UAwGAQEGBxYPDA0CAgICDQwPEQgIAQECBg1P/eZEDAYCAgEJCBEPDA0AAAIAAP+3A/8DtwATADkAAAEyFxYVFAcCBwYjIicmNTQ3ATYzARYXFh8BFgcGIyInJicmJyY1FhcWFxYXFjMyNzY3Njc2NzY3NjcDmygeHhq+TDdFSDQ0NQFtISn9+BcmJy8BAkxMe0c2NiEhEBEEExQQEBIRCRcIDxITFRUdHR4eKQO3GxooJDP+mUY0NTRJSTABSx/9sSsfHw0oek1MGhsuLzo6RAMPDgsLCgoWJRsaEREKCwQEAgABAAAAAAAA9evv618PPPUACwQAAAAAANbEBFgAAAAA1sQEWAAA/7cEAQPAAAAACAACAAAAAAAAAAEAAAPA/8AAAAQAAAD//wQBAAEAAAAAAAAAAAAAAAAAAAAhBAAAAAAAAAAAAAAAAgAAAAQAAAAEAAAABAAAAAQAAMAEAAAABAAAAAQAAAAEAABABAAAAAQAAAAEAAAeBAAAAAQAAAAEAABlBAAAAAQAAMAEAADABAAAgAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAMlAD8DJQAAA74ABwQAACMD/wAAAAAAAAAKABQAHgBMAJQA+AE2AXwBwgI2AnQCvgLoA34EHgSIBMoE8gU0BXAFiAXgBiIGagaSBroG5AcoB+AIKgkcCXgAAQAAACEAtAAKAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAcAAAABAAAAAAACAAcAYAABAAAAAAADAAcANgABAAAAAAAEAAcAdQABAAAAAAAFAAsAFQABAAAAAAAGAAcASwABAAAAAAAKABoAigADAAEECQABAA4ABwADAAEECQACAA4AZwADAAEECQADAA4APQADAAEECQAEAA4AfAADAAEECQAFABYAIAADAAEECQAGAA4AUgADAAEECQAKADQApGljb21vb24AaQBjAG8AbQBvAG8AblZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMGljb21vb24AaQBjAG8AbQBvAG8Abmljb21vb24AaQBjAG8AbQBvAG8AblJlZ3VsYXIAUgBlAGcAdQBsAGEAcmljb21vb24AaQBjAG8AbQBvAG8AbkZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=) format(\'truetype\'); font-weight: normal; font-style: normal;}[class^="w-e-icon-"],[class*=" w-e-icon-"] { /* use !important to prevent issues with browser extensions that change fonts */ font-family: \'w-e-icon\' !important; speak: none; font-style: normal; font-weight: normal; font-variant: normal; text-transform: none; line-height: 1; /* Better Font Rendering =========== */ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;}.w-e-icon-close:before { content: "\\f00d";}.w-e-icon-upload2:before { content: "\\e9c6";}.w-e-icon-trash-o:before { content: "\\f014";}.w-e-icon-header:before { content: "\\f1dc";}.w-e-icon-pencil2:before { content: "\\e906";}.w-e-icon-paint-brush:before { content: "\\f1fc";}.w-e-icon-image:before { content: "\\e90d";}.w-e-icon-play:before { content: "\\e912";}.w-e-icon-location:before { content: "\\e947";}.w-e-icon-undo:before { content: "\\e965";}.w-e-icon-redo:before { content: "\\e966";}.w-e-icon-quotes-left:before { content: "\\e977";}.w-e-icon-list-numbered:before { content: "\\e9b9";}.w-e-icon-list2:before { content: "\\e9bb";}.w-e-icon-link:before { content: "\\e9cb";}.w-e-icon-happy:before { content: "\\e9df";}.w-e-icon-bold:before { content: "\\ea62";}.w-e-icon-underline:before { content: "\\ea63";}.w-e-icon-italic:before { content: "\\ea64";}.w-e-icon-strikethrough:before { content: "\\ea65";}.w-e-icon-table2:before { content: "\\ea71";}.w-e-icon-paragraph-left:before { content: "\\ea77";}.w-e-icon-paragraph-center:before { content: "\\ea78";}.w-e-icon-paragraph-right:before { content: "\\ea79";}.w-e-icon-terminal:before { content: "\\f120";}.w-e-icon-page-break:before { content: "\\ea68";}.w-e-icon-cancel-circle:before { content: "\\ea0d";}.w-e-icon-font:before { content: "\\ea5c";}.w-e-icon-text-heigh:before { content: "\\ea5f";}.w-e-toolbar { display: -webkit-box; display: -ms-flexbox; display: flex; padding: 0 5px; /* flex-wrap: wrap; */ /* 单个菜单 */}.w-e-toolbar .w-e-menu { position: relative; text-align: center; padding: 5px 10px; cursor: pointer;}.w-e-toolbar .w-e-menu i { color: #999;}.w-e-toolbar .w-e-menu:hover i { color: #333;}.w-e-toolbar .w-e-active i { color: #1e88e5;}.w-e-toolbar .w-e-active:hover i { color: #1e88e5;}.w-e-text-container .w-e-panel-container { position: absolute; top: 0; left: 50%; border: 1px solid #ccc; border-top: 0; box-shadow: 1px 1px 2px #ccc; color: #333; background-color: #fff; /* 为 emotion panel 定制的样式 */ /* 上传图片的 panel 定制样式 */}.w-e-text-container .w-e-panel-container .w-e-panel-close { position: absolute; right: 0; top: 0; padding: 5px; margin: 2px 5px 0 0; cursor: pointer; color: #999;}.w-e-text-container .w-e-panel-container .w-e-panel-close:hover { color: #333;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-title { list-style: none; display: -webkit-box; display: -ms-flexbox; display: flex; font-size: 14px; margin: 2px 10px 0 10px; border-bottom: 1px solid #f1f1f1;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-title .w-e-item { padding: 3px 5px; color: #999; cursor: pointer; margin: 0 3px; position: relative; top: 1px;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-title .w-e-active { color: #333; border-bottom: 1px solid #333; cursor: default; font-weight: 700;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content { padding: 10px 15px 10px 15px; font-size: 16px; /* 输入框的样式 */ /* 按钮的样式 */}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input:focus,.w-e-text-container .w-e-panel-container .w-e-panel-tab-content textarea:focus,.w-e-text-container .w-e-panel-container .w-e-panel-tab-content button:focus { outline: none;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content textarea { width: 100%; border: 1px solid #ccc; padding: 5px;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content textarea:focus { border-color: #1e88e5;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input[type=text] { border: none; border-bottom: 1px solid #ccc; font-size: 14px; height: 20px; color: #333; text-align: left;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input[type=text].small { width: 30px; text-align: center;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input[type=text].block { display: block; width: 100%; margin: 10px 0;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input[type=text]:focus { border-bottom: 2px solid #1e88e5;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button { font-size: 14px; color: #1e88e5; border: none; padding: 5px 10px; background-color: #fff; cursor: pointer; border-radius: 3px;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button.left { float: left; margin-right: 10px;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button.right { float: right; margin-left: 10px;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button.gray { color: #999;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button.red { color: #c24f4a;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button:hover { background-color: #f1f1f1;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container:after { content: ""; display: table; clear: both;}.w-e-text-container .w-e-panel-container .w-e-emoticon-container .w-e-item { cursor: pointer; font-size: 18px; padding: 0 3px; display: inline-block; *display: inline; *zoom: 1;}.w-e-text-container .w-e-panel-container .w-e-up-img-container { text-align: center;}.w-e-text-container .w-e-panel-container .w-e-up-img-container .w-e-up-btn { display: inline-block; *display: inline; *zoom: 1; color: #999; cursor: pointer; font-size: 60px; line-height: 1;}.w-e-text-container .w-e-panel-container .w-e-up-img-container .w-e-up-btn:hover { color: #333;}.w-e-text-container { position: relative;}.w-e-text-container .w-e-progress { position: absolute; background-color: #1e88e5; bottom: 0; left: 0; height: 1px;}.w-e-text { padding: 0 10px; overflow-y: scroll;}.w-e-text p,.w-e-text h1,.w-e-text h2,.w-e-text h3,.w-e-text h4,.w-e-text h5,.w-e-text table,.w-e-text pre { margin: 10px 0; line-height: 1.5;}.w-e-text ul,.w-e-text ol { margin: 10px 0 10px 20px;}.w-e-text blockquote { display: block; border-left: 8px solid #d0e5f2; padding: 5px 10px; margin: 10px 0; line-height: 1.4; font-size: 100%; background-color: #f1f1f1;}.w-e-text code { display: inline-block; *display: inline; *zoom: 1; background-color: #f1f1f1; border-radius: 3px; padding: 3px 5px; margin: 0 3px;}.w-e-text pre code { display: block;}.w-e-text table { border-top: 1px solid #ccc; border-left: 1px solid #ccc;}.w-e-text table td,.w-e-text table th { border-bottom: 1px solid #ccc; border-right: 1px solid #ccc; padding: 3px 5px;}.w-e-text table th { border-bottom: 2px solid #ccc; text-align: center;}.w-e-text:focus { outline: none;}.w-e-text img { cursor: pointer;}.w-e-text img:hover { box-shadow: 0 0 5px #333;}'; + +// 将 css 代码添加到 "].join(""),a=f(o.layHeader.html());a.append(o.layMain.find("table").html()),a.append(o.layTotal.find("table").html()),a.find("th.layui-table-patch").remove(),a.find("thead>tr>th."+F).filter(function(e,t){return!f(t).children("."+u).length}).remove(),a.find("tbody>tr>td."+F).remove(),t.document.write(e+a.prop("outerHTML")),t.document.close(),layui.device("edg").edg?(t.onafterprint=t.close,t.print()):(t.print(),t.close())}}});"object"==typeof e.defaultToolbar&&(l=[],e.defaultToolbar=f.map(e.defaultToolbar,function(e,t){var a="string"==typeof e,i=a?n[e]:e;return i&&(!(i=i.name&&n[i.name]?f.extend({},n[i.name],i):i).name&&a&&(i.name=e),l.push('
                      ')),i}),o.layTool.find(".layui-table-tool-self").html(l.join("")))},n.prototype.renderPagebar=function(){var e,t=this.config,a=this.layPagebar=f('
                      ');t.pagebar&&((e=f(t.pagebar).html()||"")&&a.append(m(e).render(t)),this.layPage.append(a))},n.prototype.setParentCol=function(e,t){var a=this.config,i=this.layHeader.find('th[data-key="'+t+'"]'),l=parseInt(i.attr("colspan"))||0;i[0]&&(t=t.split("-"),t=a.cols[t[1]][t[2]],e?l--:l++,i.attr("colspan",l),i[l?"removeClass":"addClass"](W),t.colspan2=l,t.hide=l<1,a=i.data("parentkey"))&&this.setParentCol(e,a)},n.prototype.setColsPatch=function(){var a=this,e=a.config;layui.each(e.cols,function(e,t){layui.each(t,function(e,t){t.hide&&a.setParentCol(t.hide,t.parentKey)})})},n.prototype.setGroupWidth=function(i){var e,l=this;l.config.cols.length<=1||((e=l.layHeader.find((i?"th[data-key="+i.data("parentkey")+"]>":"")+"."+u)).css("width",0),layui.each(e.get().reverse(),function(){var e=f(this),t=e.parent().data("key"),a=0;l.layHeader.eq(0).find("th[data-parentkey="+t+"]").width(function(e,t){f(this).hasClass(W)||0o.layMain.prop("clientHeight")&&(e.style.width=parseFloat(e.style.width)-i+"px")}),!p&&h?y.width(o.getContentWidth(l)):y.width("auto"),o.setGroupWidth()},n.prototype.resize=function(){var e=this;e.layMain&&("isConnected"in e.layMain[0]?e.layMain[0].isConnected:f.contains(document.body,e.layMain[0]))&&(e.fullSize(),e.setColsWidth(),e.scrollPatch())},n.prototype.reload=function(e,t,a){var i=this;e=e||{},delete i.haveInit,layui.each(e,function(e,t){"array"===layui.type(t)&&delete i.config[e]}),i.config=f.extend(t,{},i.config,e),"reloadData"!==a&&(layui.each(i.config.cols,function(e,t){layui.each(t,function(e,t){delete t.colspan2})}),delete i.config.HAS_SET_COLS_PATCH),i.render(a)},n.prototype.errorView=function(e){var t=this,a=t.layMain.find("."+h),e=f('
                      '+(e||"Error")+"
                      ");a[0]&&(t.layNone.remove(),a.remove()),t.layFixed.addClass(W),t.layMain.find("tbody").html(""),t.layMain.append(t.layNone=e),t.layTotal.addClass(y),t.layPage.find(P).addClass(y),k.cache[t.key]=[],t.syncCheckAll(),t.renderForm(),t.setColsWidth(),t.loading(!1)},n.prototype.page=1,n.prototype.pullData=function(i,l){var e,t,n=this,o=n.config,a=(o.HAS_SET_COLS_PATCH||n.setColsPatch(),o.HAS_SET_COLS_PATCH=!0,o.request),d=o.response,r=function(){"object"==typeof o.initSort&&n.sort({field:o.initSort.field,type:o.initSort.type,reloadType:l.type})},c=function(e,t){n.setColsWidth(),n.loading(!1),"function"==typeof o.done&&o.done(e,i,e[d.countName],t)};l=l||{},"function"==typeof o.before&&o.before(o),n.startTime=(new Date).getTime(),l.renderData?((e={})[d.dataName]=k.cache[n.key],e[d.countName]=o.url?"object"===layui.type(o.page)?o.page.count:e[d.dataName].length:o.data.length,"object"==typeof o.totalRow&&(e[d.totalRowName]=f.extend({},n.totalRow)),n.renderData({res:e,curr:i,count:e[d.countName],type:l.type,sort:!0}),c(e,"renderData")):o.url?(t={},o.page&&(t[a.pageName]=i,t[a.limitName]=o.limit),a=f.extend(t,o.where),o.contentType&&0==o.contentType.indexOf("application/json")&&(a=JSON.stringify(a)),n.loading(!0),n._xhr&&4!==n._xhr.readyState&&(n._xhrAbort=!0,n._xhr.abort()),n._xhr=f.ajax({type:o.method||"get",url:o.url,contentType:o.contentType,data:a,dataType:o.dataType||"json",jsonpCallback:o.jsonpCallback,headers:o.headers||{},complete:"function"==typeof o.complete?o.complete:undefined,success:function(e){var t,a;(e="function"==typeof o.parseData?o.parseData(e)||e:e)[d.statusName]!=d.statusCode?n.errorView(e[d.msgName]||'\u8fd4\u56de\u7684\u6570\u636e\u4e0d\u7b26\u5408\u89c4\u8303\uff0c\u6b63\u786e\u7684\u6210\u529f\u72b6\u6001\u7801\u5e94\u4e3a\uff1a"'+d.statusName+'": '+d.statusCode):(t=e[d.countName],(a=Math.ceil(t/o.limit)||1)','
                      "+function(){var e,t=f.extend(!0,{LAY_COL:l},o),a=k.config.checkName,i=k.config.disabledName;switch(l.type){case"checkbox":return'';case"radio":return'';case"numbers":return c}return l.toolbar?m(f(l.toolbar).html()||"").render(t):T.call(s,{item3:l,content:n,tplData:t})}(),"
                      "].join(""),i.push(e),l.fixed&&"right"!==l.fixed&&d.push(e),"right"===l.fixed&&r.push(e))}),e=['data-index="'+e+'"'],o[k.config.checkName]&&e.push('class="'+A+'"'),e=e.join(" "),y.push(""+i.join("")+""),h.push(""+d.join("")+""),p.push(""+r.join("")+""))}),{trs:y,trs_fixed:h,trs_fixed_r:p}},k.getTrHtml=function(e,t){e=C(e);return e.getTrHtml(t,null,e.page)},n.prototype.renderData=function(e){var a=this,i=a.config,t=e.res,l=e.curr,n=a.count=e.count,o=e.sort,d=t[i.response.dataName]||[],t=t[i.response.totalRowName],r=[],c=[],s=[],u=function(){if(!o&&a.sortKey)return a.sort({field:a.sortKey.field,type:a.sortKey.sort,pull:!0,reloadType:e.type});a.getTrHtml(d,o,l,{trs:r,trs_fixed:c,trs_fixed_r:s}),"fixed"===i.scrollPos&&"reloadData"===e.type||a.layBody.scrollTop(0),"reset"===i.scrollPos&&a.layBody.scrollLeft(0),a.layMain.find("."+h).remove(),a.layMain.find("tbody").html(r.join("")),a.layFixLeft.find("tbody").html(c.join("")),a.layFixRight.find("tbody").html(s.join("")),a.syncCheckAll(),a.renderForm(),a.fullSize(),a.haveInit?a.scrollPatch():setTimeout(function(){a.scrollPatch()},50),a.haveInit=!0,g.close(a.tipsIndex)};return k.cache[a.key]=d,a.layTotal[0==d.length?"addClass":"removeClass"](y),a.layPage[i.page||i.pagebar?"removeClass":"addClass"](W),a.layPage.find(P)[!i.page||0==n||0===d.length&&1==l?"addClass":"removeClass"](y),0===d.length?a.errorView(i.text.none):(a.layFixLeft.removeClass(W),o?u():(u(),a.renderTotal(d,t),a.layTotal&&a.layTotal.removeClass(W),void(i.page&&(i.page=f.extend({elem:"layui-table-page"+i.index,count:n,limit:i.limit,limits:i.limits||[10,20,30,40,50,60,70,80,90],groups:3,layout:["prev","page","next","skip","count","limit"],prev:'',next:'',jump:function(e,t){t||(a.page=e.curr,i.limit=e.limit,a.pullData(e.curr))}},i.page),i.page.count=n,p.render(i.page)))))},k.renderData=function(e){e=C(e);e&&e.pullData(e.page,{renderData:!0,type:"reloadData"})},n.prototype.renderTotal=function(e,o){var d,r=this,c=r.config,s={};c.totalRow&&(layui.each(e,function(e,i){"array"===layui.type(i)&&0===i.length||r.eachCols(function(e,t){var e=t.field||e,a=i[e];t.totalRow&&(s[e]=(s[e]||0)+(parseFloat(a)||0))})}),r.dataTotal=[],d=[],r.eachCols(function(e,t){var e=t.field||e,a=o&&o[t.field],i="totalRowDecimals"in t?t.totalRowDecimals:2,i=s[e]?parseFloat(s[e]||0).toFixed(i):"",i=(n=t.totalRowText||"",(l={LAY_COL:t})[e]=i,l=t.totalRow&&T.call(r,{item3:t,content:i,tplData:l})||n,a||l),l="string"==typeof(n=t.totalRow||c.totalRow)?m(n).render(f.extend({TOTAL_NUMS:a||s[e],TOTAL_ROW:o||{},LAY_COL:t},t)):i,n=(t.field&&r.dataTotal.push({field:t.field,total:f("
                      "+l+"
                      ").text()}),['','
                      "+l,"
                      "].join(""));d.push(n)}),e=r.layTotal.find(".layui-table-patch"),r.layTotal.find("tbody").html(""+d.join("")+(e.length?e.get(0).outerHTML:"")+""))},n.prototype.getColElem=function(e,t){return e.eq(0).find(".laytable-cell-"+t+":eq(0)")},n.prototype.renderForm=function(e){this.config;var t=this.elem.attr("lay-filter");i.render(e,t)},n.prototype.renderFormByElem=function(a){layui.each(["input","select"],function(e,t){i.render(a.find(t))})},n.prototype.syncCheckAll=function(){var a,e=this,i=e.config,t=e.layHeader.find('input[name="layTableCheckbox"]'),l=k.checkStatus(e.key);t[0]&&(a=l.isAll,e.eachCols(function(e,t){"checkbox"===t.type&&(t[i.checkName]=a)}),t.prop({checked:l.isAll,indeterminate:!l.isAll&&l.data.length}))},n.prototype.setRowActive=function(e,t,a){this.config;e=this.layBody.find('tr[data-index="'+e+'"]');if(t=t||"layui-table-click",a)return e.removeClass(t);e.addClass(t),e.siblings("tr").removeClass(t)},n.prototype.setRowChecked=function(i){var a,e,l,t,n,o,d,r=this,c=r.config,s="all"===i.index,u="array"===layui.type(i.index),y=s||u;c.tree&&c.tree.view||y&&(r.layBox.addClass(O),"radio"===i.type)||(u&&(a={},layui.each(i.index,function(e,t){a[t]=!0}),i.index=a),e=r.layBody.children(".layui-table").children("tbody"),d=y?"tr":'tr[data-index="'+i.index+'"]',d=e.children(d),e=s?d:d.filter(u?function(){var e=f(this).data("index");return i.index[e]}:'[data-index="'+i.index+'"]'),i=f.extend({type:"checkbox"},i),l=k.cache[r.key],t="checked"in i,n=function(e){return"radio"===i.type||(t?i.checked:!e)},e.each(function(){var e=f(this),t=e.attr("data-index"),a=l[t];t&&"array"!==layui.type(a)&&!a[c.disabledName]&&(a=a[c.checkName]=n(e.hasClass(A)),e.toggleClass(A,!!a),"radio"===i.type)&&(o=t,e.siblings().removeClass(A))}),o&&layui.each(l,function(e,t){Number(o)!==Number(e)&&delete t[c.checkName]}),d=(u=(s=e.children("td").children(".layui-table-cell").children('input[lay-type="'+({radio:"layTableRadio",checkbox:"layTableCheckbox"}[i.type]||"checkbox")+'"]:not(:disabled)')).last()).closest(I),("radio"===i.type&&d.hasClass(W)?s.first():s).prop("checked",n(u.prop("checked"))),r.syncCheckAll(),y&&setTimeout(function(){r.layBox.removeClass(O)},100))},n.prototype.sort=function(l){var e,t=this,a={},i=t.config,n=i.elem.attr("lay-filter"),o=k.cache[t.key];"string"==typeof(l=l||{}).field&&(d=l.field,t.layHeader.find("th").each(function(e,t){var a=f(this),i=a.data("field");if(i===l.field)return l.field=a,d=i,!1}));try{var d=d||l.field.data("field"),r=l.field.data("key");if(t.sortKey&&!l.pull&&d===t.sortKey.field&&l.type===t.sortKey.sort)return;var c=t.layHeader.find("th .laytable-cell-"+r).find(L);t.layHeader.find("th").find(L).removeAttr("lay-sort"),c.attr("lay-sort",l.type||null),t.layFixed.find("th")}catch(s){b.error("Table modules: sort field '"+d+"' not matched")}t.sortKey={field:d,sort:l.type},i.autoSort&&("asc"===l.type?e=layui.sort(o,d,null,!0):"desc"===l.type?e=layui.sort(o,d,!0,!0):(e=layui.sort(o,k.config.initIndexName,null,!0),delete t.sortKey,delete i.initSort)),a[i.response.dataName]=e||o,t.renderData({res:a,curr:t.page,count:t.count,sort:!0,type:l.reloadType}),l.fromEvent&&(i.initSort={field:d,type:l.type},layui.event.call(l.field,R,"sort("+n+")",f.extend({config:i},i.initSort)))},n.prototype.loading=function(e){this.config.loading&&this.layBox.find(".layui-table-init").toggleClass(W,!e)},n.prototype.cssRules=function(t,a){var e=this.elem.children("style")[0];r.getStyleRules(e,function(e){if(e.selectorText===".laytable-cell-"+t)return a(e),!0})},n.prototype.fullSize=function(){var e,a,i=this,t=i.config,l=t.height;i.fullHeightGap?(l=d.height()-i.fullHeightGap)<135&&(l=135):i.parentDiv&&i.parentHeightGap?(l=f(i.parentDiv).height()-i.parentHeightGap)<135&&(l=135):i.customHeightFunc&&(l=i.customHeightFunc())<135&&(l=135),1
                      ')).find("div").css({width:a}),e.find("tr").append(t)):e.find(".layui-table-patch").remove()};n(e.layHeader),n(e.layTotal);n=e.layMain.height()-i;e.layFixed.find(D).css("height",t.height()>=n?n:"auto").scrollTop(e.layMain.scrollTop()),e.layFixRight[k.cache[e.key]&&k.cache[e.key].length&&0
                    ');a.html(t),s.height&&a.css("max-height",s.height-(c.layTool.outerHeight()||50)),i.find("."+S)[0]||i.append(a),c.renderForm(),a.on("click",function(e){layui.stope(e)}),e.done&&e.done(a,t)};layui.stope(e),z.trigger("table.tool.panel.remove"),g.close(c.tipsIndex),layui.each(s.defaultToolbar,function(e,t){if(t.layEvent===a)return"function"==typeof t.onClick&&t.onClick({data:l,config:s,openPanel:n,elem:i}),!0}),layui.event.call(this,R,"toolbar("+o+")",f.extend({event:a,config:s},{}))}),c.layHeader.on("click","*[lay-event]",function(e){var t=f(this),a=t.attr("lay-event"),t=t.closest("th").data("key"),t=c.col(t);layui.event.call(this,R,"colTool("+o+")",f.extend({event:a,config:s,col:t},{}))}),c.layPagebar.on("click","*[lay-event]",function(e){var t=f(this).attr("lay-event");layui.event.call(this,R,"pagebar("+o+")",f.extend({event:t,config:s},{}))}),e.on("mousemove",function(e){var t=f(this),a=t.offset().left,e=e.clientX-a;t.data("unresize")||w.eventMoveElem||(r.allowResize=t.width()-e<=10,d.css("cursor",r.allowResize?"col-resize":""))}).on("mouseleave",function(){f(this);w.eventMoveElem||(r.allowResize=!1,d.css("cursor",""))}).on("mousedown",function(e){var t,a=f(this);r.allowResize&&(t=a.data("key"),e.preventDefault(),r.offset=[e.clientX,e.clientY],c.cssRules(t,function(e){var t=e.style.width||a.outerWidth();r.rule=e,r.ruleWidth=parseFloat(t),r.minWidth=a.data("minwidth")||s.cellMinWidth,r.maxWidth=a.data("maxwidth")||s.cellMaxWidth}),a.data(j,r),w.eventMoveElem=a)}),w.docEvent||z.on("mousemove",function(e){var t,a;w.eventMoveElem&&(t=w.eventMoveElem.data(j)||{},w.eventMoveElem.data("resizing",1),e.preventDefault(),t.rule)&&(e=t.ruleWidth+e.clientX-t.offset[0],a=w.eventMoveElem.closest("."+_).attr(N),a=C(a))&&((e=et.maxWidth&&(e=t.maxWidth),t.rule.style.width=e+"px",a.setGroupWidth(w.eventMoveElem),g.close(c.tipsIndex))}).on("mouseup",function(e){var t,a,i,l,n;w.eventMoveElem&&(i=(t=w.eventMoveElem).closest("."+_).attr(N),a=C(i))&&(i=t.data("key"),l=a.col(i),n=a.config.elem.attr("lay-filter"),r={},d.css("cursor",""),a.scrollPatch(),t.removeData(j),delete w.eventMoveElem,a.cssRules(i,function(e){l.width=parseFloat(e.style.width),layui.event.call(t[0],R,"colResized("+n+")",{col:l,config:a.config})}))}),w.docEvent=!0,e.on("click",function(e){var t=f(this),a=t.find(L),i=a.attr("lay-sort");if(!a[0]||1===t.data("resizing"))return t.removeData("resizing");c.sort({field:t,type:"asc"===i?"desc":"desc"===i?null:"asc",fromEvent:!0})}).find(L+" .layui-edge ").on("click",function(e){var t=f(this),a=t.index(),t=t.parents("th").eq(0).data("field");layui.stope(e),0===a?c.sort({field:t,type:"asc",fromEvent:!0}):c.sort({field:t,type:"desc",fromEvent:!0})}),c.commonMember=function(e){var a=f(this).parents("tr").eq(0).data("index"),t=c.layBody.find('tr[data-index="'+a+'"]'),i=(k.cache[c.key]||[])[a]||{},l={tr:t,config:s,data:k.clearCacheKey(i),dataCache:i,index:a,del:function(){k.cache[c.key][a]=[],t.remove(),c.scrollPatch()},update:function(e,t){c.updateRow({index:a,data:e=e||{},related:t},function(e,t){l.data[e]=t})},setRowChecked:function(e){c.setRowChecked(f.extend({index:a},e))}};return f.extend(l,e)}),t=(c.elem.on("click",'input[name="layTableCheckbox"]+',function(e){var t=f(this),a=t.closest("td"),t=t.prev(),i=(c.layBody.find('input[name="layTableCheckbox"]'),t.parents("tr").eq(0).data("index")),l=t[0].checked,n="layTableAllChoose"===t.attr("lay-filter");t[0].disabled||(n?c.setRowChecked({index:"all",checked:l}):c.setRowChecked({index:i,checked:l}),layui.stope(e),layui.event.call(t[0],R,"checkbox("+o+")",y.call(t[0],{checked:l,type:n?"all":"one",getCol:function(){return c.col(a.data("key"))}})))}),c.elem.on("click",'input[lay-type="layTableRadio"]+',function(e){var t=f(this),a=t.closest("td"),t=t.prev(),i=t[0].checked,l=t.parents("tr").eq(0).data("index");if(layui.stope(e),t[0].disabled)return!1;c.setRowChecked({type:"radio",index:l}),layui.event.call(t[0],R,"radio("+o+")",y.call(t[0],{checked:i,getCol:function(){return c.col(a.data("key"))}}))}),c.layBody.on("mouseenter","tr",function(){var e=f(this),t=e.index();e.data("off")||c.layBody.find("tr:eq("+t+")").addClass(M)}).on("mouseleave","tr",function(){var e=f(this),t=e.index();e.data("off")||c.layBody.find("tr:eq("+t+")").removeClass(M)}).on("click","tr",function(e){t.call(this,"row",e)}).on("dblclick","tr",function(e){t.call(this,"rowDouble",e)}).on("contextmenu","tr",function(e){s.defaultContextmenu||e.preventDefault(),t.call(this,"rowContextmenu",e)}),function(e,t){var a=f(this);if(!a.data("off")){if("rowContextmenu"!==e){var i=[".layui-form-checkbox",".layui-form-switch",".layui-form-radio","[lay-unrow]"].join(",");if(f(t.target).is(i)||f(t.target).closest(i)[0])return}layui.event.call(this,R,e+"("+o+")",y.call(a.children("td")[0],{e:t}))}}),n=function(e,t){var a,i,l;(e=f(e)).data("off")||(l=e.data("field"),i=e.data("key"),i=c.col(i),a=e.closest("tr").data("index"),a=k.cache[c.key][a],e.children(u),(i="function"==typeof i.edit?i.edit(a):i.edit)&&((i=f("textarea"===i?'':''))[0].value=(l=e.data("content")||a[l])===undefined||null===l?"":l,e.find("."+E)[0]||e.append(i),i.focus(),t)&&layui.stope(t))},i=(c.layBody.on("change","."+E,function(){var e=f(this),t=e.parent(),a=this.value,i=e.parent().data("field"),e=e.closest("tr").data("index"),e=k.cache[c.key][e],l=y.call(t[0],{value:a,field:i,oldValue:e[i],td:t,reedit:function(){setTimeout(function(){n(l.td);var e={};e[i]=l.oldValue,l.update(e)})},getCol:function(){return c.col(t.data("key"))}}),e={};e[i]=a,l.update(e),layui.event.call(t[0],R,"edit("+o+")",l)}).on("blur","."+E,function(){f(this).remove()}),c.layBody.on(s.editTrigger,"td",function(e){n(this,e)}).on("mouseenter","td",function(){a.call(this)}).on("mouseleave","td",function(){a.call(this,"hide")}),c.layTotal.on("mouseenter","td",function(){a.call(this)}).on("mouseleave","td",function(){a.call(this,"hide")}),"layui-table-grid-down"),a=function(e){var t=f(this),a=t.children(u);t.data("off")||t.parent().hasClass(H)||(e?t.find(".layui-table-grid-down").remove():!(a.prop("scrollWidth")>a.prop("clientWidth")||0
                    '))},l=function(e,t){var a=f(this),i=a.parent(),l=i.data("key"),n=c.col(l),o=i.parent().data("index"),d=i.children(u),i="layui-table-cell-c",r=f('');"tips"===(t=t||n.expandedMode||s.cellExpandedMode)?c.tipsIndex=g.tips(['
                    ',d.html(),"
                    ",''].join(""),d[0],{tips:[3,""],time:-1,anim:-1,maxWidth:x.ios||x.android?300:c.elem.width()/2,isOutAnim:!1,skin:"layui-table-tips",success:function(e,t){e.find(".layui-table-tips-c").on("click",function(){g.close(t)})}}):(c.elem.find("."+i).trigger("click"),c.cssRules(l,function(e){var t=e.style.width,a=n.expandedWidth||s.cellExpandedWidth;atr").each(function(i){n.cols[i]=[],f(this).children().each(function(e){var t=f(this),a=t.attr("lay-data"),a=r.options(this,{attr:a?"lay-data":null,errorText:d+(a||t.attr("lay-options"))}),t=f.extend({title:t.text(),colspan:parseInt(t.attr("colspan"))||1,rowspan:parseInt(t.attr("rowspan"))||1},a);n.cols[i].push(t)})}),e.find("tbody>tr")),t=k.render(n);!a.length||o.data||t.config.url||(l=0,k.eachCols(t.config.id,function(e,i){a.each(function(e){n.data[e]=n.data[e]||{};var t=f(this),a=i.field;n.data[e][a]=t.children("td").eq(l).html()}),l++}),t.reloadData({data:n.data}))}),this},w.that={},w.config={},function(a,i,e,l){var n,o;l.colGroup&&(n=0,a++,l.CHILD_COLS=[],o=e+(parseInt(l.rowspan)||1),layui.each(i[o],function(e,t){t.parentKey?t.parentKey===l.key&&(t.PARENT_COL_INDEX=a,l.CHILD_COLS.push(t),c(a,i,o,t)):t.PARENT_COL_INDEX||1<=n&&n==(l.colspan||1)||(t.PARENT_COL_INDEX=a,l.CHILD_COLS.push(t),n+=parseInt(1td').filter('[data-field="'+e+'"]')}}})).replace(/"/g,'""'),n.push(a='"'+a+'"')):t.field&&"normal"!==t.type&&0==i&&(u[t.field]=!0)}),c.push(n.join(","))}),o&&layui.each(o.dataTotal,function(e,t){u[t.field]||s.push('"'+(t.total||"")+'"')}),r.join(",")+"\r\n"+c.join("\r\n")+"\r\n"+s.join(","))),d.download=(a.title||l.title||"table_"+(l.index||""))+"."+i,document.body.appendChild(d),d.click(),document.body.removeChild(d)},k.getOptions=l,k.hideCol=function(e,l){var n=C(e);n&&("boolean"===layui.type(l)?n.eachCols(function(e,t){var a=t.key,i=n.col(a),t=t.parentKey;i.hide!=l&&(i=i.hide=l,n.elem.find('*[data-key="'+a+'"]')[i?"addClass":"removeClass"](W),n.setParentCol(i,t))}):(l=layui.isArray(l)?l:[l],layui.each(l,function(e,l){n.eachCols(function(e,t){var a,i;l.field===t.field&&(a=t.key,i=n.col(a),t=t.parentKey,"hide"in l)&&i.hide!=l.hide&&(i=i.hide=!!l.hide,n.elem.find('*[data-key="'+a+'"]')[i?"addClass":"removeClass"](W),n.setParentCol(i,t))})})),f("."+S).remove(),n.resize())},k.reload=function(e,t,a,i){if(l(e))return(e=C(e)).reload(t,a,i),w.call(e)},k.reloadData=function(){var a=f.extend([],arguments),i=(a[3]="reloadData",new RegExp("^("+["elem","id","cols","width","height","maxHeight","toolbar","defaultToolbar","className","css","pagebar"].join("|")+")$"));return layui.each(a[1],function(e,t){i.test(e)&&delete a[1][e]}),k.reload.apply(null,a)},k.render=function(e){e=new n(e);return w.call(e)},k.clearCacheKey=function(e){return delete(e=f.extend({},e))[k.config.checkName],delete e[k.config.indexName],delete e[k.config.initIndexName],delete e[k.config.numbersName],delete e[k.config.disabledName],e},f(function(){k.init()}),s(R,k)});layui.define(["table"],function(e){"use strict";var P=layui.$,h=layui.form,B=layui.table,y=layui.hint(),j={config:{},on:B.on,eachCols:B.eachCols,index:B.index,set:function(e){var t=this;return t.config=P.extend({},t.config,e),t},resize:B.resize,getOptions:B.getOptions,hideCol:B.hideCol,renderData:B.renderData},i=function(){var a=this,e=a.config,n=e.id||e.index;return{config:e,reload:function(e,t){a.reload.call(a,e,t)},reloadData:function(e,t){j.reloadData(n,e,t)}}},F=function(e){var t=i.that[e];return t||y.error(e?"The treeTable instance with ID '"+e+"' not found":"ID argument required"),t||null},L="lay-table-id",q="layui-hide",s=".layui-table-body",R=".layui-table-main",Y=".layui-table-fixed-l",z=".layui-table-fixed-r",l="layui-table-checked",m="layui-table-tree",H="LAY_DATA_INDEX",b="LAY_DATA_INDEX_HISTORY",p="LAY_PARENT_INDEX",g="LAY_CHECKBOX_HALF",X="LAY_EXPAND",V="LAY_HAS_EXPANDED",U="LAY_ASYNC_STATUS",n=["all","parent","children","none"],t=/<[^>]+?>/,f=["flexIconClose","flexIconOpen","iconClose","iconOpen","iconLeaf","icon"],a=function(e){var t=this;t.index=++j.index,t.config=P.extend(!0,{},t.config,j.config,e),t.init(),t.render()},x=function(n,i,e){var l=B.cache[n];layui.each(e||l,function(e,t){var a=t[H]||"";-1!==a.indexOf("-")&&(l[a]=t),t[i]&&x(n,i,t[i])})},d=function(d,a,e){var r=F(d),o=("reloadData"!==e&&(r.status={expand:{}}),P.extend(!0,{},r.getOptions(),a)),n=o.tree,c=n.customName.children,i=n.customName.id,l=(delete a.hasNumberCol,delete a.hasChecboxCol,delete a.hasRadioCol,B.eachCols(null,function(e,t){"numbers"===t.type?a.hasNumberCol=!0:"checkbox"===t.type?a.hasChecboxCol=!0:"radio"===t.type&&(a.hasRadioCol=!0)},o.cols),a.parseData),u=a.done;"reloadData"===e&&"fixed"===o.scrollPos&&(r.scrollTopCache=r.config.elem.next().find(s).scrollTop()),o.url?e&&(!l||l.mod)||(a.parseData=function(){var e=this,t=arguments,a=t[0],t=("function"===layui.type(l)&&(a=l.apply(e,t)||t[0]),e.response.dataName);return n.data.isSimpleData&&!n["async"].enable&&(a[t]=r.flatToTree(a[t])),N(a[t],function(e){e[X]=X in e?e[X]:e[i]!==undefined&&r.status.expand[e[i]]},c),e.autoSort&&e.initSort&&e.initSort.type&&layui.sort(a[t],e.initSort.field,"desc"===e.initSort.type,!0),r.initData(a[t]),a},a.parseData.mod=!0):a.data!==undefined&&(a.data=a.data||[],n.data.isSimpleData&&(a.data=r.flatToTree(a.data)),r.initData(a.data)),e&&(!u||u.mod)||(a.done=function(){var e,t=arguments,a=t[3],n="renderData"===a,i=(n||delete r.isExpandAll,this.elem.next()),l=(r.updateStatus(null,{LAY_HAS_EXPANDED:!1}),x(d,c),i.find('[name="layTableCheckbox"][lay-filter="layTableAllChoose"]'));if(l.length&&(e=j.checkStatus(d),l.prop({checked:e.isAll&&e.data.length,indeterminate:!e.isAll&&e.data.length})),!n&&o.autoSort&&o.initSort&&o.initSort.type&&j.sort(d),r.renderTreeTable(i),"reloadData"===a&&"fixed"===this.scrollPos&&i.find(s).scrollTop(r.scrollTopCache),"function"===layui.type(u))return u.apply(this,t)},a.done.mod=!0),a&&a.tree&&a.tree.view&&layui.each(f,function(e,t){a.tree.view[t]!==undefined&&(a.tree.view[t]=r.normalizedIcon(a.tree.view[t]))})};a.prototype.init=function(){var e=this.config,t=e.tree.data.cascade,t=(-1===n.indexOf(t)&&(e.tree.data.cascade="all"),B.render(P.extend({},e,{data:[],url:"",done:null}))),a=t.config.id;(i.that[a]=this).tableIns=t,d(a,e)},a.prototype.config={tree:{customName:{children:"children",isParent:"isParent",name:"name",id:"id",pid:"parentId",icon:"icon"},view:{indent:14,flexIconClose:'',flexIconOpen:'',showIcon:!0,icon:"",iconClose:'',iconOpen:'',iconLeaf:'',showFlexIconIfNotParent:!1,dblClickExpand:!0,expandAllDefault:!1},data:{isSimpleData:!1,rootPid:null,cascade:"all"},"async":{enable:!1,url:"",type:null,contentType:null,headers:null,where:null,autoParam:[]},callback:{beforeExpand:null,onExpand:null}}},a.prototype.normalizedIcon=function(e){return e?t.test(e)?e:'':""},a.prototype.getOptions=function(){return this.tableIns?B.getOptions(this.tableIns.config.id):this.config},a.prototype.flatToTree=function(e){var n,i,l,d,r,o,c,u,t=this.getOptions(),a=t.tree,s=a.customName,t=t.id;return e=e||B.cache[t],t=e,n=s.id,i=s.pid,l=s.children,d=a.data.rootPid,n=n||"id",i=i||"parentId",l=l||"children",c={},u=[],layui.each(t,function(e,t){r=n+t[n],o=n+t[i],c[r]||(c[r]={},c[r][l]=[]);var a={};a[l]=c[r][l],c[r]=P.extend({},t,a),((d?c[r][i]===d:!c[r][i])?u:(c[o]||(c[o]={},c[o][l]=[]),c[o][l])).push(c[r])}),u},a.prototype.treeToFlat=function(e,n,i){var l=this,d=l.getOptions().tree.customName,r=d.children,o=d.pid,c=[];return layui.each(e,function(e,t){var e=(i?i+"-":"")+e,a=P.extend({},t);a[o]="undefined"!=typeof t[o]?t[o]:n,c.push(a),c=c.concat(l.treeToFlat(t[r],t[d.id],e))}),c},a.prototype.getTreeNode=function(e){var t,a,n=this;return e?(a=(t=n.getOptions()).tree,t.id,a.customName,{data:e,dataIndex:e[H],getParentNode:function(){return n.getNodeByIndex(e[p])}}):y.error("\u627e\u4e0d\u5230\u8282\u70b9\u6570\u636e")},a.prototype.getNodeByIndex=function(t){var a,e,n=this,i=n.getNodeDataByIndex(t);return i?((e=n.getOptions()).tree.customName.parent,a=e.id,(e={data:i,dataIndex:i[H],getParentNode:function(){return n.getNodeByIndex(i[p])},update:function(e){return j.updateNode(a,t,e)},remove:function(){return j.removeNode(a,t)},expand:function(e){return j.expandNode(a,P.extend({},e,{index:t}))},setChecked:function(e){return j.setRowChecked(a,P.extend({},e,{index:t}))}}).dataIndex=t,e):y.error("\u627e\u4e0d\u5230\u8282\u70b9\u6570\u636e")},a.prototype.getNodeById=function(a){var e=this.getOptions(),n=e.tree.customName.id,i="",e=j.getData(e.id,!0);if(layui.each(e,function(e,t){if(t[n]===a)return i=t[H],!0}),i)return this.getNodeByIndex(i)},a.prototype.getNodeDataByIndex=function(e,t,a){var n=this.getOptions(),i=n.tree,n=n.id,n=B.cache[n],l=n[e];if("delete"!==a&&l)return P.extend(l,a),t?P.extend({},l):l;for(var d=n,r=String(e).split("-"),o=0,c=i.customName.children;o
                    '),I=function(e){p[U]="success",p[f.children]=e,u.initData(p[f.children],p[H]),K(t,!0,!x&&n,i,l,d)},D=b.format,"function"===layui.type(D)?D(p,c,I):(C=P.extend({},b.where||c.where),D=b.autoParam,layui.each(D,function(e,t){t=t.split("=");C[t[0].trim()]=p[(t[1]||t[0]).trim()]}),(D=b.contentType||c.contentType)&&0==D.indexOf("application/json")&&(C=JSON.stringify(C)),S=b.method||c.method,T=b.dataType||c.dataType,_=b.jsonpCallback||c.jsonpCallback,k=b.headers||c.headers,w=b.parseData||c.parseData,O=b.response||c.response,P.ajax({type:S||"get",url:g,contentType:D,data:C,dataType:T||"json",jsonpCallback:_,headers:k||{},success:function(e){(e="function"==typeof w?w.call(c,e)||e:e)[O.statusName]!=O.statusCode?(p[U]="error",p[X]=!1,v.html('')):I(e[O.dataName])},error:function(e,t){p[U]="error",p[X]=!1,"function"==typeof c.error&&c.error(e,t)}})),m;p[V]=!0,N.length&&(!c.initSort||c.url&&!c.autoSort||((b=c.initSort).type?layui.sort(N,b.field,"desc"===b.type,!0):layui.sort(N,B.config.indexName,null,!0)),u.initData(p[f.children],p[H]),S=B.getTrHtml(o,N,null,null,e),E={trs:P(S.trs.join("")),trs_fixed:P(S.trs_fixed.join("")),trs_fixed_r:P(S.trs_fixed_r.join(""))},A=(e.split("-").length-1||0)+1,layui.each(N,function(e,t){E.trs.eq(e).attr({"data-index":t[H],"lay-data-index":t[H],"data-level":A}).data("index",t[H]),E.trs_fixed.eq(e).attr({"data-index":t[H],"lay-data-index":t[H],"data-level":A}).data("index",t[H]),E.trs_fixed_r.eq(e).attr({"data-index":t[H],"lay-data-index":t[H],"data-level":A}).data("index",t[H])}),r.find(R).find('tbody tr[lay-data-index="'+e+'"]').after(E.trs),r.find(Y).find('tbody tr[lay-data-index="'+e+'"]').after(E.trs_fixed),r.find(z).find('tbody tr[lay-data-index="'+e+'"]').after(E.trs_fixed_r),u.renderTreeTable(E.trs,A),n)&&!x&&layui.each(N,function(e,t){K({dataIndex:t[H],trElem:r.find('tr[lay-data-index="'+t[H]+'"]').first(),tableViewElem:r,tableId:o,options:c},a,n,i,l,d)})}else u.isExpandAll=!1,(n&&!x?(layui.each(N,function(e,t){K({dataIndex:t[H],trElem:r.find('tr[lay-data-index="'+t[H]+'"]').first(),tableViewElem:r,tableId:o,options:c},a,n,i,l,d)}),r.find(N.map(function(e,t,a){return'tr[lay-data-index="'+e[H]+'"]'}).join(","))):(g=u.treeToFlat(N,p[f.id],e),r.find(g.map(function(e,t,a){return'tr[lay-data-index="'+e[H]+'"]'}).join(",")))).addClass(q);J("resize-"+o,function(){j.resize(o)},0)(),l&&"loading"!==p[U]&&(D=s.callback.onExpand,"function"===layui.type(D))&&D(o,p,h),"function"===layui.type(d)&&"loading"!==p[U]&&d(o,p,h)}return m},v=(j.expandNode=function(e,t){var a,n,i,l,e=F(e);if(e)return a=(t=t||{}).index,n=t.expandFlag,i=t.inherit,l=t.callbackFlag,e=e.getOptions().elem.next(),K({trElem:e.find('tr[lay-data-index="'+a+'"]').first()},n,i,null,l,t.done)},j.expandAll=function(a,e){if("boolean"!==layui.type(e))return y.error("expandAll \u7684\u5c55\u5f00\u72b6\u6001\u53c2\u6570\u53ea\u63a5\u6536true/false");var t=F(a);if(t){t.isExpandAll=e;var n=t.getOptions(),i=n.tree,l=n.elem.next(),d=i.customName.isParent,r=i.customName.id,o=i.view.showFlexIconIfNotParent;if(e){e=j.getData(a,!0);if(i["async"].enable){var c=!0;if(layui.each(e,function(e,t){if(t[d]&&!t[U])return!(c=!1)}),!c)return void layui.each(j.getData(a),function(e,t){j.expandNode(a,{index:t[H],expandFlag:!0,inherit:!0})})}var u=!0;if(layui.each(e,function(e,t){if(t[d]&&!t[V])return!(u=!1)}),u)t.updateStatus(null,function(e){(e[d]||o)&&(e[X]=!0,e[r]!==undefined)&&(t.status.expand[e[r]]=!0)}),l.find('tbody tr[data-level!="0"]').removeClass(q),l.find(".layui-table-tree-flexIcon").html(i.view.flexIconOpen),i.view.showIcon&&l.find(".layui-table-tree-nodeIcon:not(.layui-table-tree-iconCustom,.layui-table-tree-iconLeaf)").html(i.view.iconOpen);else{if(t.updateStatus(null,function(e){(e[d]||o)&&(e[X]=!0,e[V]=!0,e[r]!==undefined)&&(t.status.expand[e[r]]=!0)}),n.initSort&&n.initSort.type&&n.autoSort)return j.sort(a);var s,n=B.getTrHtml(a,e),f={trs:P(n.trs.join("")),trs_fixed:P(n.trs_fixed.join("")),trs_fixed_r:P(n.trs_fixed_r.join(""))};layui.each(e,function(e,t){var a=t[H].split("-").length-1;s={"data-index":t[H],"lay-data-index":t[H],"data-level":a},f.trs.eq(e).attr(s).data("index",t[H]),f.trs_fixed.eq(e).attr(s).data("index",t[H]),f.trs_fixed_r.eq(e).attr(s).data("index",t[H])}),layui.each(["main","fixed-l","fixed-r"],function(e,t){l.find(".layui-table-"+t+" tbody").html(f[["trs","trs_fixed","trs_fixed_r"][e]])}),t.renderTreeTable(l,0,!1)}}else t.updateStatus(null,function(e){(e[d]||o)&&(e[X]=!1,e[r]!==undefined)&&(t.status.expand[e[r]]=!1)}),l.find('.layui-table-box tbody tr[data-level!="0"]').addClass(q),l.find(".layui-table-tree-flexIcon").html(i.view.flexIconClose),i.view.showIcon&&l.find(".layui-table-tree-nodeIcon:not(.layui-table-tree-iconCustom,.layui-table-tree-iconLeaf)").html(i.view.iconClose);j.resize(a)}},a.prototype.updateNodeIcon=function(e){var t=this.getOptions().tree||{},a=e.scopeEl,n=e.isExpand,e=e.isParent;a.find(".layui-table-tree-flexIcon").css("visibility",e||t.view.showFlexIconIfNotParent?"visible":"hidden").html(n?t.view.flexIconOpen:t.view.flexIconClose),t.view.showIcon&&(a=a.find(".layui-table-tree-nodeIcon:not(.layui-table-tree-iconCustom)"),n=e?n?t.view.iconOpen:t.view.iconClose:t.view.iconLeaf,a.toggleClass("layui-table-tree-iconLeaf",!e).html(n))},a.prototype.renderTreeTable=function(e,t,a){var l=this,n=l.getOptions(),d=n.elem.next(),i=(d.hasClass(m)||d.addClass(m),n.id),r=n.tree||{},o=(r.data,r.view||{}),c=r.customName||{},u=c.isParent,s=(d.attr("lay-filter"),l),f=n.data.length,y=((t=t||0)||(d.find(".layui-table-body tr:not([data-level])").attr("data-level",t),layui.each(B.cache[i],function(e,t){f&&(t[H]=String(e));t=t[H];d.find('.layui-table-main tbody tr[data-level="0"]:eq('+e+")").attr("lay-data-index",t),d.find('.layui-table-fixed-l tbody tr[data-level="0"]:eq('+e+")").attr("lay-data-index",t),d.find('.layui-table-fixed-r tbody tr[data-level="0"]:eq('+e+")").attr("lay-data-index",t)})),null),p=c.name,x=o.indent||20;if(layui.each(e.find('td[data-field="'+p+'"]'),function(e,t){var a,n,i=(t=P(t)).closest("tr"),t=t.children(".layui-table-cell");t.hasClass("layui-table-tree-item")||(n=i.attr("lay-data-index"))&&(i=d.find('tr[lay-data-index="'+n+'"]'),(a=s.getNodeDataByIndex(n))[X]&&a[u]&&((y=y||{})[n]=!0),a[g]&&i.find('input[type="checkbox"][name="layTableCheckbox"]').prop("indeterminate",!0),n=t.html(),(t=i.find('td[data-field="'+p+'"]>div.layui-table-cell')).addClass("layui-table-tree-item"),t.html(['
                    ',a[X]?o.flexIconOpen:o.flexIconClose,"
                    ",o.showIcon?'
                    '+(l.normalizedIcon(a[c.icon])||o.icon||(a[u]?a[X]?o.iconOpen:o.iconClose:o.iconLeaf)||"")+"
                    ":"",n].join("")).find(".layui-table-tree-flexIcon").on("click",function(e){layui.stope(e),K({trElem:i},null,null,null,!0)}))}),!t&&r.view.expandAllDefault&&l.isExpandAll===undefined)return j.expandAll(i,!0);(!1!==a&&y?(layui.each(y,function(e,t){e=d.find('tr[lay-data-index="'+e+'"]');e.find(".layui-table-tree-flexIcon").html(o.flexIconOpen),K({trElem:e.first()},!0)}),J("renderTreeTable2-"+i,function(){h.render(P(".layui-table-tree["+L+'="'+i+'"]'))},0)):J("renderTreeTable-"+i,function(){n.hasNumberCol&&v(l),h.render(P(".layui-table-tree["+L+'="'+i+'"]'))},0))()},function(a){var e=a.getOptions(),t=e.elem.next(),n=0,i=t.find(".layui-table-main tbody tr"),l=t.find(".layui-table-fixed-l tbody tr"),d=t.find(".layui-table-fixed-r tbody tr");layui.each(a.treeToFlat(B.cache[e.id]),function(e,t){t.LAY_HIDE||(a.getNodeDataByIndex(t[H]).LAY_NUM=++n,i.eq(e).find(".laytable-cell-numbers").html(n),l.eq(e).find(".laytable-cell-numbers").html(n),d.eq(e).find(".laytable-cell-numbers").html(n))})}),N=(a.prototype.render=function(e){var t=this;t.tableIns=B["reloadData"===e?"reloadData":"reload"](t.tableIns.config.id,P.extend(!0,{},t.config)),t.config=t.tableIns.config},a.prototype.reload=function(e,t,a){var n=this;e=e||{},delete n.haveInit,layui.each(e,function(e,t){"array"===layui.type(t)&&delete n.config[e]}),d(n.getOptions().id,e,a||!0),n.config=P.extend(t,{},n.config,e),n.render(a)},j.reloadData=function(){var e=P.extend(!0,[],arguments);return e[3]="reloadData",j.reload.apply(null,e)},function(e,a,n,i){var l=[];return layui.each(e,function(e,t){"function"===layui.type(a)?a(t):P.extend(t,a),l.push(P.extend({},t)),i||(l=l.concat(N(t[n],a,n,i)))}),l}),o=(a.prototype.updateStatus=function(e,t,a){var n=this.getOptions(),i=n.tree;return e=e||B.cache[n.id],N(e,t,i.customName.children,a)},a.prototype.getTableData=function(){var e=this.getOptions();return B.cache[e.id]},j.updateStatus=function(e,t,a){var e=F(e),n=e.getOptions();return a=a||(n.url?B.cache[n.id]:n.data),e.updateStatus(a,t)},j.sort=function(e){var t,a,i,l,n,d=F(e);d&&(n=(t=d.getOptions()).tree,a=j.getData(e),i=n.customName.children,l=function(e,a,n){layui.sort(e,a,n,!0),layui.each(e,function(e,t){l(t[i]||[],a,n)})},t.autoSort)&&((n=t.initSort).type?l(a,n.field,"desc"===n.type):l(a,B.config.indexName,null),B.cache[e]=a,d.initData(a),j.renderData(e))},function(n){var t=n.config.id,i=F(t),a=n.data=j.getNodeDataByIndex(t,n.index),l=a[H],d=(n.dataIndex=l,n.update);n.update=function(){var e=arguments,t=(P.extend(i.getNodeDataByIndex(l),e[0]),d.apply(this,e)),a=n.config.tree.customName.name;return a in e[0]&&n.tr.find('td[data-field="'+a+'"]').children("div.layui-table-cell").removeClass("layui-table-tree-item"),i.renderTreeTable(n.tr,n.tr.attr("data-level"),!1),t},n.del=function(){j.removeNode(t,a)},n.setRowChecked=function(e){j.setRowChecked(t,{index:a,checked:e})}}),u=(j.updateNode=function(e,a,t){var n,i,l,d,r,o=F(e);o&&((d=o.getOptions()).tree,d=(n=d.elem.next()).find('tr[lay-data-index="'+a+'"]'),i=d.attr("data-index"),l=d.attr("data-level"),t)&&(d=o.getNodeDataByIndex(a,!1,t),r=B.getTrHtml(e,[d]),layui.each(["main","fixed-l","fixed-r"],function(e,t){n.find(".layui-table-"+t+' tbody tr[lay-data-index="'+a+'"]').replaceWith(P(r[["trs","trs_fixed","trs_fixed_r"][e]].join("")).attr({"data-index":i,"lay-data-index":a,"data-level":l}).data("index",i))}),o.renderTreeTable(n.find('tr[lay-data-index="'+a+'"]'),l))},j.removeNode=function(e,t,a){var n=F(e);if(n){var i,l=n.getOptions(),d=l.tree,r=d.customName.isParent,o=d.customName.children,c=l.elem.next(),u=[],s=B.cache[e],t=n.getNodeDataByIndex("string"===layui.type(t)?t:t[H],!1,"delete"),f=n.getNodeDataByIndex(t[p]),d=(n.updateCheckStatus(f),n.treeToFlat([t],t[d.customName.pid],t[p])),t=(layui.each(d,function(e,t){t=t[H];u.push('tr[lay-data-index="'+t+'"]'),-1!==t.indexOf("-")&&delete s[t]}),c.find(u.join(",")).remove(),n.initData());for(i in s)-1!==i.indexOf("-")&&i!==s[i][H]&&delete s[i];layui.each(n.treeToFlat(t),function(e,t){t[b]&&t[b]!==t[H]&&c.find('tr[lay-data-index="'+t[b]+'"]').attr({"data-index":t[H],"lay-data-index":t[H]}).data("index",t[H])}),layui.each(s,function(e,t){c.find('tr[data-level="0"][lay-data-index="'+t[H]+'"]').attr("data-index",e).data("index",e)}),l.hasNumberCol&&v(n),f&&(d=c.find('tr[lay-data-index="'+f[H]+'"]'),a||(f[r]=!(!f[o]||!f[o].length)),n.updateNodeIcon({scopeEl:d,isExpand:f[X],isParent:f[r]})),j.resize(e)}},j.addNodes=function(e,t){var a=F(e);if(a){var n=a.getOptions(),i=n.tree,l=n.elem.next(),d=B.config.checkName,r=(t=t||{}).parentIndex,o=t.index,c=t.data,t=t.focus,u=(r="number"===layui.type(r)?r.toString():r)?a.getNodeDataByIndex(r):null,o="number"===layui.type(o)?o:-1,c=P.extend(!0,[],layui.isArray(c)?c:[c]);layui.each(c,function(e,t){d in t||!u||(t[d]=u[d])}),a.getTableData();if(u){var s=i.customName.isParent,f=i.customName.children;u[s]=!0;var y=(y=u[f])?(p=y.splice(-1===o?y.length:o),u[f]=y.concat(c,p)):u[f]=c,f=(a.updateStatus(y,function(e){(e[s]||i.view.showFlexIconIfNotParent)&&(e[V]=!1)}),a.treeToFlat(y));l.find(f.map(function(e){return'tr[lay-data-index="'+e[H]+'"]'}).join(",")).remove(),a.initData(),u[V]=!1,u[U]="local",K({trElem:l.find('tr[lay-data-index="'+r+'"]')},!0)}else{var p=B.cache[e].splice(-1===o?B.cache[e].length:o);if(B.cache[e]=B.cache[e].concat(c,p),n.url||(n.page?(y=n.page,n.data.splice.apply(n.data,[y.limit*(y.curr-1),y.limit].concat(B.cache[e]))):n.data=B.cache[e]),a.initData(),l.find(".layui-none").length)return B.renderData(e),c;var x,f=B.getTrHtml(e,c),h={trs:P(f.trs.join("")),trs_fixed:P(f.trs_fixed.join("")),trs_fixed_r:P(f.trs_fixed_r.join(""))},r=(layui.each(c,function(e,t){x={"data-index":t[H],"lay-data-index":t[H],"data-level":"0"},h.trs.eq(e).attr(x).data("index",t[H]),h.trs_fixed.eq(e).attr(x).data("index",t[H]),h.trs_fixed_r.eq(e).attr(x).data("index",t[H])}),parseInt(c[0][H])-1),y=l.find(R),n=l.find(Y),f=l.find(z);-1==r?y.find('tr[data-level="0"][data-index="0"]')[0]?(y.find('tr[data-level="0"][data-index="0"]').before(h.trs),n.find('tr[data-level="0"][data-index="0"]').before(h.trs_fixed),f.find('tr[data-level="0"][data-index="0"]').before(h.trs_fixed_r)):(y.find("tbody").prepend(h.trs),n.find("tbody").prepend(h.trs_fixed),f.find("tbody").prepend(h.trs_fixed_r)):-1===o?(y.find("tbody").append(h.trs),n.find("tbody").append(h.trs_fixed),f.find("tbody").append(h.trs_fixed_r)):(r=p[0][b],y.find('tr[data-level="0"][data-index="'+r+'"]').before(h.trs),n.find('tr[data-level="0"][data-index="'+r+'"]').before(h.trs_fixed),f.find('tr[data-level="0"][data-index="'+r+'"]').before(h.trs_fixed_r)),layui.each(B.cache[e],function(e,t){l.find('tr[data-level="0"][lay-data-index="'+t[H]+'"]').attr("data-index",e).data("index",e)}),a.renderTreeTable(l.find(c.map(function(e,t,a){return'tr[lay-data-index="'+e[H]+'"]'}).join(",")))}return a.updateCheckStatus(u),u&&(o=l.find('tr[lay-data-index="'+u[H]+'"]'),a.updateNodeIcon({scopeEl:o,isExpand:u[X],isParent:u[s]})),j.resize(e),t&&l.find(R).find('tr[lay-data-index="'+c[0][H]+'"]').get(0).scrollIntoViewIfNeeded(),c}},j.checkStatus=function(e,n){var i,t,a,l=F(e);if(l)return l=l.getOptions().tree,i=B.config.checkName,t=j.getData(e,!0).filter(function(e,t,a){return e[i]||n&&e[g]}),a=!0,layui.each("all"===l.data.cascade?B.cache[e]:j.getData(e,!0),function(e,t){if(!t[i])return!(a=!1)}),{data:t,isAll:a}},j.on("sort",function(e){var e=e.config,t=e.elem.next(),e=e.id;t.hasClass(m)&&j.sort(e)}),j.on("row",function(e){e.config.elem.next().hasClass(m)&&o(e)}),j.on("rowDouble",function(e){var t=e.config,a=t.elem.next();t.id;a.hasClass(m)&&(o(e),(t.tree||{}).view.dblClickExpand)&&K({trElem:e.tr.first()},null,null,null,!0)}),j.on("rowContextmenu",function(e){var t=e.config,a=t.elem.next();t.id;a.hasClass(m)&&o(e)}),j.on("tool",function(e){var t=e.config,a=t.elem.next();t.id;a.hasClass(m)&&o(e)}),j.on("edit",function(e){var t=e.config,a=t.elem.next();t.id;a.hasClass(m)&&(o(e),e.field===t.tree.customName.name)&&((a={})[e.field]=e.value,e.update(a))}),j.on("radio",function(e){var t=e.config,a=t.elem.next(),t=t.id;a.hasClass(m)&&(a=F(t),o(e),u.call(a,e.tr,e.checked))}),a.prototype.setRowCheckedClass=function(e,t){var a=this.getOptions(),n=(e.data("index"),a.elem.next());e[t?"addClass":"removeClass"](l),e.each(function(){var e=P(this).data("index");n.find('.layui-table-fixed-r tbody tr[data-index="'+e+'"]')[t?"addClass":"removeClass"](l)})},a.prototype.updateCheckStatus=function(e,t){var a,n,i,l,d,r,o,c=this,u=c.getOptions();return!!u.hasChecboxCol&&(a=u.tree,n=u.id,i=u.elem.next(),l=B.config.checkName,"all"!==(d=a.data.cascade)&&"parent"!==d||!e||(d=c.updateParentCheckStatus(e,"boolean"===layui.type(t)?t:null),layui.each(d,function(e,t){var a=i.find('tr[lay-data-index="'+t[H]+'"] input[name="layTableCheckbox"]:not(:disabled)'),n=t[l];c.setRowCheckedClass(a.closest("tr"),n),a.prop({checked:n,indeterminate:t[g]})})),o=!(r=!0),0<(e=(e="all"===a.data.cascade?B.cache[n]:j.getData(n,!0)).filter(function(e){return!e[u.disabledName]})).length?layui.each(e,function(e,t){if((t[l]||t[g])&&(o=!0),t[l]||(r=!1),o&&!r)return!0}):r=!1,o=o&&!r,i.find('input[name="layTableCheckbox"][lay-filter="layTableAllChoose"]').prop({checked:r,indeterminate:o}),r)},a.prototype.updateParentCheckStatus=function(a,n){var i,e=this.getOptions(),t=e.tree,e=e.id,l=B.config.checkName,t=t.customName.children,d=[];return!(a[g]=!1)===n?a[t].length?layui.each(a[t],function(e,t){if(!t[l])return n=!1,a[g]=!0}):n=!1:!1===n?layui.each(a[t],function(e,t){if(t[l]||t[g])return a[g]=!0}):(n=!1,i=0,layui.each(a[t],function(e,t){t[l]&&i++}),n=a[t].length?a[t].length===i:a[l],a[g]=!n&&0li"],n.bodyElem=["."+C.CONST.BODY+":eq(0)",">."+C.CONST.ITEM],n.getContainer=function(){var e=n.documentElem||t.elem;return{header:{elem:e.find(n.headerElem[0]),items:e.find(n.headerElem.join(""))},body:{elem:e.find(n.bodyElem[0]),items:e.find(n.bodyElem.join(""))}}},"array"===layui.type(t.header)?"string"==typeof t.header[0]?(n.headerElem=t.header.concat(),n.documentElem=p(document)):(n.elemView=p('
                    '),t.className&&n.elemView.addClass(t.className),a=p('
                      '),i=p('
                      '),layui.each(t.header,function(e,t){t=n.renderHeaderItem(t);a.append(t)}),layui.each(t.body,function(e,t){t=n.renderBodyItem(t);i.append(t)}),n.elemView.append(a).append(i),t.elem.html(n.elemView)):n.renderClose(),"array"===layui.type(t.body)&&"string"==typeof t.body[0]&&(n.documentElem=p(document),n.bodyElem=t.body.concat()),n.data());"index"in t&&e.index!=t.index?n.change(n.findHeaderItem(t.index),!0):-1===e.index&&n.change(n.findHeaderItem(0),!0),n.roll("auto"),t.elem.hasClass(C.CONST.CLASS_HIDEV)&&t.elem.removeClass(C.CONST.CLASS_HIDEV),"function"==typeof t.afterRender&&t.afterRender(e),layui.event.call(t.elem[0],C.CONST.MOD_NAME,"afterRender("+t.id+")",e)},events:function(){var e,t=this,a=t.config,i=t.getContainer(),n=C.CONST.MOD_NAME,i=(t.documentElem?i.header:a).elem,a=a.trigger+(".lay_"+n+"_trigger"),n=t.documentElem?t.headerElem[1]:t.headerElem.join("");i.off(a).on(a,n,function(){t.change(p(this))}),r.onresize||(p(window).on("resize",function(){clearTimeout(e),e=setTimeout(function(){layui.each(C.cache.id,function(e){e=C.getInst(e);e&&e.roll("init")})},50)}),r.onresize=!0)}}),r={},t=C.Class;t.prototype.add=function(e){var t,a,i=this,n=(i.config,i.getContainer()),r=i.renderHeaderItem(e),d=i.renderBodyItem(e),o=i.data();e=p.extend({active:!0},e),/(before|after)/.test(e.mode)?(a=(t=e.hasOwnProperty("index"))?i.findHeaderItem(e.index):o.thisHeaderItem,t=t?i.findBodyItem(e.index):o.thisHeaderItem,a[e.mode](r),t[e.mode](d)):(a={prepend:"prepend",append:"append"}[e.mode||"append"]||"append",n.header.elem[a](r),n.body.elem[a](d)),e.active?i.change(r,!0):i.roll("auto"),"function"==typeof e.done&&e.done(p.extend(o,{headerItem:r,bodyItem:d}))},t.prototype.close=function(e,t){if(e&&e[0]){var a=this,i=a.config,n=e.attr("lay-id"),r=e.index();if("false"!==e.attr("lay-closable")){var d=a.data();if(!t)if(!1===layui.event.call(e[0],C.CONST.MOD_NAME,"beforeClose("+i.id+")",p.extend(d,{index:r})))return;e.hasClass(C.CONST.CLASS_THIS)&&(e.next()[0]?a.change(e.next(),!0):e.prev()[0]&&a.change(e.prev(),!0)),a.findBodyItem(n||r).remove(),e.remove(),a.roll("auto",r);d=a.data();layui.event.call(d.thisHeaderItem[0],C.CONST.MOD_NAME,"afterClose("+i.id+")",d)}}},t.prototype.closeMult=function(i,e){var n=this,t=n.config,a=n.getContainer(),r=n.data(),d=a.header.items,a=(a.body.items,'[lay-closable="false"]'),o=(e=e===undefined?r.index:e,n.findHeaderItem(e)),l=(n.findBodyItem(e),o.index()),r=("false"!==r.thisHeaderItem.attr("lay-closable")&&("all"!==i&&i?e!==r.index&&n.change(o,!0):(e=d.filter(":gt("+r.index+")"+a).eq(0),o=p(d.filter(":lt("+r.index+")"+a).get().reverse()).eq(0),e[0]?n.change(e,!0):o[0]&&n.change(o,!0))),d.each(function(e){var t=p(this),a=t.attr("lay-id"),a=n.findBodyItem(a||e);"false"!==t.attr("lay-closable")&&("other"===i&&e!==l||"right"===i&&l");return t.html(e.title||"New Tab").attr("lay-id",e.id),this.appendClose(t,e),t},t.prototype.renderBodyItem=function(e){var t=this.config,t=p(e.bodyItem||t.bodyItem||'
                      ');return t.html(e.content||"").attr("lay-id",e.id),t},t.prototype.appendClose=function(e,t){var a=this;a.config.closable&&(0==(t=t||{}).closable&&e.attr("lay-closable","false"),"false"===e.attr("lay-closable")||e.find("."+C.CONST.CLOSE)[0]||((t=p('')).on("click",function(){return a.close(p(this).parent()),!1}),e.append(t)))},t.prototype.renderClose=function(){var t=this,a=t.config;t.getContainer().header.items.each(function(){var e=p(this);a.closable?t.appendClose(e):e.find("."+C.CONST.CLOSE).remove()})},t.prototype.roll=function(e,i){var n=this,t=n.config,a=n.getContainer(),r=a.header.elem,d=a.header.items,a=r.prop("scrollWidth"),o=Math.ceil(r.outerWidth()),l=r.data("left")||0,s="scroll"===t.headerMode,c="layui-tabs-scroll",f="layui-tabs-bar",u=["layui-icon-prev","layui-icon-next"],m={elem:p('
                      '),bar:p(['
                      ','','',"
                      "].join(""))};if("normal"!==t.headerMode){var h,y=r.parent("."+c);if(s||!s&&o=o-l)return r.css("left",-a).data("left",-a),!1}),l=r.data("left")||0,y.find("."+u[0])[l<0?"removeClass":"addClass"](C.CONST.CLASS_DISABLED),y.find("."+u[1])[0
                      ')),n=(e.tree(a),i.elem=p(i.elem));if(n[0]){if(e.key=i.id||e.index,e.elem=a,e.elemNone=p('
                      '+i.text.none+"
                      "),n.html(e.elem),0==e.elem.find(".layui-tree-set").length)return e.elem.append(e.elemNone);i.showCheckbox&&e.renderForm("checkbox"),e.elem.find(".layui-tree-set").each(function(){var e=p(this);e.parent(".layui-tree-pack")[0]||e.addClass("layui-tree-setHide"),!e.next()[0]&&e.parents(".layui-tree-pack").eq(1).hasClass("layui-tree-lineExtend")&&e.addClass(T),e.next()[0]||e.parents(".layui-tree-set").eq(0).next()[0]||e.addClass(T)}),e.events()}},l.prototype.renderForm=function(e){i.render(e,"LAY-tree-"+this.index)},l.prototype.tree=function(r,e){var d=this,s=d.config,o=s.customName,e=e||s.data;layui.each(e,function(e,i){var a,n,t=i[o.children]&&0
                      "),c=p(['
                      ','
                      ','
                      ',s.showLine?t?'':'':'',s.showCheckbox?'':"",s.isJump&&i.href?''+(i[o.title]||i.label||s.text.defaultNodeName)+"":''+(i[o.title]||i.label||s.text.defaultNodeName)+"","
                      ",s.edit?(a={add:'',update:'',del:''},n=['
                      '],!0===s.edit&&(s.edit=["update","del"]),"object"==typeof s.edit?(layui.each(s.edit,function(e,i){n.push(a[i]||"")}),n.join("")+"
                      "):void 0):"","
                      "].join(""));t&&(c.append(l),d.tree(l,i[o.children])),r.append(c),c.prev("."+k)[0]&&c.prev().children(".layui-tree-pack").addClass("layui-tree-showLine"),t||c.parent(".layui-tree-pack").addClass("layui-tree-lineExtend"),d.spread(c,i),s.showCheckbox&&(i.checked&&d.checkids.push(i[o.id]),d.checkClick(c,i)),s.edit&&d.operate(c,i)})},l.prototype.spread=function(n,t){var l=this,c=l.config,e=n.children("."+b),i=e.children("."+g),a=i.find('input[same="layuiTreeCheck"]'),r=e.find("."+C),e=e.find("."+w),d=c.onlyIconControl?r:i,s="";d.on("click",function(e){var i=n.children("."+N),a=(d.children(".layui-icon")[0]?d:d.find(".layui-tree-icon")).children(".layui-icon");i[0]?n.hasClass(F)?(n.removeClass(F),i.slideUp(200),a.removeClass(x).addClass(v),l.updateFieldValue(t,"spread",!1)):(n.addClass(F),i.slideDown(200),a.addClass(x).removeClass(v),l.updateFieldValue(t,"spread",!0),c.accordion&&((i=n.siblings("."+k)).removeClass(F),i.children("."+N).slideUp(200),i.find(".layui-tree-icon").children(".layui-icon").removeClass(x).addClass(v))):s="normal"}),e.on("click",function(){p(this).hasClass(u)||(s=n.hasClass(F)?c.onlyIconControl?"open":"close":c.onlyIconControl?"close":"open",a[0]&&l.updateFieldValue(t,"checked",a.prop("checked")),c.click&&c.click({elem:n,state:s,data:t}))})},l.prototype.updateFieldValue=function(e,i,a){i in e&&(e[i]=a)},l.prototype.setCheckbox=function(e,i,a){var t,n=this,l=n.config.customName,c=a.prop("checked");a.prop("disabled")||("object"!=typeof i[l.children]&&!e.find("."+N)[0]||e.find("."+N).find('input[same="layuiTreeCheck"]').each(function(e){this.disabled||((e=i[l.children][e])&&n.updateFieldValue(e,"checked",c),n.updateFieldValue(this,"checked",c))}),(t=function(e){var i,a,n;e.parents("."+k)[0]&&(a=(e=e.parent("."+N)).parent(),n=e.prev().find('input[same="layuiTreeCheck"]'),c?n.prop("checked",c):(e.find('input[same="layuiTreeCheck"]').each(function(){this.checked&&(i=!0)}),i||n.prop("checked",!1)),t(a))})(e),n.renderForm("checkbox"))},l.prototype.checkClick=function(a,n){var t=this,l=t.config;a.children("."+b).children("."+g).on("click",'input[same="layuiTreeCheck"]+',function(e){layui.stope(e);var e=p(this).prev(),i=e.prop("checked");e.prop("disabled")||(t.setCheckbox(a,n,e),t.updateFieldValue(n,"checked",i),l.oncheck&&l.oncheck({elem:a,checked:i,data:n}))})},l.prototype.operate=function(r,d){var s=this,o=s.config,u=o.customName,e=r.children("."+b),h=e.children("."+g);e.children(".layui-tree-btnGroup").on("click",".layui-icon",function(e){layui.stope(e);var i,e=p(this).data("type"),n=r.children("."+N),t={data:d,type:e,elem:r};if("add"==e){n[0]||(o.showLine?(h.find("."+C).addClass("layui-tree-icon"),h.find("."+C).children(".layui-icon").addClass(v).removeClass("layui-icon-file")):h.find(".layui-tree-iconArrow").removeClass(m),r.append('
                      '));var a,l=o.operate&&o.operate(t),c={};if(c[u.title]=o.text.defaultNodeName,c[u.id]=l,s.tree(r.children("."+N),[c]),o.showLine&&(n[0]?(n.hasClass(S)||n.addClass(S),r.find("."+N).each(function(){p(this).children("."+k).last().addClass(T)}),(n.children("."+k).last().prev().hasClass(T)?n.children("."+k).last().prev():n.children("."+k).last()).removeClass(T),!r.parent("."+N)[0]&&r.next()[0]&&n.children("."+k).last().removeClass(T)):(l=r.siblings("."+k),a=1,c=r.parent("."+N),layui.each(l,function(e,i){p(i).children("."+N)[0]||(a=0)}),(1==a?(l.children("."+N).addClass(L),l.children("."+N).children("."+k).removeClass(T),r.children("."+N).addClass(L),c.removeClass(S),c.children("."+k).last().children("."+N).children("."+k).last()):r.children("."+N).children("."+k)).addClass(T))),!o.showCheckbox)return;h.find('input[same="layuiTreeCheck"]')[0].checked&&(r.children("."+N).children("."+k).last().find('input[same="layuiTreeCheck"]')[0].checked=!0),s.renderForm("checkbox")}else"update"==e?(l=h.children("."+w).html(),h.children("."+w).html(""),h.append(''),h.children(".layui-tree-editInput").val(f.unescape(l)).focus(),i=function(e){var i=f.escape(e.val().trim())||o.text.defaultNodeName;e.remove(),h.children("."+w).html(i),t.data[u.title]=i,o.operate&&o.operate(t)},h.children(".layui-tree-editInput").blur(function(){i(p(this))}),h.children(".layui-tree-editInput").on("keydown",function(e){13===e.keyCode&&(e.preventDefault(),i(p(this)))})):y.confirm('\u786e\u8ba4\u5220\u9664\u8be5\u8282\u70b9 "'+(d[u.title]||"")+'" \u5417\uff1f',function(e){var l,a,i;o.operate&&o.operate(t),t.status="remove",y.close(e),r.prev("."+k)[0]||r.next("."+k)[0]||r.parent("."+N)[0]?(r.siblings("."+k).children("."+b)[0]?(o.showCheckbox&&(l=function(e){var i,a,n,t;e.parents("."+k)[0]&&(i=e.siblings("."+k).children("."+b),a=(e=e.parent("."+N).prev()).find('input[same="layuiTreeCheck"]')[0],n=1,(t=0)==a.checked)&&(i.each(function(e,i){i=p(i).find('input[same="layuiTreeCheck"]')[0];0!=i.checked||i.disabled||(n=0),i.disabled||(t=1)}),1==n)&&1==t&&(a.checked=!0,s.renderForm("checkbox"),l(e.parent("."+k)))})(r),o.showLine&&(e=r.siblings("."+k),a=1,i=r.parent("."+N),layui.each(e,function(e,i){p(i).children("."+N)[0]||(a=0)}),1==a?(n[0]||(i.removeClass(S),e.children("."+N).addClass(L),e.children("."+N).children("."+k).removeClass(T)),(r.next()[0]?i.children("."+k).last():r.prev()).children("."+N).children("."+k).last().addClass(T),r.next()[0]||r.parents("."+k)[1]||r.parents("."+k).eq(0).next()[0]||r.prev("."+k).addClass(T)):!r.next()[0]&&r.hasClass(T)&&r.prev().addClass(T))):(e=r.parent("."+N).prev(),o.showLine?(e.find("."+C).removeClass("layui-tree-icon"),e.find("."+C).children(".layui-icon").removeClass(x).addClass("layui-icon-file"),(i=e.parents("."+N).eq(0)).addClass(S),i.children("."+k).each(function(){p(this).children("."+N).children("."+k).last().addClass(T)})):e.find(".layui-tree-iconArrow").addClass(m),r.parents("."+k).eq(0).removeClass(F),r.parent("."+N).remove()),r.remove()):(r.remove(),s.elem.append(s.elemNone))})})},l.prototype.events=function(){var i=this,t=i.config;i.elem.find(".layui-tree-checkedFirst");i.setChecked(i.checkids),i.elem.find(".layui-tree-search").on("keyup",function(){var e=p(this),a=e.val(),e=e.nextAll(),n=[];e.find("."+w).each(function(){var i,e=p(this).parents("."+b);-1!=p(this).html().indexOf(a)&&(n.push(p(this).parent()),(i=function(e){e.addClass("layui-tree-searchShow"),e.parent("."+N)[0]&&i(e.parent("."+N).parent("."+k))})(e.parent("."+k)))}),e.find("."+b).each(function(){var e=p(this).parent("."+k);e.hasClass("layui-tree-searchShow")||e.addClass(m)}),0==e.find(".layui-tree-searchShow").length&&i.elem.append(i.elemNone),t.onsearch&&t.onsearch({elem:n})}),i.elem.find(".layui-tree-search").on("keydown",function(){p(this).nextAll().find("."+b).each(function(){p(this).parent("."+k).removeClass("layui-tree-searchShow "+m)}),p(".layui-tree-emptyText")[0]&&p(".layui-tree-emptyText").remove()})},l.prototype.getChecked=function(){var t=this,e=t.config,l=e.customName,i=[],a=[],c=(t.elem.find(".layui-form-checked").each(function(){i.push(p(this).prev()[0].value)}),function(e,n){layui.each(e,function(e,a){layui.each(i,function(e,i){if(a[l.id]==i)return t.updateFieldValue(a,"checked",!0),delete(i=p.extend({},a))[l.children],n.push(i),a[l.children]&&(i[l.children]=[],c(a[l.children],i[l.children])),!0})})});return c(p.extend({},e.data),a),a},l.prototype.setChecked=function(l){this.config;this.elem.find("."+k).each(function(e,i){var a=p(this).data("id"),n=p(i).children("."+b).find('input[same="layuiTreeCheck"]'),t=n.next();if("number"==typeof l){if(a.toString()==l.toString())return n[0].checked||t.click(),!1}else"object"==typeof l&&layui.each(l,function(e,i){if(i.toString()==a.toString()&&!n[0].checked)return t.click(),!0})})},n.that={},n.config={},t.reload=function(e,i){e=n.that[e];return e.reload(i),n.call(e)},t.getChecked=function(e){return n.that[e].getChecked()},t.setChecked=function(e,i){return n.that[e].setChecked(i)},t.render=function(e){e=new l(e);return n.call(e)},e(a,t)});layui.define(["laytpl","form"],function(e){"use strict";var d=layui.$,n=layui.laytpl,t=layui.form,a="transfer",i={config:{},index:layui[a]?layui[a].index+1e4:0,set:function(e){var t=this;return t.config=d.extend({},t.config,e),t},on:function(e,t){return layui.onevent.call(this,a,e,t)}},l=function(){var t=this,e=t.config,a=e.id||t.index;return l.that[a]=t,{config:l.config[a]=e,reload:function(e){t.reload.call(t,e)},getData:function(){return t.getData.call(t)}}},s="layui-hide",u="layui-btn-disabled",c="layui-none",r="layui-transfer-box",h="layui-transfer-header",o="layui-transfer-search",f="layui-transfer-data",y=function(e){return['
                      ','
                      ','","
                      ","{{# if(d.data.showSearch){ }}",'","{{# } }}",'
                        ',"
                        "].join("")},p=['
                        ',y({index:0,checkAllName:"layTransferLeftCheckAll"}),'
                        ','",'","
                        ",y({index:1,checkAllName:"layTransferRightCheckAll"}),"
                        "].join(""),v=function(e){var t=this;t.index=++i.index,t.config=d.extend({},t.config,i.config,e),t.render()};v.prototype.config={title:["\u5217\u8868\u4e00","\u5217\u8868\u4e8c"],width:200,height:360,data:[],value:[],showSearch:!1,id:"",text:{none:"\u65e0\u6570\u636e",searchNone:"\u65e0\u5339\u914d\u6570\u636e"}},v.prototype.reload=function(e){var t=this;t.config=d.extend({},t.config,e),t.render()},v.prototype.render=function(){var e=this,t=e.config,a=e.elem=d(n(p,{open:"{{",close:"}}",tagStyle:"legacy"}).render({data:t,index:e.index})),i=t.elem=d(t.elem);i[0]&&(t.data=t.data||[],t.value=t.value||[],t.id="id"in t?t.id:elem.attr("id")||e.index,e.key=t.id,i.html(e.elem),e.layBox=e.elem.find("."+r),e.layHeader=e.elem.find("."+h),e.laySearch=e.elem.find("."+o),e.layData=a.find("."+f),e.layBtn=a.find(".layui-transfer-active .layui-btn"),e.layBox.css({width:t.width,height:t.height}),e.layData.css({height:(i=t.height-e.layHeader.outerHeight(),t.showSearch&&(i-=e.laySearch.outerHeight()),i-2)}),e.renderData(),e.events())},v.prototype.renderData=function(){var e=this,t=e.config,l=[{checkName:"layTransferLeftCheck",views:[]},{checkName:"layTransferRightCheck",views:[]}];e.parseData(function(a){var i=a.selected?1:0,n=["
                      • ",'',"
                      • "].join("");i?layui.each(t.value,function(e,t){t==a.value&&a.selected&&(l[i].views[e]=n)}):l[i].views.push(n),delete a.selected}),e.layData.eq(0).html(l[0].views.join("")),e.layData.eq(1).html(l[1].views.join("")),e.renderCheckBtn()},v.prototype.renderForm=function(e){t.render(e,"LAY-transfer-"+this.index)},v.prototype.renderCheckBtn=function(c){var r=this,o=r.config;c=c||{},r.layBox.each(function(e){var t=d(this),a=t.find("."+f),t=t.find("."+h).find('input[type="checkbox"]'),i=a.find('input[type="checkbox"]'),n=0,l=!1;i.each(function(){var e=d(this).data("hide");(this.checked||this.disabled||e)&&n++,this.checked&&!e&&(l=!0)}),t.prop("checked",l&&n===i.length),r.layBtn.eq(e)[l?"removeClass":"addClass"](u),c.stopNone||(i=a.children("li:not(."+s+")").length,r.noneView(a,i?"":o.text.none))}),r.renderForm("checkbox")},v.prototype.noneView=function(e,t){var a=d('

                        '+(t||"")+"

                        ");e.find("."+c)[0]&&e.find("."+c).remove(),t.replace(/\s/g,"")&&e.append(a)},v.prototype.setValue=function(){var e=this.config,t=[];return this.layBox.eq(1).find("."+f+' input[type="checkbox"]').each(function(){d(this).data("hide")||t.push(this.value)}),e.value=t,this},v.prototype.parseData=function(t){var i=this.config,n=[];return layui.each(i.data,function(e,a){a=("function"==typeof i.parseData?i.parseData(a):a)||a,n.push(a=d.extend({},a)),layui.each(i.value,function(e,t){t==a.value&&(a.selected=!0)}),t&&t(a)}),i.data=n,this},v.prototype.getData=function(e){var t=this.config,i=[];return this.setValue(),layui.each(e||t.value,function(e,a){layui.each(t.data,function(e,t){delete t.selected,a==t.value&&i.push(t)})}),i},v.prototype.transfer=function(e,t){var a,i=this,n=i.config,l=i.layBox.eq(e),c=[],t=(t?((a=(t=t).find('input[type="checkbox"]'))[0].checked=!1,l.siblings("."+r).find("."+f).append(t.clone()),t.remove(),c.push(a[0].value),i.setValue()):l.each(function(e){d(this).find("."+f).children("li").each(function(){var e=d(this),t=e.find('input[type="checkbox"]'),a=t.data("hide");t[0].checked&&!a&&(t[0].checked=!1,l.siblings("."+r).find("."+f).append(e.clone()),e.remove(),c.push(t[0].value)),i.setValue()})}),i.renderCheckBtn(),l.siblings("."+r).find("."+o+" input"));""!==t.val()&&t.trigger("keyup"),n.onchange&&n.onchange(i.getData(c),e)},v.prototype.events=function(){var n=this,l=n.config;n.elem.on("click",'input[lay-filter="layTransferCheckbox"]+',function(){var e=d(this).prev(),t=e[0].checked,a=e.parents("."+r).eq(0).find("."+f);e[0].disabled||("all"===e.attr("lay-type")&&a.find('input[type="checkbox"]').each(function(){this.disabled||(this.checked=t)}),setTimeout(function(){n.renderCheckBtn({stopNone:!0})},0))}),n.elem.on("dblclick","."+f+">li",function(e){var t=d(this),a=t.children('input[type="checkbox"]'),i=t.parent().parent().data("index");a[0].disabled||!1!==("function"==typeof l.dblclick?l.dblclick({elem:t,data:n.getData([a[0].value])[0],index:i}):null)&&n.transfer(i,t)}),n.layBtn.on("click",function(){var e=d(this),t=e.data("index");e.hasClass(u)||n.transfer(t)}),n.laySearch.find("input").on("keyup",function(){var i=this.value,e=d(this).parents("."+o).eq(0).siblings("."+f),t=e.children("li"),t=(t.each(function(){var e=d(this),t=e.find('input[type="checkbox"]'),a=t[0].title,a=("cs"!==l.showSearch&&(a=a.toLowerCase(),i=i.toLowerCase()),-1!==a.indexOf(i));e[a?"removeClass":"addClass"](s),t.data("hide",!a)}),n.renderCheckBtn(),t.length===e.children("li."+s).length);n.noneView(e,t?l.text.searchNone:"")})},l.that={},l.config={},i.reload=function(e,t){e=l.that[e];return e.reload(t),l.call(e)},i.getData=function(e){return l.that[e].getData()},i.render=function(e){e=new v(e);return l.call(e)},e(a,i)});layui.define(["jquery","lay"],function(e){"use strict";var o=layui.$,l=layui.lay,t=(layui.hint(),layui.device(),{config:{},set:function(e){var n=this;return n.config=o.extend({},n.config,e),n},on:function(e,n){return layui.onevent.call(this,r,e,n)}}),r="carousel",s="layui-this",u="layui-carousel-left",c="layui-carousel-right",m="layui-carousel-prev",h="layui-carousel-next",a="layui-carousel-arrow",d="layui-carousel-ind",n=function(e){var n=this;n.config=o.extend({},n.config,t.config,e),n.render()};n.prototype.config={width:"600px",height:"280px",full:!1,arrow:"hover",indicator:"inside",autoplay:!0,interval:3e3,anim:"",trigger:"click",index:0},n.prototype.render=function(){var e=this,n=e.config,i=o(n.elem);if(1*[carousel-item]>*"),n.index<0&&(n.index=0),n.index>=e.elemItem.length&&(n.index=e.elemItem.length-1),n.interval<800&&(n.interval=800),n.full?n.elem.css({position:"fixed",width:"100%",height:"100%",zIndex:9999}):n.elem.css({width:n.width,height:n.height}),n.elem.attr("lay-anim",n.anim),e.elemItem.eq(n.index).addClass(s),e.indicator(),e.arrow(),e.autoplay(),1=this.elemItem.length?0:e},n.prototype.addIndex=function(e){var n=this.config;n.index=n.index+(e=e||1),n.index>=this.elemItem.length&&(n.index=0)},n.prototype.subIndex=function(e){var n=this.config;n.index=n.index-(e=e||1),n.index<0&&(n.index=this.elemItem.length-1)},n.prototype.autoplay=function(){var e=this,n=e.config,i=e.elemItem.length;n.autoplay&&(clearInterval(e.timer),1',''].join(""));e.elem.attr("lay-arrow",e.arrow),e.elem.find("."+a)[0]&&e.elem.find("."+a).remove(),1i.index?n.slide("add",e-i.index):e
                          ',(n=[],layui.each(e.elemItem,function(e){n.push("")}),n.join("")),"
                        "].join(""));i.elem.attr("lay-indicator",i.indicator),i.elem.find("."+d)[0]&&i.elem.find("."+d).remove(),1t[a?"height":"width"]()/3)&&o.slide(0a.length&&(a.value=a.length),parseInt(a.value)===a.value||a.half||(a.value=Math.ceil(a.value)-a.value<.5?Math.ceil(a.value):Math.floor(a.value)),'
                          "),i=1;i<=a.length;i++){var n='
                        • ";a.half&&parseInt(a.value)!==a.value&&i==Math.ceil(a.value)?t=t+'
                        • ":t+=n}t+="
                        ",a.text&&(t+=''+a.value+"");var s=a.elem,o=s.next("."+c.ELEM);o[0]&&o.remove(),e.elemTemplate=u(t),a.span=e.elemTemplate.next("span"),a.setText&&a.setText(a.value),s.html(e.elemTemplate),s.addClass("layui-inline"),a.readonly||e.action()},extendsInstance:function(){var a=this,l=a.config;return{setvalue:function(e){l.value=e,a.render()}}}}),c=l.CONST;l.Class.prototype.action=function(){var n=this.config,t=this.elemTemplate,i=t.find("i").width(),l=t.children("li");l.each(function(e){var a=e+1,l=u(this);l.on("click",function(e){n.value=a,n.half&&e.pageX-u(this).offset().left<=i/2&&(n.value=n.value-.5),n.text&&t.next("span").text(n.value),n.choose&&n.choose(n.value),n.setText&&n.setText(n.value)}),l.on("mousemove",function(e){t.find("i").each(function(){u(this).addClass(c.ICON_RATE).removeClass(c.ICON_SOLID_HALF)}),t.find("i:lt("+a+")").each(function(){u(this).addClass(c.ICON_RATE_SOLID).removeClass(c.ICON_HALF_RATE)}),n.half&&e.pageX-u(this).offset().left<=i/2&&l.children("i").addClass(c.ICON_RATE_HALF).removeClass(c.ICON_RATE_SOLID)}),l.on("mouseleave",function(){t.find("i").each(function(){u(this).addClass(c.ICON_RATE).removeClass(c.ICON_SOLID_HALF)}),t.find("i:lt("+Math.floor(n.value)+")").each(function(){u(this).addClass(c.ICON_RATE_SOLID).removeClass(c.ICON_HALF_RATE)}),n.half&&parseInt(n.value)!==n.value&&t.children("li:eq("+Math.floor(n.value)+")").children("i").addClass(c.ICON_RATE_HALF).removeClass(c.ICON_SOLID_RATE)})}),a.touchSwipe(t,{onTouchMove:function(e,a){var i;Date.now()-a.timeStart<=200||(a=e.touches[0].pageX,e=t.width()/n.length,a=(a-t.offset().left)/e,(i=(i=(e=a%1)<=.5&&n.half?.5+(a-e):Math.ceil(a))>n.length?n.length:i)<0&&(i=0),l.each(function(e){var a=u(this).children("i"),l=Math.ceil(i)-e==1,t=Math.ceil(i)>e,e=i-e==.5;t?(a.addClass(c.ICON_RATE_SOLID).removeClass(c.ICON_HALF_RATE),n.half&&e&&a.addClass(c.ICON_RATE_HALF).removeClass(c.ICON_RATE_SOLID)):a.addClass(c.ICON_RATE).removeClass(c.ICON_SOLID_HALF),a.toggleClass("layui-rate-hover",l)}),n.value=i,n.text&&t.next("span").text(n.value),n.setText&&n.setText(n.value))},onTouchEnd:function(e,a){Date.now()-a.timeStart<=200||(t.find("i").removeClass("layui-rate-hover"),n.choose&&n.choose(n.value),n.setText&&n.setText(n.value))}})},e(c.MOD_NAME,l)});layui.define("jquery",function(o){"use strict";var w=layui.$,l=function(o){};l.prototype.load=function(o){var i,n,r,l,c,m,e,t,a,f,s,u,p,d,y,g=this,h=0,v=w((o=o||{}).elem);if(v[0])return c=w(o.scrollElem||document),m="mb"in o?o.mb:50,e=!("isAuto"in o)||o.isAuto,t=o.moreText||"\u52a0\u8f7d\u66f4\u591a",a=o.end||"\u6ca1\u6709\u66f4\u591a\u4e86",f="top"===(o.direction||"bottom"),g._cleanup(v,c),s=o.scrollElem&&o.scrollElem!==document,p=w('"),v.find(".layui-flow-more")[0]||v[f?"prepend":"append"](p),d=function(o,l){var e=s?c.prop("scrollHeight"):document.documentElement.scrollHeight,t=c.scrollTop();o=w(o),p[f?"after":"before"](o),(l=0==l||null)?p.html(a):p.find("a").html(u),n=l,i=null,r&&r(),f&&(o=s?c.prop("scrollHeight"):document.documentElement.scrollHeight,1===h?c.scrollTop(o):1'),"function"==typeof o.done&&o.done(++h,d)})(),p.find("a").on("click.flow",function(){w(this);n||i||y()}),o.isLazyimg&&(r=g.lazyimg({elem:o.elem+" img",scrollElem:o.scrollElem,direction:o.direction})),e&&c.on("scroll.flow",function(){var e=w(this),t=e.scrollTop();l&&clearTimeout(l),!n&&v.width()&&(l=setTimeout(function(){var o=(s?e:w(window)).height(),l=s?e.prop("scrollHeight"):document.documentElement.scrollHeight;(f?t<=m:l-t-o<=m)&&!i&&y()},100))}),g},l.prototype.lazyimg=function(o){var l,m=this,a=0,f=w((o=o||{}).scrollElem||document),s=o.elem||"img",n="top"===(o.direction||"bottom"),u=o.scrollElem&&o.scrollElem!==document,p=function(l,o){var e,t=f.scrollTop(),o=t+o,i=u?l.offset().top-f.offset().top+t:l.offset().top;(n?i+l.height():i)>=t&&i<=o&&l.attr("lay-src")&&(e=l.attr("lay-src"),layui.img(e,function(){var o=m.lazyimg.elem.eq(a);l.attr("src",e).removeAttr("lay-src"),o[0]&&r(o),a++},function(){m.lazyimg.elem.eq(a);l.removeAttr("lay-src")}))},r=function(o,l){var e=(u?l||f:w(window)).height(),t=f.scrollTop(),i=t+e;if(m.lazyimg.elem=w(s),o)p(o,e);else for(var n=0;n"),preview:"Preview"},wordWrap:!0,lang:"text",highlighter:!1,langMarker:!1},R=layui.code?layui.code.index+1e4:0,j=function(e){return String(e).replace(/\s+$/,"").replace(/^\n|\n$/,"")};e("code",function(l,e){var o,i,t,a,n,d,c,s,r,u,y,p,E,f,h,v,m,L,_,M,C,g={config:l=x.extend(!0,{},T,l),reload:function(e){layui.code(this.updateOptions(e))},updateOptions:function(e){return delete(e=e||{}).elem,x.extend(!0,l,e)},reloadCode:function(e){layui.code(this.updateOptions(e),"reloadCode")}},w=x(l.elem);return 1',l.ln?['
                        ',D.digit(t+1)+".","
                        "].join(""):"",'
                        ',e||" ","
                        ","
                        "].join("")})}},a=l.code,n=function(e){return"function"==typeof l.codeParse?l.codeParse(e,l):e},"reloadCode"===e?o.children(".layui-code-wrap").html(w(n(a)).html):(d=layui.code.index=++R,o.attr("lay-code-index",d),(M=A.CDDE_DATA_CLASS in o.data())&&o.attr("class",o.data(A.CDDE_DATA_CLASS)||""),M||o.data(A.CDDE_DATA_CLASS,o.attr("class")),c={copy:{className:"file-b",title:["\u590d\u5236\u4ee3\u7801"],event:function(e){var t=D.unescape(n(l.code)),a="function"==typeof l.onCopy;lay.clipboard.writeText({text:t,done:function(){if(a&&!1===l.onCopy(t,!0))return;W.msg("\u5df2\u590d\u5236",{icon:1})},error:function(){if(a&&!1===l.onCopy(t,!1))return;W.msg("\u590d\u5236\u5931\u8d25",{icon:2})}})}}},function b(){var e=o.parent("."+A.ELEM_PREVIEW),t=e.children("."+A.ELEM_TAB),a=e.children("."+A.ELEM_ITEM+"-preview");return t.remove(),a.remove(),e[0]&&o.unwrap(),b}(),l.preview&&(M="LAY-CODE-DF-"+d,f=l.layout||["code","preview"],s="iframe"===l.preview,E=x('
                        '),C=x('
                        '),r=x('
                        '),_=x('
                        '),u=x('
                        '),l.id&&E.attr("id",l.id),E.addClass(l.className),C.attr("lay-filter",M),layui.each(f,function(e,t){var a=x('
                      • ');0===e&&a.addClass("layui-this"),a.html(l.text[t]),r.append(a)}),x.extend(c,{full:{className:"screen-full",title:["\u6700\u5927\u5316\u663e\u793a","\u8fd8\u539f\u663e\u793a"],event:function(e){var e=e.elem,t=e.closest("."+A.ELEM_PREVIEW),a="layui-icon-"+this.className,i="layui-icon-screen-restore",l=this.title,o=x("html,body"),n="layui-scrollbar-hide";e.hasClass(a)?(t.addClass(A.ELEM_FULL),e.removeClass(a).addClass(i),e.attr("title",l[1]),o.addClass(n)):(t.removeClass(A.ELEM_FULL),e.removeClass(i).addClass(a),e.attr("title",l[0]),o.removeClass(n))}},window:{className:"release",title:["\u5728\u65b0\u7a97\u53e3\u9884\u89c8"],event:function(e){D.openWin({content:n(l.code)})}}}),l.copy&&("array"===layui.type(l.tools)?-1===l.tools.indexOf("copy")&&l.tools.unshift("copy"):l.tools=["copy"]),u.on("click",">i",function(){var e=x(this),t=e.data("type"),e={elem:e,type:t,options:l,rawCode:l.code,finalCode:D.unescape(n(l.code))};c[t]&&"function"==typeof c[t].event&&c[t].event(e),"function"==typeof l.toolsEvent&&l.toolsEvent(e)}),l.addTools&&l.tools&&(l.tools=[].concat(l.tools,l.addTools)),layui.each(l.tools,function(e,t){var a="object"==typeof t,i=a?t:c[t]||{className:t,title:[t]},l=i.className||i.type,o=i.title||[""],a=a?i.type||l:t;a&&(c[a]||((t={})[a]=i,x.extend(c,t)),u.append(''))}),o.addClass(A.ELEM_ITEM).wrap(E),C.append(r),l.tools&&C.append(u),o.before(C),s&&_.html(''),y=function(e){var t=e.children("iframe")[0];s&&t?t.srcdoc=n(l.code):e.html(l.code),setTimeout(function(){"function"==typeof l.done&&l.done({container:e,options:l,render:function(){N.render(e.find(".layui-form")),S.render(),I.render({elem:["."+A.ELEM_PREVIEW,".layui-tabs"].join(" ")})}})},3)},"preview"===f[0]?(_.addClass(A.ELEM_SHOW),o.before(_),y(_)):o.addClass(A.ELEM_SHOW).after(_),l.previewStyle=[l.style,l.previewStyle].join(""),_.attr("style",l.previewStyle),S.on("tab("+M+")",function(e){var t=x(this),a=x(e.elem).closest("."+A.ELEM_PREVIEW).find("."+A.ELEM_ITEM),e=a.eq(e.index);a.removeClass(A.ELEM_SHOW),e.addClass(A.ELEM_SHOW),"preview"===t.attr("lay-id")&&y(e),L()})),p=x(''),o.addClass((E=["layui-code-view layui-border-box"],l.wordWrap||E.push("layui-code-nowrap"),E.join(" "))),(C=l.theme||l.skin)&&(o.removeClass("layui-code-theme-dark layui-code-theme-light"),o.addClass("layui-code-theme-"+C)),l.highlighter&&o.addClass([l.highlighter,"language-"+l.lang,"layui-code-hl"].join(" ")),f=w(l.encode?D.escape(n(a)):a),h=f.lines,o.html(p.html(f.html)),l.ln&&o.append('
                        '),l.height&&p.css("max-height",l.height),l.codeStyle=[l.style,l.codeStyle].join(""),l.codeStyle&&p.attr("style",function(e,t){return(t||"")+l.codeStyle}),v=[{selector:">.layui-code-wrap>.layui-code-line{}",setValue:function(e,t){e.style["padding-left"]=t+"px"}},{selector:">.layui-code-wrap>.layui-code-line>.layui-code-line-number{}",setValue:function(e,t){e.style.width=t+"px"}},{selector:">.layui-code-ln-side{}",setValue:function(e,t){e.style.width=t+"px"}}],m=lay.style({target:o[0],id:"DF-code-"+d,text:x.map(x.map(v,function(e){return e.selector}),function(e,t){return['.layui-code-view[lay-code-index="'+d+'"]',e].join(" ")}).join("")}),L=function b(){var e,i;return l.ln&&(e=Math.floor(h.length/100),i=p.children("."+A.ELEM_LINE).last().children("."+A.ELEM_LINE_NUM).outerWidth(),o.addClass(A.ELEM_LN_MODE),e)&&A.LINE_RAW_WIDTH
                      • ')).html(l.title||l.text.code),o.prepend(_)),M=x('
                        '),l.copy&&!l.preview&&((C=x(['','',""].join(""))).on("click",function(){c.copy.event()}),M.append(C)),l.langMarker&&M.append(''+l.lang+""),l.about&&M.append(l.about),o.append(M),l.preview||setTimeout(function(){"function"==typeof l.done&&l.done({})},3),l.elem.length===1+d&&"function"==typeof l.allDone&&l.allDone())),g})}),layui["layui.all"]||layui.addcss("modules/code.css?v=6","skincodecss"); \ No newline at end of file diff --git a/public/tac/css/tac.css b/public/tac/css/tac.css new file mode 100644 index 0000000..ee76ff1 --- /dev/null +++ b/public/tac/css/tac.css @@ -0,0 +1,7 @@ +#tianai-captcha-parent{box-shadow:0 0 11px 0 #999;width:318px;height:318px;overflow:hidden;position:relative;z-index:997;box-sizing:border-box;border-radius:5px;padding:8px}#tianai-captcha-parent #tianai-captcha-box{height:260px;width:100%;position:relative;overflow:hidden}#tianai-captcha-parent #tianai-captcha-box .loading{width:120px;height:20px;-webkit-mask:linear-gradient(90deg, #000 70%, rgba(0, 0, 0, 0) 0) 0/20%;background:linear-gradient(#f7b645 0 0) 0/0% no-repeat rgba(221,221,221,.4196078431);animation:cartoon 1s infinite steps(6);margin:120px auto}@keyframes cartoon{100%{background-size:120%}}#tianai-captcha-parent #tianai-captcha-box #tianai-captcha{transform-style:preserve-3d;will-change:transform;transition-duration:.45s;transform:translateX(-300px)}#tianai-captcha-parent #tianai-captcha-bg-img{background-color:#fff;background-position:top;background-size:cover;z-index:-1;width:100%;height:100%;top:0;left:0;position:absolute;border-radius:6px}#tianai-captcha-parent .slider-bottom{height:19px;width:100%}#tianai-captcha-parent .slider-bottom .close-btn{width:20px;height:20px;background-image:url(../../tac/images/icon.png);background-repeat:no-repeat;background-position:0 -14px;float:right;margin-right:2px;cursor:pointer}#tianai-captcha-parent .slider-bottom .refresh-btn{width:20px;height:20px;background-image:url(../../tac/images/icon.png);background-position:0 -167px;background-repeat:no-repeat;float:right;margin-right:10px;cursor:pointer}#tianai-captcha-parent .slider-bottom .logo{height:30px;float:left}#tianai-captcha-parent .slider-move-shadow{animation:myanimation 2s infinite;height:100%;width:5px;background-color:#fff;position:absolute;top:0;left:0;filter:opacity(0.5);box-shadow:1px 1px 1px #fff;border-radius:50%}#tianai-captcha-parent #tianai-captcha-slider-move-track-mask{border-width:1px;border-style:solid;border-color:#00f4ab;width:0;height:32px;background-color:#a9ffe5;opacity:.5;position:absolute;top:-1px;left:-1px;border-radius:5px} +#tianai-captcha{text-align:left;box-sizing:content-box;width:300px;height:260px;z-index:999}#tianai-captcha .slider-bottom .logo{height:30px}#tianai-captcha .slider-bottom{height:19px;width:100%}#tianai-captcha .content .tianai-captcha-tips{height:25px;width:100%;position:absolute;bottom:-25px;left:0;z-index:999;font-size:15px;line-height:25px;color:#fff;text-align:center;transition:bottom .3s ease-in-out}#tianai-captcha .content .tianai-captcha-tips.tianai-captcha-tips-error{background-color:#ff5d39}#tianai-captcha .content .tianai-captcha-tips.tianai-captcha-tips-success{background-color:#39c522}#tianai-captcha .content .tianai-captcha-tips.tianai-captcha-tips-on{bottom:0}#tianai-captcha .content #tianai-captcha-loading{z-index:9999;background-color:#f5f5f5;text-align:center;height:100%;overflow:hidden;position:relative;display:flex;justify-content:center;align-items:center}#tianai-captcha .content #tianai-captcha-loading img{display:block;width:45px;height:45px}#tianai-captcha #tianai-captcha-slider-bg-canvas{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:5px}#tianai-captcha #tianai-captcha-slider-bg-div{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:5px}#tianai-captcha #tianai-captcha-slider-bg-div .tianai-captcha-slider-bg-div-slice{position:absolute}@keyframes myanimation{from{left:0}to{left:289px}} +#tianai-captcha.tianai-captcha-slider{z-index:999;position:absolute;left:0;top:0;user-select:none}#tianai-captcha.tianai-captcha-slider .content{width:100%;height:180px;position:relative;overflow:hidden}#tianai-captcha.tianai-captcha-slider .bg-img-div{width:100%;height:100%;position:absolute;transform:translate(0px, 0px)}#tianai-captcha.tianai-captcha-slider .bg-img-div img{height:100%;width:100%;border-radius:5px}#tianai-captcha.tianai-captcha-slider .slider-img-div{height:100%;position:absolute;left:0;transform:translate(0px, 0px)}#tianai-captcha.tianai-captcha-slider .slider-img-div #tianai-captcha-slider-move-img{height:100%}#tianai-captcha.tianai-captcha-slider .slider-move{height:34px;width:100%;margin:11px 0;position:relative}#tianai-captcha.tianai-captcha-slider .slider-move-track{position:relative;height:32px;line-height:32px;text-align:center;background:#f5f5f5;color:#999;transition:0s;font-size:14px;box-sizing:content-box;border:1px solid #f5f5f5;border-radius:4px}#tianai-captcha.tianai-captcha-slider .refresh-btn,#tianai-captcha.tianai-captcha-slider .close-btn{display:inline-block}#tianai-captcha.tianai-captcha-slider .slider-move{line-height:38px;font-size:14px;text-align:center;white-space:nowrap;color:#88949d;-moz-user-select:none;-webkit-user-select:none;user-select:none;filter:opacity(0.8)}#tianai-captcha.tianai-captcha-slider .slider-move .slider-move-btn{transform:translate(0px, 0px);position:absolute;top:-6px;left:0;width:63px;height:45px;background-color:#fff;background-repeat:no-repeat;background-size:contain;border-radius:5px}#tianai-captcha.tianai-captcha-slider .slider-tip{margin-bottom:5px;font-weight:bold;font-size:15px;line-height:normal;color:#000}#tianai-captcha.tianai-captcha-slider .slider-move-btn:hover{cursor:pointer} +#tianai-captcha.tianai-captcha-rotate .rotate-img-div{height:100%;text-align:center}#tianai-captcha.tianai-captcha-rotate .rotate-img-div img{height:100%;transform:rotate(0deg);display:inline-block} +#tianai-captcha.tianai-captcha-concat .tianai-captcha-slider-concat-img-div{background-size:100% 180px;position:absolute;transform:translate(0px, 0px);z-index:1;width:100%}#tianai-captcha.tianai-captcha-concat .tianai-captcha-slider-concat-bg-img{width:100%;height:100%;position:absolute;transform:translate(0px, 0px);background-size:100% 180px} +#tianai-captcha.tianai-captcha-disable{z-index:999;position:absolute;left:0;top:0}#tianai-captcha.tianai-captcha-disable .content{width:100%;height:180px;position:relative;overflow:hidden}#tianai-captcha.tianai-captcha-disable .content .bg-img-div{background-image:url(../../tac/images/dun.jpeg);width:100%;height:100%;overflow:hidden}#tianai-captcha.tianai-captcha-disable .content .bg-img-div #content-span{color:#fff;overflow:hidden;margin-top:132px;display:block;text-align:center} +#tianai-captcha.tianai-captcha-word-click{box-sizing:border-box}#tianai-captcha.tianai-captcha-word-click .click-tip{position:relative;height:40px;width:100%}#tianai-captcha.tianai-captcha-word-click .click-tip .tip-img{height:35px;position:absolute;right:15px}#tianai-captcha.tianai-captcha-word-click .click-tip #tianai-captcha-click-track-font{font-size:18px;display:inline-block;height:40px;line-height:40px;position:absolute}#tianai-captcha.tianai-captcha-word-click .slider-bottom{position:relative;top:6px}#tianai-captcha.tianai-captcha-word-click .content #bg-img-click-mask{width:100%;height:100%;position:absolute;left:0;top:0}#tianai-captcha.tianai-captcha-word-click .content #bg-img-click-mask .click-span{position:absolute;left:0;top:0;border-radius:50px;background-color:#409eff;width:20px;height:20px;text-align:center;line-height:20px;color:#fff;border:2px solid #fff;box-sizing:content-box}#tianai-captcha.tianai-captcha-word-click .click-confirm-btn{width:100%;height:35px;border-radius:4px;background-image:linear-gradient(173deg, hsl(38.09, 91%, 57.89%) 0%, hsl(38.09, 89.38%, 71.74%) 100%);font-size:15px;text-align:center;box-sizing:border-box;line-height:35px;color:#fff;margin-top:3px}#tianai-captcha.tianai-captcha-word-click .click-confirm-btn:hover{cursor:pointer} diff --git a/public/tac/images/dun.jpeg b/public/tac/images/dun.jpeg new file mode 100644 index 0000000..347a371 Binary files /dev/null and b/public/tac/images/dun.jpeg differ diff --git a/public/tac/images/icon.png b/public/tac/images/icon.png new file mode 100644 index 0000000..586a123 Binary files /dev/null and b/public/tac/images/icon.png differ diff --git a/public/tac/js/tac.min.js b/public/tac/js/tac.min.js new file mode 100644 index 0000000..9ee4bb7 --- /dev/null +++ b/public/tac/js/tac.min.js @@ -0,0 +1 @@ +(()=>{"use strict";var t,e,a={783:(t,e,a)=>{var i=a(618),n=Object.create(null),r="undefined"==typeof document,s=Array.prototype.forEach;function c(){}function o(t,e){if(!e){if(!t.href)return;e=t.href.split("?")[0]}if(l(e)&&!1!==t.isLoaded&&e&&e.indexOf(".css")>-1){t.visited=!0;var a=t.cloneNode();a.isLoaded=!1,a.addEventListener("load",(function(){a.isLoaded||(a.isLoaded=!0,t.parentNode.removeChild(t))})),a.addEventListener("error",(function(){a.isLoaded||(a.isLoaded=!0,t.parentNode.removeChild(t))})),a.href="".concat(e,"?").concat(Date.now()),t.nextSibling?t.parentNode.insertBefore(a,t.nextSibling):t.parentNode.appendChild(a)}}function d(t){if(!t)return!1;var e=document.querySelectorAll("link"),a=!1;return s.call(e,(function(e){if(e.href){var n=function(t,e){var a;return t=i(t),e.some((function(i){t.indexOf(e)>-1&&(a=i)})),a}(e.href,t);l(n)&&!0!==e.visited&&n&&(o(e,n),a=!0)}})),a}function h(){var t=document.querySelectorAll("link");s.call(t,(function(t){!0!==t.visited&&o(t)}))}function l(t){return!!/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(t)}t.exports=function(t,e){if(r)return c;var a,s,o,l=function(t){var e=n[t];if(!e){if(document.currentScript)e=document.currentScript.src;else{var a=document.getElementsByTagName("script"),r=a[a.length-1];r&&(e=r.src)}n[t]=e}return function(t){if(!e)return null;var a=e.split(/([^\\/]+)\.js$/),n=a&&a[1];return n&&t?t.split(",").map((function(t){var a=new RegExp("".concat(n,"\\.js$"),"g");return i(e.replace(a,"".concat(t.replace(/{fileName}/g,n),".css")))})):[e.replace(".js",".css")]}}(t);return a=function(){var t=d(l(e.filename));e.locals?h():t||h()},s=50,o=0,function(){var t=this,e=arguments;clearTimeout(o),o=setTimeout((function(){return a.apply(t,e)}),s)}}},618:t=>{t.exports=function(t){if(t=t.trim(),/^data:/i.test(t))return t;var e=-1!==t.indexOf("//")?t.split("//")[0]+"//":"",a=t.replace(new RegExp(e,"i"),"").split("/"),i=a[0].toLowerCase().replace(/\.$/,"");return a[0]="",e+i+a.reduce((function(t,e){switch(e){case"..":t.pop();break;case".":break;default:t.push(e)}return t}),[]).join("/")}},488:(t,e,a)=>{var i=a(783)(t.id,{locals:!1});t.hot.dispose(i),t.hot.accept(void 0,i)},523:(t,e,a)=>{var i=a(783)(t.id,{locals:!1});t.hot.dispose(i),t.hot.accept(void 0,i)},991:(t,e,a)=>{var i=a(783)(t.id,{locals:!1});t.hot.dispose(i),t.hot.accept(void 0,i)},2:(t,e,a)=>{var i=a(783)(t.id,{locals:!1});t.hot.dispose(i),t.hot.accept(void 0,i)},492:(t,e,a)=>{var i=a(783)(t.id,{locals:!1});t.hot.dispose(i),t.hot.accept(void 0,i)},305:(t,e,a)=>{var i=a(783)(t.id,{locals:!1});t.hot.dispose(i),t.hot.accept(void 0,i)},444:(t,e,a)=>{var i=a(783)(t.id,{locals:!1});t.hot.dispose(i),t.hot.accept(void 0,i)},687:(t,e,a)=>{a(488),a(523),a(444);function i(t){t.preventDefault&&t.preventDefault()}function n(t){f(t).each((t=>{t.addEventListener("touchmove",i,{passive:!1}),t.addEventListener("mousemove",i,{passive:!1})}))}function r(t){if(null!==t.pageX&&void 0!==t.pageX)return{x:Math.round(t.pageX),y:Math.round(t.pageY)};let e;return t.changedTouches?e=t.changedTouches:t.targetTouches?e=t.targetTouches:t.originalEvent&&t.originalEvent.targetTouches&&(e=t.originalEvent.targetTouches),null!==e[0].pageX&&void 0!==e[0].pageX?{x:Math.round(e[0].pageX),y:Math.round(e[0].pageY)}:{x:Math.round(e[0].clientX),y:Math.round(e[0].clientY)}}function s(t,e){const a=r(e);let i=a.x,n=a.y;t.currentCaptchaData.startX=i,t.currentCaptchaData.startY=n;const s=t.currentCaptchaData.trackList;t.currentCaptchaData.startTime=new Date;const o=t.currentCaptchaData.startTime;s.push({x:a.x,y:a.y,type:"down",t:(new Date).getTime()-o.getTime()}),t.__m__=c.bind(null,t),t.__u__=d.bind(null,t),window.addEventListener("mousemove",t.__m__),window.addEventListener("mouseup",t.__u__),window.addEventListener("touchmove",t.__m__,!1),window.addEventListener("touchend",t.__u__,!1),t&&t.doDown&&t.doDown(e,t)}function c(t,e){e.touches&&e.touches.length>0&&(e=e.touches[0]);const a=r(e);let i=a.x,n=a.y;const s=t.currentCaptchaData.startX,c=t.currentCaptchaData.startY,o=t.currentCaptchaData.startTime,d=t.currentCaptchaData.end,h=(t.currentCaptchaData.bgImageWidth,t.currentCaptchaData.trackList);let l=i-s,p=n-c;const u={x:a.x,y:a.y,type:"move",t:(new Date).getTime()-o.getTime()};h.push(u),l<0?l=0:l>d&&(l=d),t.currentCaptchaData.moveX=l,t.currentCaptchaData.moveY=p,t.doMove&&t.doMove(e,t)}function o(t){t&&(t.__m__&&(window.removeEventListener("mousemove",t.__m__),window.removeEventListener("touchmove",t.__m__)),t.__u__&&(window.removeEventListener("mouseup",t.__u__),window.removeEventListener("touchend",t.__u__)))}function d(t,e){o(t);const a=r(e);t.currentCaptchaData.stopTime=new Date;const i=t.currentCaptchaData.startTime,n=t.currentCaptchaData.trackList,s={x:a.x,y:a.y,type:"up",t:(new Date).getTime()-i.getTime()};n.push(s),t.doUp&&t.doUp(e,t),t.endCallback(t.currentCaptchaData,t)}function h(t,e,a,i,n){const r={startTime:new Date,trackList:[],movePercent:0,clickCount:0,bgImageWidth:Math.round(t),bgImageHeight:Math.round(e),templateImageWidth:Math.round(a),templateImageHeight:Math.round(i),end:n};return r}function l(t,e){f(t).find("#tianai-captcha-tips").removeClass("tianai-captcha-tips-on"),e&&setTimeout(e,.35)}function p(t,e,a,i){const n=f(t).find("#tianai-captcha-tips");n.text(e),1===a?(n.removeClass("tianai-captcha-tips-error"),n.addClass("tianai-captcha-tips-success")):(n.removeClass("tianai-captcha-tips-success"),n.addClass("tianai-captcha-tips-error")),n.addClass("tianai-captcha-tips-on"),setTimeout(i,1e3)}class u{showTips(t,e,a){p(this.el,t,e,a)}closeTips(t,e){l(this.el,t)}}function f(t,e){return new m(t,e)}class m{constructor(t,e){if(e&&"object"==typeof e&&void 0!==e.nodeType)return this.dom=e,void(this.domStr=t);if(t instanceof m)this.dom=t.dom,this.domStr=t.domStr;else if("string"==typeof t)this.dom=document.querySelector(t),this.domStr=t;else{if("object"!=typeof document||void 0===document.nodeType)throw new Error("不支持的类型");this.dom=t,this.domStr=t.nodeName}}each(t){this.getTarget().querySelectorAll("*").forEach(t)}removeClass(t){let e=this.getTarget();if(e.classList)e.classList.remove(t);else{const a=e.className,i=new RegExp("(?:^|\\s)"+t+"(?!\\S)","g");e.className=a.replace(i,"")}return this}addClass(t){const e=this.getTarget();if(e.classList)e.classList.add(t);else{let a=e.className;-1===a.indexOf(t)&&(e.className=a+" "+t)}return this}find(t){const e=this.getTarget().querySelector(t);return e?new m(t,e):null}children(t){const e=this.getTarget().childNodes;for(let a=0;a\n
                        \n ${t.i18n.slider_title}\n
                        \n
                        \n
                        \n \n \n
                        \n
                        \n
                        \n \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n\n
                        \n`}(this.styleConfig)),this.el=this.boxEl.find("#tianai-captcha"),this.loadStyle(),this.el.find("#tianai-captcha-slider-move-btn").mousedown(s.bind(null,this)),this.el.find("#tianai-captcha-slider-move-btn").touchstart(s.bind(null,this)),this.loadCaptchaForData(this,t),this.endCallback=e,a&&a(this),this}showTips(t,e,a){p(this.el,t,e,a)}closeTips(t){l(this.el,t)}destroy(){const t=this.boxEl.children("#tianai-captcha");t&&t.remove(),o()}doMove(){const t=this.currentCaptchaData.moveX;this.el.find("#tianai-captcha-slider-move-btn").css("transform","translate("+t+"px, 0px)"),this.el.find("#tianai-captcha-slider-img-div").css("transform","translate("+t+"px, 0px)"),this.el.find("#tianai-captcha-slider-move-track-mask").css("width",t+"px")}loadStyle(){let t="",e="#00f4ab",a="#a9ffe5";const i=this.styleConfig;i&&(t=i.btnUrl,a=i.moveTrackMaskBgColor,e=i.moveTrackMaskBorderColor),this.el.find(".slider-move .slider-move-btn").css("background-image","url("+t+")"),this.el.find("#tianai-captcha-slider-move-track-mask").css("border-color",e),this.el.find("#tianai-captcha-slider-move-track-mask").css("background-color",a)}loadCaptchaForData(t,e){const a=t.el.find("#tianai-captcha-slider-bg-img"),i=t.el.find("#tianai-captcha-slider-move-img");a.attr("src",e.data.backgroundImage),i.attr("src",e.data.templateImage),a.on("load",(()=>{t.currentCaptchaData=h(a.width(),a.height(),i.width(),i.height(),242),t.currentCaptchaData.currentCaptchaId=e.data.id}))}};a(305);const g=class extends u{constructor(t,e){super(),this.boxEl=t,this.styleConfig=e,this.type="ROTATE",this.currentCaptchaData={}}init(t,e,a){return this.destroy(),this.boxEl.append(function(t){return`\n
                        \n
                        \n ${t.i18n.rotate_title}\n
                        \n
                        \n
                        \n \n \n
                        \n
                        \n \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n`}(this.styleConfig)),this.el=this.boxEl.find("#tianai-captcha"),this.loadStyle(),this.el.find("#tianai-captcha-slider-move-btn").mousedown(s.bind(null,this)),this.el.find("#tianai-captcha-slider-move-btn").touchstart(s.bind(null,this)),this.loadCaptchaForData(this,t),this.endCallback=e,a&&a(this),this}destroy(){const t=this.boxEl.children("#tianai-captcha");t&&t.remove(),o()}doMove(){const t=this.currentCaptchaData.moveX;this.el.find("#tianai-captcha-slider-move-btn").css("transform","translate("+t+"px, 0px)"),this.el.find("#tianai-captcha-slider-move-img").css("transform","rotate("+t/(this.currentCaptchaData.end/360)+"deg)"),this.el.find("#tianai-captcha-slider-move-track-mask").css("width",t+"px")}loadStyle(){let t="",e="#00f4ab",a="#a9ffe5";const i=this.styleConfig;i&&(t=i.btnUrl,a=i.moveTrackMaskBgColor,e=i.moveTrackMaskBorderColor),this.el.find(".slider-move .slider-move-btn").css("background-image","url("+t+")"),this.el.find("#tianai-captcha-slider-move-track-mask").css("border-color",e),this.el.find("#tianai-captcha-slider-move-track-mask").css("background-color",a)}loadCaptchaForData(t,e){const a=t.el.find("#tianai-captcha-slider-bg-img"),i=t.el.find("#tianai-captcha-slider-move-img");a.attr("src",e.data.backgroundImage),i.attr("src",e.data.templateImage),a.on("load",(()=>{t.currentCaptchaData=h(a.width(),a.height(),i.width(),i.height(),242),t.currentCaptchaData.currentCaptchaId=e.data.id}))}};a(991);const b=class extends u{constructor(t,e){super(),this.boxEl=f(t),this.styleConfig=e,this.type="CONCAT",this.currentCaptchaData={}}init(t,e,a){return this.destroy(),this.boxEl.append((this.styleConfig,'\n
                        \n
                        \n 拖动滑块完成拼图\n
                        \n
                        \n
                        \n \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n
                        \n ')),this.el=this.boxEl.find("#tianai-captcha"),this.loadStyle(),this.el.find("#tianai-captcha-slider-move-btn").mousedown(s.bind(null,this)),this.el.find("#tianai-captcha-slider-move-btn").touchstart(s.bind(null,this)),n(this.el),window.currentCaptcha=this,this.loadCaptchaForData(this,t),this.endCallback=e,a&&a(this),this}destroy(){o();const t=this.boxEl.children("#tianai-captcha");t&&t.remove()}doMove(){const t=this.currentCaptchaData.moveX;this.el.find("#tianai-captcha-slider-move-btn").css("transform","translate("+t+"px, 0px)"),this.el.find("#tianai-captcha-slider-concat-img-div").css("background-position-x",t+"px"),this.el.find("#tianai-captcha-slider-move-track-mask").css("width",t+"px")}loadStyle(){let t="",e="#00f4ab",a="#a9ffe5";const i=this.styleConfig;i&&(t=i.btnUrl,a=i.moveTrackMaskBgColor,e=i.moveTrackMaskBorderColor),this.el.find(".slider-move .slider-move-btn").css("background-image","url("+t+")"),this.el.find("#tianai-captcha-slider-move-track-mask").css("border-color",e),this.el.find("#tianai-captcha-slider-move-track-mask").css("background-color",a)}loadCaptchaForData(t,e){const a=t.el.find(".tianai-captcha-slider-concat-bg-img"),i=t.el.find("#tianai-captcha-slider-concat-img-div");a.css("background-image","url("+e.data.backgroundImage+")"),i.css("background-image","url("+e.data.backgroundImage+")"),i.css("background-position","0px 0px");var n=e.data.backgroundImageHeight,r=(n-e.data.data.randomY)/n*180;i.css("height",r+"px"),t.currentCaptchaData=h(a.width(),a.height(),i.width(),i.height(),242),t.currentCaptchaData.currentCaptchaId=e.data.id}};a(2);const C=class{constructor(t,e){this.boxEl=t,this.styleConfig=e,this.type="DISABLE",this.currentCaptchaData={}}init(t,e,a){return this.destroy(),this.boxEl.append(function(t){return`\n
                        \n
                        \n ${t.i18n.disable_title}\n
                        \n
                        \n
                        \n\x3c!-- --\x3e\n\x3c!-- --\x3e\n\x3c!-- --\x3e\n\x3c!-- --\x3e\n \n
                        \n
                        \n
                        \n `}(this.styleConfig)),this.el=this.boxEl.find("#tianai-captcha"),this.loadCaptchaForData(this,t),this.endCallback=e,a&&a(this),this}destroy(){const t=this.boxEl.find("#tianai-captcha");t&&t.remove()}loadCaptchaForData(t,e){const a=e.msg||e.message||"接口异常";t.el.find("#content-span").text(a)}};a(492);const y=class extends u{constructor(t,e){super(),this.boxEl=t,this.styleConfig=e,this.type="IMAGE_CLICK",this.currentCaptchaData={}}init(t,e,a){this.destroy(),this.boxEl.append(function(t){return`\n
                        \n
                        \n ${t.i18n.image_click_title}\n \n
                        \n
                        \n
                        \n \n \n
                        \n
                        \n
                        \n
                        \n
                        确定
                        \n
                        \n`}(this.styleConfig)),this.el=this.boxEl.find("#tianai-captcha"),this.loadCaptchaForData(this,t),this.endCallback=e;const i=c.bind(null,this);return this.el.find("#bg-img-click-mask").click((t=>{if("click-span"===t.target.className)return;this.currentCaptchaData.clickCount++;const e=this.currentCaptchaData.trackList;1===this.currentCaptchaData.clickCount&&(this.currentCaptchaData.startTime=new Date,window.addEventListener("mousemove",i),this.currentCaptchaData.startX=t.offsetX,this.currentCaptchaData.startY=t.offsetY);const a=this.currentCaptchaData.startTime;e.push({x:Math.round(t.offsetX),y:Math.round(t.offsetY),type:"click",t:(new Date).getTime()-a.getTime()});const n=t.offsetX-10,r=t.offsetY-10;this.el.find("#bg-img-click-mask").append(""+this.currentCaptchaData.clickCount+"")})),this.el.find(".click-confirm-btn").click((()=>{this.currentCaptchaData.clickCount>0&&(this.currentCaptchaData.stopTime=new Date,window.removeEventListener("mousemove",i),this.endCallback(this.currentCaptchaData,this))})),a&&a(this),this}destroy(){const t=this.boxEl.children("#tianai-captcha");t&&t.remove(),o()}loadCaptchaForData(t,e){const a=t.el.find("#tianai-captcha-slider-bg-img"),i=t.el.find("#tianai-captcha-tip-img");a.on("load",(()=>{t.currentCaptchaData=h(a.width(),a.height(),i.width(),i.height()),t.currentCaptchaData.currentCaptchaId=e.data.id})),a.attr("src",e.data.backgroundImage),i.attr("src",e.data.templateImage)}};const w=class extends y{constructor(t,e){super(t,e),this.type="WORD_IMAGE_CLICK"}},k={btnUrl:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAABkCAYAAABU19jRAAAJcUlEQVR4nO2d63MT1xmHf9rV6mr5fgNMuSW+ENsY8N0EE2BMhinJNB8y/dD2Qz/0v+gMf0w/JHTKNJAhICwbsA02TpNAHEMgQIwNBSEb8F2rvXTeY1kjYyA+TmVJmfeZ8YiRWa9299E57/mdI63Dtm3E+RjAKTDMaj4F8AU9uyzMCQBn+EQxb+EjAF+RMH8AcJrPFLMGvCSMzWeKWSN/I2GiAFx8xpi1oPBZYiTQWRhGChaGkYKFYaRgYRgpWBhGChaGkYKFYaRgYRgpWBhGChaGkYKFYaRgYRgpWBhGChaGkYKFYaRgYRgpWBhGChaGkYKFYaRgYRgpWBhGChaGkYKFYaRgYRgpWBhGCiefrtShGwZiup74+4qqwu12Z/W7lIVJEfN6FDfv3sPXfYOIRRfpm1UQKC7EkQ+PYFtRcdZKw8KkiLsPJ/CfgSFcH7yOxWhU7MSluYQoR44fxdaCoqyUhoVJEfZ8FN99c1N0Sx6PR+zEMAz0XAgBNtB14hi25OXDkWXHxUVvinA4ln6ScTqdsGwbvRd7EPwyiEcvXyDbvpyHhUkRaq4fe/c3wEWSWFZiJySNYZroCYYQPHsBY1OTWSWNevLkyb/TYwa8lt8UAb8ftluDW9UwPj4hDs0Rb3JUVRXd09j9nwELKKgoR4HXlw2Hb3INkyK8mob9NdUwLROq4sCVKwMrdqRpGkzTFN0TaWR2HcKu0rKMr2lYmBTi1jS01dUt7UBx4PKlfvHP5JaGuqseIY0DjmOHsKukNKOPiYVJMU5VRXt9PSwboO+fvHJ5QEiiKEvlIz3S86HuHiiqAhw9iJ0lpRnb0rAwG4CqKHh/Tz0UhwOWaWGg/5oofEkmJLU4wfPdQia765CQJhNHJCzMBkEtSVtdLRw2YNo2hgaGEDMMMWpahrwJBUMUCkM9djgjE2EWZgOhFqW5rlbMKdm2heHBYUT1mCiAEW9pKKfpPh8Sj5mYCLMwG4zLqWJfTZWQgL5S++uhYURjBrR4S0MtUSYnwixMGvBoGvZUV4quh0S4Pjgsaho1XtOIcM8wxJCb+qmu33dljDS/CWEeTb/E/Pw89EUdebkBVBQWrnnbWVjQoMAtsT9asGDQhf8VUbnX5UJ9VaVoZahVuXZ1cMXoiaSJxWIiEab/dPj4UXFczjRrk/VJ70/hp/jhuxF89o9TGP1+FH6fD9OxGHw5Pnicb34/PJ2dweitu7hwLojvb47A9rhQmJeXGLm8iQeP/4uRH27h88/+iZhhYs40UFZQsK7XrqkqigvyYbk18VrHH74+EX74YAzRqI66mupE15UmzKwW5kEkgtFvRxA8ex7hJ2HMzczgzu0f8fjxExRt2YzcgB9udfUJjuo6Tv/7HE6f+pe4GHd//AkwLRhuDeXFRW+U5v7EI4yMjKI3GMLt0Tt4cO8BAoEcWJoTZYXrl6asqBC6U0GOy42HY+MrZi1JmoWFRZQW5sNyuVBeUpxOabJ7aiASjiB4/iKmnj+H5loaacwvLOL2jRF4AjnY8dc/I/DKbTdoSHvr8SO8DD/DzPSMWHrg1JwYvHZdpK2NVZWU26/aF3VDTyLP0N/bh4mJR3C7XZiZnRVdht/nx7u7tsOzzg5qORFWHAocigO9vX2Jronwej24cXMEbq8XrfW169rH/4usnq02o1FEo9FEE47luN22sTAzC0OPrd7ItnHn9h0MDg3D6/WKbZdHJqYRg26ar92XDgvD39zA2Ng4VKdTbEf7mpmeRX/fAPRfeRch+luNNTXICeSu+h3ti7okUzdgp3luO6uFUTUN9lLmnniOCkdKVnML8uB0r76rD72Di4qL4NI0IUnydpZlw/WmGsY00bRvDzZvKhfFKLAU9VOG8v7BdijW+i8kLX649yyMz0+fwVQksur3NILyejzw5efCoaT3kmW1MN68AMq2bBIXXtd18WMZBt6r242DBzvgda3uWhQ4xNzOkeNdohZYXFjA4vwCfD4/Sio2i9bjdeSoGirKylFYXirykehiFHpUR2FJCbZu+x1yXlMrrQWSZWwygv6Ll3DxXBCX+66u6I7o2DRFRWtbM1o62xNdb7rI7lGSqqBs+zZMTj4XLYY/x49t7+zABx8eReWO7ciLL41ctZmqoqRiE/x+P6amp5FbkI9jx7tw+GgncqmbesPuPAEfduzcgenZOTg0FaWby/GXP/0RdZXvrOvlkyzjzyfR81UIoQs9IpRJniqglszt0tDc1oS9bc2o37lTLMhKI2bW35HtRXQRs3MLmH/xUrzzVb8HJQUFyHX/crJCQ+JwOALFqaKspGjNRWtkbg5zc7PQXC5szl/f6Ig6MFqiSavuqHCmumuFLIYBt+ZEY0sTGtua0VBTJQK/NKPzLfzSQEKWL4NiiG5a1gpZzPhMdnNrE/a3N2NPVaUI+jIAnacGNhiShdbx9pzrFgunSA4tqeUQRbuqoLW9BQ0tjSINzhBZBCzMBvPzVAS950KiG6KWJVkWGnXRELrjQBtqG/eioTqzZAELs3FQy3Iv/BR9wUtiUtGOr+tNhoptGt1V7atD4+4aEehlGizMBnH/WRj9wcuiG7LjI7Vllm8d3nnoAKoaakXq+0tzWumChUkxdlyWge4rYt0uzRMpSck01SzUDR3s7MC7e2pFRqSmOZx7GyxMCrESLcsldAd7oCgrEx6xrldRRM1SvbceHfV1K0K7TISFSREx28L41KRIcGmdruOVz82KBFd1oqWjBe/tb0ArLd3McFnAwqSOiclJ9JwP4fLFXtEtJXdDywluU2uTGDpTgZupNcur8GerU8R0eBJDV6+LRVbJLYdIcF2aSHD3tzaL9b20zjdbYGFShB0z4HY6V9QtFNLRXFATxf2U4FZXZkLcLwULkyJoaUXMNMV6HbyS4O6jicQMS3DXCguTInJKC9HU0YoPOg8k1uy0t7eivnmfSHB9WSgLwZOPKcKwLcT0GL69cxe3b46KoK6+ZS92V2zNyAR3jfBsdaox6LPSpiVyf/rEo/rq11JlFzxbnWoomEMW5CtrhWsYRgoWhpGChWGkYGEYKVgYRgoWhpGChWGkYGEYKVgYRgoWhpGChWGkYGEYKVgYRgoWhpGChWGkYGEYKVgYRgoWhpGChWGkYGEYKVgYRgoWhpGChWGkYGEYKVgYRgr6qGx6b4/BZBXUwnzCl4xZI5844g3MCQBn+Kwxb+EjAGcdST3SxwBO8RljXsOnAL4AgP8BXnVIgIvemwsAAAAASUVORK5CYII=",moveTrackMaskBgColor:"#89d2ff",moveTrackMaskBorderColor:"#0298f8",i18n:{tips_success:"验证成功,耗时%s秒",tips_error:"验证失败,请重新尝试!",slider_title:"拖动滑块完成拼图",concat_title:"拖动滑块完成拼图",image_click_title:"请依次点击:",rotate_title:"拖动滑块完成拼图",slider_title_size:"15px",concat_title_size:"15px",image_click_title_size:"20px",rotate_title_size:"15px"}};class E{constructor(t){if(!t.bindEl)throw new Error("[TAC] 必须配置 [bindEl]用于将验证码绑定到该元素上");if(!t.requestCaptchaDataUrl)throw new Error("[TAC] 必须配置 [requestCaptchaDataUrl]请求验证码接口");if(!t.validCaptchaUrl)throw new Error("[TAC] 必须配置 [validCaptchaUrl]验证验证码接口");this.bindEl=t.bindEl,this.domBindEl=f(t.bindEl),this.requestCaptchaDataUrl=t.requestCaptchaDataUrl,this.validCaptchaUrl=t.validCaptchaUrl,t.validSuccess&&(this.validSuccess=t.validSuccess),t.validFail&&(this.validFail=t.validFail),t.requestHeaders?this.requestHeaders=t.requestHeaders:this.requestHeaders={},t.btnCloseFun&&(this.btnCloseFun=t.btnCloseFun),t.btnRefreshFun&&(this.btnRefreshFun=t.btnRefreshFun),this.requestChain=[],this.timeToTimestamp=t.timeToTimestamp||!0,this.insertRequestChain(0,{preRequest(t,e,a,i){if(this.timeToTimestamp&&e.data)for(let t in e.data)e.data[t]instanceof Date&&(e.data[t]=e.data[t].getTime());return!0}})}addRequestChain(t){this.requestChain.push(t)}insertRequestChain(t,e){this.requestChain.splice(t,0,e)}removeRequestChain(t){this.requestChain.splice(t,1)}requestCaptchaData(){const t={};t.headers=this.requestHeaders||{},t.data={},t.headers["Content-Type"]="application/json;charset=UTF-8",t.method="POST",t.url=this.requestCaptchaDataUrl,this._preRequest("requestCaptchaData",t);return this.doSendRequest(t).then((e=>(this._postRequest("requestCaptchaData",t,e),e)))}doSendRequest(t){if(t.headers)for(const e in t.headers)if(t.headers[e].indexOf("application/json")>-1){"string"!=typeof t.data&&(t.data=JSON.stringify(t.data));break}return(e=t,new Promise((function(t,a){var i=new XMLHttpRequest;if(i.open(e.method||"GET",e.url),e.headers)for(const t in e.headers)e.headers.hasOwnProperty(t)&&i.setRequestHeader(t,e.headers[t]);i.onreadystatechange=function(){if(i.readyState===XMLHttpRequest.DONE)if(i.status>=200&&i.status<=500){const e=i.getResponseHeader("Content-Type");e&&-1!==e.indexOf("application/json")?t(JSON.parse(i.responseText)):t(i.responseText)}else a(new Error("Request failed with status: "+i.status))},i.onerror=function(){a(new Error("Network Error"))},i.send(e.data)}))).then((t=>{try{return JSON.parse(t)}catch(e){return t}}));var e}_preRequest(t,e,a,i){for(let n=0;n(this._postRequest("validCaptcha",r,t,a,i),t))).then((t=>{if(200==t.code){const n=(e.stopTime-e.startTime)/1e3;a.showTips(`验证成功,耗时${n}秒`,1,(()=>this.validSuccess(t,a,i)))}else{let e="验证失败,请重新尝试!";t.code&&4001!=t.code&&(e="验证码被黑洞吸走了!"),a.showTips(e,0,(()=>this.validFail(t,a,i)))}})).catch((t=>{let e=a.styleConfig.i18n.tips_error;t.code&&200!=t.code&&(4001!=res.code&&(e=a.styleConfig.i18n.tips_4001),a.showTips(e,0,(()=>this.validFail(res,a,i))))}))}validSuccess(t,e,a){window.currentCaptchaRes=t,a.destroyWindow()}validFail(t,e,a){a.reloadCaptcha()}}window.TAC=class{constructor(t,e){this.config=function(t){return t instanceof E?t:new E(t)}(t),this.config.btnRefreshFun&&(this.btnRefreshFun=this.config.btnRefreshFun),this.config.btnCloseFun&&(this.btnCloseFun=this.config.btnCloseFun),this.style=function(t){let e={...k,...t};return e.i18n={...k.i18n,...t?.i18n},e}(e)}init(){return this.destroyWindow(),this.config.domBindEl.append('\n
                        \n
                        \n
                        \n
                        \n
                        \n \x3c!-- 底部 --\x3e\n
                        \n \n
                        \n
                        \n
                        \n
                        \n '),this.domTemplate=this.config.domBindEl.find("#tianai-captcha-parent"),n(this.domTemplate),this.loadStyle(),this.config.domBindEl.find("#tianai-captcha-slider-refresh-btn").click((t=>{this.btnRefreshFun(t,this)})),this.config.domBindEl.find("#tianai-captcha-slider-close-btn").click((t=>{this.btnCloseFun(t,this)})),this.reloadCaptcha(),this}btnRefreshFun(t,e){e.reloadCaptcha()}btnCloseFun(t,e){e.destroyWindow()}reloadCaptcha(){this.showLoading(),this.destroyCaptcha((()=>{this.createCaptcha()}))}showLoading(){this.config.domBindEl.find("#tianai-captcha-loading").css("display","block")}closeLoading(){this.config.domBindEl.find("#tianai-captcha-loading").css("display","none")}loadStyle(){const t=this.style.bgUrl,e=this.style.logoUrl;t&&this.config.domBindEl.find("#tianai-captcha-bg-img").css("background-image","url("+t+")"),e&&""!==e?this.config.domBindEl.find("#tianai-captcha-logo").attr("src",e):null===e&&this.config.domBindEl.find("#tianai-captcha-logo").css("display","none")}destroyWindow(){this.C&&(this.C.destroy(),this.C=void 0),this.domTemplate&&this.domTemplate.remove()}openCaptcha(){setTimeout((()=>{this.C.el.css("transform","translateX(0)")}),10)}createCaptcha(){this.config.requestCaptchaData().then((t=>{if(this.closeLoading(),!t.code)throw new Error("[TAC] 后台验证码接口数据错误!!!");let e=200===t.code?t.data?.type:"DISABLED";const a=function(t,e){const a=e.config.domBindEl.find("#tianai-captcha-box"),i=e.style;switch(t){case"SLIDER":return new v(a,i);case"ROTATE":return new g(a,i);case"CONCAT":return new b(a,i);case"WORD_IMAGE_CLICK":return new w(a,i);case"DISABLED":return new C(a,i);default:return null}}(e,this);if(null==a)throw new Error("[TAC] 未知的验证码类型["+e+"]");a.init(t,((t,e)=>{const a=e.currentCaptchaData,i={bgImageWidth:a.bgImageWidth,bgImageHeight:a.bgImageHeight,templateImageWidth:a.templateImageWidth,templateImageHeight:a.templateImageHeight,startTime:a.startTime.getTime(),stopTime:a.stopTime.getTime(),trackList:a.trackList};"ROTATE_DEGREE"!==e.type&&"ROTATE"!==e.type||(i.bgImageWidth=e.currentCaptchaData.end),a.data&&(i.data=a.data);const n=e.currentCaptchaData.currentCaptchaId;e.currentCaptchaData=void 0,this.config.validCaptcha(n,i,e,this)})),this.C=a,this.openCaptcha()}))}destroyCaptcha(t){this.C?(this.C.el.css("transform","translateX(300px)"),setTimeout((()=>{this.C.destroy(),t&&t()}),500)):t()}},window.CaptchaConfig=E}},i={};function n(t){var e=i[t];if(void 0!==e){if(void 0!==e.error)throw e.error;return e.exports}var r=i[t]={id:t,exports:{}};try{var s={id:t,module:r,factory:a[t],require:n};n.i.forEach((function(t){t(s)})),r=s.module,s.factory.call(r.exports,r,r.exports,s.require)}catch(t){throw r.error=t,t}return r.exports}n.m=a,n.c=i,n.i=[],n.hu=t=>t+"."+n.h()+".hot-update.js",n.miniCssF=t=>{},n.hmrF=()=>"main."+n.h()+".hot-update.json",n.h=()=>"45e3837e3116285a06d5",n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),t={},e="webpack-demo:",n.l=(a,i,r,s)=>{if(t[a])t[a].push(i);else{var c,o;if(void 0!==r)for(var d=document.getElementsByTagName("script"),h=0;h{c.onerror=c.onload=null,clearTimeout(u);var n=t[a];if(delete t[a],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach((t=>t(i))),e)return e(i)},u=setTimeout(p.bind(null,void 0,{type:"timeout",target:c}),12e4);c.onerror=p.bind(null,c.onerror),c.onload=p.bind(null,c.onload),o&&document.head.appendChild(c)}},(()=>{var t,e,a,i={},r=n.c,s=[],c=[],o="idle",d=0,h=[];function l(t){o=t;for(var e=[],a=0;a0)return l("abort").then((function(){throw n[0]}));var r=l("dispose");i.forEach((function(t){t.dispose&&t.dispose()}));var s,c=l("apply"),o=function(t){s||(s=t)},d=[];return i.forEach((function(t){if(t.apply){var e=t.apply(o);if(e)for(var a=0;a=0&&b._disposeHandlers.splice(e,1)},invalidate:function(){switch(this._selfInvalidated=!0,o){case"idle":e=[],Object.keys(n.hmrI).forEach((function(t){n.hmrI[t](m,e)})),l("ready");break;case"ready":Object.keys(n.hmrI).forEach((function(t){n.hmrI[t](m,e)}));break;case"prepare":case"check":case"dispose":case"apply":(a=a||[]).push(m)}},check:u,apply:f,status:function(t){if(!t)return o;c.push(t)},addStatusHandler:function(t){c.push(t)},removeStatusHandler:function(t){var e=c.indexOf(t);e>=0&&c.splice(e,1)},data:i[m]},t=void 0,b),C.parents=s,C.children=[],s=[],h.require=y})),n.hmrC={},n.hmrI={}})(),(()=>{var t;n.g.importScripts&&(t=n.g.location+"");var e=n.g.document;if(!t&&e&&(e.currentScript&&(t=e.currentScript.src),!t)){var a=e.getElementsByTagName("script");if(a.length)for(var i=a.length-1;i>-1&&!t;)t=a[i--].src}if(!t)throw new Error("Automatic publicPath is not supported in this browser");t=t.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),n.p=t+"../../"})(),(()=>{if("undefined"!=typeof document){var t=(t,e,a,i,n)=>{var r=document.createElement("link");r.rel="stylesheet",r.type="text/css";return r.onerror=r.onload=a=>{if(r.onerror=r.onload=null,"load"===a.type)i();else{var s=a&&("load"===a.type?"missing":a.type),c=a&&a.target&&a.target.href||e,o=new Error("Loading CSS chunk "+t+" failed.\n("+c+")");o.code="CSS_CHUNK_LOAD_FAILED",o.type=s,o.request=c,r.parentNode&&r.parentNode.removeChild(r),n(o)}},r.href=e,a?a.parentNode.insertBefore(r,a.nextSibling):document.head.appendChild(r),r},e=(t,e)=>{for(var a=document.getElementsByTagName("link"),i=0;i({dispose:()=>{for(var t=0;t{for(var t=0;t{h.push(r),s.forEach((r=>{var s=n.miniCssF(r),c=n.p+s,o=e(s,c);o&&d.push(new Promise(((e,n)=>{var s=t(r,c,o,(()=>{s.as="style",s.rel="preload",e()}),n);a.push(o),i.push(s)})))}))}}})(),(()=>{var t,e,a,i,r,s=n.hmrS_jsonp=n.hmrS_jsonp||{179:0},c={};function o(e,a){return t=a,new Promise(((t,a)=>{c[e]=t;var i=n.p+n.hu(e),r=new Error;n.l(i,(t=>{if(c[e]){c[e]=void 0;var i=t&&("load"===t.type?"missing":t.type),n=t&&t.target&&t.target.src;r.message="Loading hot update chunk "+e+" failed.\n("+i+": "+n+")",r.name="ChunkLoadError",r.type=i,r.request=n,a(r)}}))}))}function d(t){function c(t){for(var e=[t],a={},i=e.map((function(t){return{chain:[t],id:t}}));i.length>0;){var r=i.pop(),s=r.id,c=r.chain,d=n.c[s];if(d&&(!d.hot._selfAccepted||d.hot._selfInvalidated)){if(d.hot._selfDeclined)return{type:"self-declined",chain:c,moduleId:s};if(d.hot._main)return{type:"unaccepted",chain:c,moduleId:s};for(var h=0;h ")),f.type){case"self-declined":t.onDeclined&&t.onDeclined(f),t.ignoreDeclined||(v=new Error("Aborted because of self decline: "+f.moduleId+C));break;case"declined":t.onDeclined&&t.onDeclined(f),t.ignoreDeclined||(v=new Error("Aborted because of declined dependency: "+f.moduleId+" in "+f.parentId+C));break;case"unaccepted":t.onUnaccepted&&t.onUnaccepted(f),t.ignoreUnaccepted||(v=new Error("Aborted because "+u+" is not accepted"+C));break;case"accepted":t.onAccepted&&t.onAccepted(f),g=!0;break;case"disposed":t.onDisposed&&t.onDisposed(f),b=!0;break;default:throw new Error("Unexception type "+f.type)}if(v)return{error:v};if(g)for(u in l[u]=m,o(h,f.outdatedModules),f.outdatedDependencies)n.o(f.outdatedDependencies,u)&&(d[u]||(d[u]=[]),o(d[u],f.outdatedDependencies[u]));b&&(o(h,[f.moduleId]),l[u]=p)}a=void 0;for(var y,w=[],k=0;k0;){var r=a.pop(),c=n.c[r];if(c){var o={},l=c.hot._disposeHandlers;for(k=0;k=0&&p.parents.splice(t,1))}}}for(var u in d)if(n.o(d,u)&&(c=n.c[u]))for(y=d[u],k=0;k=0&&c.children.splice(t,1)},apply:function(e){for(var a in l)n.o(l,a)&&(n.m[a]=l[a]);for(var i=0;i{for(var o in i)n.o(i,o)&&(a[o]=i[o],t&&t.push(o));s&&r.push(s),c[e]&&(c[e](),c[e]=void 0)},n.hmrI.jsonp=function(t,e){a||(a={},r=[],i=[],e.push(d)),n.o(a,t)||(a[t]=n.m[t])},n.hmrC.jsonp=function(t,c,h,l,p,u){p.push(d),e={},i=c,a=h.reduce((function(t,e){return t[e]=!1,t}),{}),r=[],t.forEach((function(t){n.o(s,t)&&void 0!==s[t]?(l.push(o(t,u)),e[t]=!0):e[t]=!1})),n.f&&(n.f.jsonpHmr=function(t,a){e&&n.o(e,t)&&!e[t]&&(a.push(o(t)),e[t]=!0)})},n.hmrM=()=>{if("undefined"==typeof fetch)throw new Error("No browser support: need fetch API");return fetch(n.p+n.hmrF()).then((t=>{if(404!==t.status){if(!t.ok)throw new Error("Failed to fetch update manifest "+t.statusText);return t.json()}}))}})();n(687)})(); \ No newline at end of file diff --git a/register.html b/register.html new file mode 100644 index 0000000..e3a4385 --- /dev/null +++ b/register.html @@ -0,0 +1,70 @@ + + + + + + 注册 - 代代相传 + + + + + + +
                        + +
                        +
                        Create Account
                        +

                        注册账号

                        +

                        注册后即可创建家谱或通过邀请码加入家族。

                        +
                        + + + + + + +
                        +
                        +
                        + +
                        + + + + + + + + + + + + + + diff --git a/response.json b/response.json new file mode 100644 index 0000000..84d18b7 --- /dev/null +++ b/response.json @@ -0,0 +1 @@ +{"code":200,"msg":"操作成功","data":{"required":true,"providerCode":"TIANAI","captchaType":"SLIDER","challengeId":"SLIDER_ea5f86b94fc143d38dee0012ca3ad408","uuid":null,"img":null,"payload":{"id":"SLIDER_ea5f86b94fc143d38dee0012ca3ad408","type":"SLIDER","backgroundImage":"data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCADAAUADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD32kpaKAEopaKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKSlooASiiigAooooAKKKWgBKKKWgAooooAKKKKACiiigAooooAKKKKACiiigAoopKAFopKWgAooooAKKKw/Efi3R/Ctusup3G1mGUiQZdh3PoB7kigDcoqlpGrWmu6Tb6nYOXtrhdyMRg9cH9Qau0AFFFFABRRRQAUUUUAFIzKilmYKo6knGKWua8QazaSxXWlJOY7oBevAIJAPP03fl9MxUmoRuyoRcnZHSKQyhlIIPII70tc14c1izWw03TjM8lzLGTnqM8lhnvgg+p6deTXS0Qmpq6CUXF2YUUUVZIUUUUAFFFFABRRVLV9VtdD0m51O9crbW6b3IGT6fzxQBdorB8N+MNG8VQNJplxudRlopBh1HrjoR7gnrW9QAUUUUAFFFFABRRRQAUUUUAFFBIAJJwB3NQ211b3sCz2txFPC2QJInDKccHkcUATUlLSUAFLSUUALRRS0AJXz58VNXSH4iROGjufIkh2xsvmKCnzEFe+Gxke9egfEL4mWXhqymtLGdZNQOULLgiM9/qw9O3evmq8nn1S9e7umfeeSc5Kg5wPdjn9c9+GhHsMfxl1qGNY47GzVVGAq2soAHoBmlPxr17/nytP/AWX/GvIhbhwcu69R8rHg9CPfHAH+0Se1I9qSpIZssPlAc43N756AD+ZoA9c/4XX4g/58rP/wABZf8AGk/4Xbr/APz5Wf8A4DS/415ILUcEM+PvZ3EkjG0e2Cef5042qEg/MNhGArsenVW/HAz3osB6z/wu3xB3sLT/AMB5f8aP+F3a/wD8+Fp/4DS/415MLEg7S7Nj/aOeARn3GSc+woFmu04ZgCygbmOehGfcHkn6CiwHrH/C7vEH/Phaf+A0v+NB+N3iD/nwtP8AwHl/xrycWSr83mP5YGHyTlSFwPz5P1oexOzehlyAVZS/TgKD+fNAHq//AAu/xB/z4Wn/AIDy/wCNc3qfxD17UtQuLs3UkHnKqmOK3G0AYxjKk9s8k9a45bNfPeH95t5YSZPdwAP0P5mnw6cJLVZ9zj5lyN3+/wD4fpScU9GhptbHX6R8Q9d0i+huVnecRRmMRS2w2lTk44UHqc8Guj/4Xf4g/wCfC0/78Sf415RFYAzzxs8nyRgD5j1MR/rz9adJZoI1XMm9wSCCeOFP8gfzNNRS2QNt7nqv/C7/ABCOunWmP+uEn+NH/C7/ABB/0D7T/vxJ/jXlbaeNzSfvAiE7eeHwwb8trY/Km/YETILOQhw7Fz1BOR9cEflTsI9V/wCF4eIP+gdaf9+JP8aP+F3+IMf8g+0/8B5P8a8pFgEDB3cleGbd93Hynv1wQfrSmwHJZn44OGwo42n8ejfnSsB6r/wvDxB/z4Wn/fiT/Gj/AIXh4g/6B9p/34k/xrylrEDO9nCgHcAx44APPtwwHuaRrIbWDSSLyc4bkdAfyODj0JoA9X/4Xhr/AP0D7T/vxJ/jTX+NuuyRsj6dZFWGGBt5CCPQjNeVNaDy23M6n5t2GPHTP/fJwRjsTTWt0BzKWVctuw54yuG/LIYD0zQB6Z8L9Ue++JUdz5sNqsks3mRIpjD70JChcY++cjp9316/RNfFFjdXOkXy3UDEODlgD3HXHpjrn0IPrX0v8P8A4k2Xiazhs7yUR6kAFy3AlP8ARvb8qGM9AooopAFFFFABRRRQAUVDc3dtZxCW6uIoIywUPK4UZPQZPepqAKmqWsl9pF7aRP5ck8DxK/8AdLKQD+teUfDjxvoPhmxfwtqpaxv7a4lEsjEskj7vXn6cccV7FXzv8YvBN5putTa7ZRwvY3z/ADqI8lJOpyMd+SCvPXI4zQB9AWl/Z6hGXs7qC4UYyYpA2M9M46VYr4vtNRksrj5Li7tShYKYpCduDyec9+McVv23jPxMCDb+Kro4wAPPKJyOPlzx+X407CufWNFfLkvjLxnMCreIrsc4ASQq36HJ/DNZ93qXiPUomS81m+li+6S0uDn0Oc/qc+1Fgue7an8Qrbw74guE1aU/YXhLQCIAncr7SB65HPWvNfF3xt1DVFey0WFraJsjKkmRh9euPYAfU1yGoy3eqabYWV2AUsI9gcZ3MOo3EkevU4+hqrBpixIFEWC/8IHLf1P44Ht3qKUZKPvblzab0Mry7i8nFxdSM8hOPXHsOxPt0Hfvi3FbNkKMYHTBz9SD/NvU8Vpi1AUtgkD5SegI/uj29cc/XBp4gZi4Py4HzHaOPTP9F9/fB0IM3y9y7QBgcE8gHA6fQD9D6saTyyQSwPPfoSD7ep7eg9K1DDtBBAAABw38Pu3qcnp/XFNa3KkjawwoJ/vDP/sx4/P8SAUPLJxjA5JHpnHX8On+PWlEW05x06A/oP6kflV3yMEjy8nIAUN1PZR7c9f8DThBwN7cnoehxjJb/PtjuAAUwhwoAJHY/Tv/AEB70gUoTnJAODzlT3IPcc4Hp1q4Ye6q2eoXrj+6CPxGR7jFHlsRlRuxxkjPYn8QcE4+lAFYRcnklh8oPIyB1U/0NGxcruVWcAYO0jeo5I/CrIjLKwYFlHy8HqF5OfQ5PB4z/I8liwXgO5VSF67m5z6cjjHr+NAFYKuBHIz7kKshI4Y8n+ZFSxMQ89ttwoWNh6fdY/1qYkBCw3OdsjhMfKqN8mV75H9BT5NqyiArmaMMHYdwFwP1BoAqpbgTmQ8b2jx743D+lCRE7DIyhJAiRDuRtKN+oNXJog1vbyL/AMu4MxHriQ//ABQpjW5jWVUdduHhJPdlIc49OO/saAKYiH7svtJAABBJKDBRiw6DnH5ijaDgvnCgH6D7rexJ4P4VcdPNkKDaUdsY6FVcZUfn396QKcGUqTkeYyrnJ/hf2ye1AFPyyuMjcy5BA9QPmA9SR3NJ5eFGOSMAHseOPplePqKu+Vt43ZIIUNjIz/AfoemKYIsoHIfZz0GSF6sPqp5HqM9BQBUCFVVhzyAMjg+n5jj1pFhHBBwO2706An9VNW/LOADHuySMKeG7kA+jcMPx60eRnkfNuOQ/v3yPfoR6jI70AUGjKoAquMA/MRyuPbuQOD6g0xon3YwFIIwB2OMge/X5fUEitFIcsCEfOMqBySQP/HiP1HHBo+zlkAUZXbkYy2F7gf3kz+IPoeoBlNbBkULgDp6AYPHPt2PboagRLnT5hPbuynjgfL+Ht/I9jW08DbsDqRu65/HI5P1HI75FH2U/MShAxkfT144x7jK+oFAHb+EvjbeaciWeswm6iXA3kkSKOO554GeDnPHIr0nSvHlrrmtrNYXY/szykR0lATZIzNkk+uNvGe9fO1xpSMq5jOQucH5SB6g88e4yPpVi1kvrPRL3TLdisd0UcyDIcbSG4IPt1G7vWVWMmly90XBpX5j66or5Ks9T17TUCWWr3ca4xuWTIz6A+v0Ofary+LfGEKEP4kvx1zundsevfcPxArSxJ9T1VvdTsNOTde3kFuMZHmyBSR7A9a+WLnxjrwUrceKrsqwyQ0xlIHfAyce3NYNzqJurhl33N0ztt/eSthjjjAGDk8DGDRYD2r4heMNK8ZC08LaIPtl5NdKUmLFERyCo/wDQj97j0B7ev2cBtbGC3LlzFGqbz1bAxmvAvgx4SvdT1uHxNcrbxWVkWRI1QAvJtI4A4GN2cnngDHcfQdIAqnqmmWus6bPYXse+CZcMO49CPcGrlJQB8v8AjLwfrvhPUjE0EOo2bnMbXMWUkX+6H4ZD6qGFcedS0WQMl7pN3bXOzy2eGYMifSORSRx/t/jX2RfWFpqVo9re28c8Dj5kcZH/ANY+9eQeMvgwLhXn0gfaYwOIHYLKn+63Rh14OPxqdUXdPc8bK+GrjzAuuXkRkRVJnsivT12SNn8c05rexeSVovEtooZNiMftAIHv8h96pav4UvdPmkRUcshw0MqlJE9iD/n2rAMUgcqUYEcEYppicbHZyW8AjMieIIWEfl4kQylEOeS2Y+pPA+vfnLC8JSQL4jgyZgRhpDlec87OnXjp83Suc0+4mspyzW5lhdSksTA4dT/I+hqC4tSk5MCSNCxymV5A9DRcLaHUyvu+0v8A8JFB/rF2Km4ZAPVcJx0Hp1HBolmEcromueSpRmUyFsOCOCB5fGTu5A79RXL/AGd8f6t/yNWtxudP+xzRP5kR3QSYPAPVT7U7iSubyXCLtA8QqT5RLEs2C/OP4Ov+1yeRyMU2Odi0ZbXwx8tiwDNkvzgfc6n15PzDniuVjgkyQ0bjHtTzA4HCv+VFwOmF0yMiTa6GxEwcMWyG54wU6nnnr83XrTo7tt0PmeIUIKHecEnOcgHKc9M8561z1+TeJHc+WyzgBZeD85HRvr61WWBsZ2N+VCYNHUrdyxi2364Fyr7mIbDdhnKfMcdznGR0pIrxs2xbXFDLu3jk+mM/J83Qdc4x2rBLeZpjWrxtvjfzInweM9V/rVRInK5KP+VK4NHSJeSbIN+sIQAwaPBJAByo+5zz9aVL+UCLfqwAw+8eWSeR0zt5/p7VU8JiSLxLbuoZWWOYg46Hymq8dZ1d341K9JJ6LM38s1PNLmsirRtdkEWpTBot+pkgBy3yHv0H3Oeg+lNW8cMjvqf7zymDOFOc84H3ep9ffr1rodVttW0qwjnm1+5aZ2C+Ss+cfIjHJDkjliOnO3PQio76HVbGx8+XXLrzgE3xeecqXVWUH5s9GbqB909alVG7Wt/XyG4Jf1/wTDW/lLxb9VfOAHYKcDHtt5ycE5z+NH2+UypKdTPmNKzudhIAPf7vv06cdOlb89jrsOmxXn9sXRV7cTkec4xndgdechc59x9akk03Wo9AOpjXpSVRXeA3DBlDbj1zzwF98kjqOV7X0/r5D5F/X/DnOi+fy9x1NvNZycAEAcYB+56Y49u1JJfSYlA1j5g4CjkZAyCfu8cdfXPPSpU1nVVdSNSuzyDzMxH86y/EcTHxVqwVWIF5KM4J/jNWpS5rMhpWujRkuzmYjWRkuAoUsAV/754HA49+nWnPeTPLPINZXCsGV1UhSeg6Jx1J7Z+tc20DBCQjn8KtyMF06G0ijYHcZJWx95uw/AVVxJGyt2UIA1qP5YflbB5bg4Pydj3/AFoW6ld1SPWlLtDjCAnLd+NnXgc9eOtc4bdgpO1vyq1ZkWttNII2NxINiHB+Qdz9aGwSuazXLlSf7aUt5QC7Scg5/wBzr79fenzXMjCcLrka8LsPIJPcjCcdeemfeudFu391vypjwvkAIxJ9qYHStPJ+/EOsiQBFYOobAPr9zg8nnjp1pVkbfIf7eiUmIbTubhuM4wnB9+Oh5OayJHEditlbxsMndNJjlz2H0FVPs7f3W/KhMGrHQk5IX/hIbdEAVgN7/eUHH8Hv/U5qaOEP5ckniaCFWcuQ3m7T67cR4/wrmbSBDOHuUfyVOSqjlvYVLdvLe3BkMexQNqIOiKOgFK47aG4bazRgZPFFm2HyQv2k5Hp/qxz71G6eHVyr61fSjJI8my3AZ7fPIvH4Vz/2dtwUKxJ9q29L8NXd/PHGI3LucLFGhd29goochqNyQ3+jKCtlpFxNM+ATcT/I+P8AYRQRx/t11/g3wbrfjLURGI47CwU/vmgiCoqnqpbq5x0DE13Pg74LmPZcayPs0fUwIwaV/wDeboo6dM/hXsVjYWmmWiWllbxwQIMKiDA/+ufelq9wbS2GaTpVnommQadYRCK3gXaq9/qfUmrlFFUQFJkAgEjJ6e9LXDfFm3hbwHdXrkJPZSRzQy5IZG3hTgjvgnigDuaK+Rh8Q/E4QAa7qQAVQB9qc4JPQnPpzml/4WN4o5265qfP3d10/wDe4zz6c0AfQ/xIsI7jwvPdDRoNRnh5LOCGijGSzAqQx+gPfPOMV826rb3FvfKsMUoUgHdLCQxXrnAPPWrEnxA8R3ETRz63fyRyAgo1w7KwJxhgTzxkn2q34xu760SweG8k2G3j+8q8ZweOOlTK19TenzOL5UtCtHNeooBglb3+yS/4U83F6P8Al2k/G1l/+Jqtr+t6ppF40VrqAkizgSeTGc8D2P8AOtCC81qUZa7H18lP/iaOVC9q10X4lYz3uObSbH/XrL/8TQsl5I4VbaTcxwM20o/UitBpNXYc3vPvEh/9lpunTalPf2qy3/7t5kUhYowcFh7e9HKl1Gqrbskvx/zM93ulcoYWLDqFgkb+QqMyXZ/5YSfjbS//ABNQaxq2qWOty28F1hC/eNCT0/2aaL/XZIllF/GqsSADAp6HHOFqdLXuOU5RfK0vx/zJ991/zwkP/btJ/hSbrk/8spP/AAHk/wAKha711VQtqEW184PkAdP+A0faddwT9vjwPWAD/wBlo07i532X4/5j2a5HHkyf9+H/AMKEW6ldUWN9zHA/cPVf7brbBiLtDg4P7kf/ABNW9LudYfVbNZbsGJrhI2KRLkZPuvsaNO4KUm7WX4k2lLNFrqxSZL+TKSBGRgeW3U9BWEwKthhgjqKveINR1LSfEMhtLgoxzGCUUnaeMdKpR6z4gmyyTK3PJMUfX8qSbT5kFRfZlo0W21W6aGeJmRlnVFfdGpOEAC4OOCAMZHvWhGsctq3ma9YqJ441eN4XO3YoVf4OCAMZHvzyc4rat4hEZkaQBQQM+QnU/hQNT8QtgiVeRnPkpj+VJ3eyS/r0JSX9f8ObV3My2BU6/aXIRNixLE+5gSehZB/ePestdUuVtpbdXAjljWJvlGdoYMAD1HIFQHVfEA5Myj/tin+FL/aevhFdrmFd4yAyxA49cYzRHmS2X9fIHZ9f6+8jTLSKFGSSOK1tSWd/EGsyDpHeS5AjJxl2x/Ksz+2NeQFheQ/L3CxD+lXLO8uW0e/upb2MXU8nzDMfzdST+Zp6t3Y4JbIepul4ZGHsYH/wpS9x/cb/AL8PVP8AtDVJCWF/COf4in+FJ9u1Xj/iYwYPHVOP0qrIPaPsvx/zLm+4xxG5/wC2L/4Ub7n/AJ5t/wB+Xqr9s1Ucf2lBn/gP+FNN5qquVOoQZwD1T/CiyD2j7L8f8y3vuv8Anm3/AH5elH2pgSIzx3MTiqZ1DVE638DfTZ/hV3Tb+5m029kuL5BKhURrmMZyGz29hRZDjNt2svxGb7jH+rY/SF/8KBJcf882/wC/T1JBdXDxKW1GIHHQmOtBrW/jwGn5xnIVCCD0IIHTFPlRPtH2X4mSXuP7h/78vUbSXR6Rn/vy9Xb9b63t5JVm+6pblF7fhVPSrm6u1mea7CbYy6/IvOKLIPaPsvxJ5ILqK3gleORDICdxjwCPbnPY19KfDHTkt/CFpeSaLbaddXCli0QJaSMnKsSxLYIwcE/lXzzc3l9LoOnyS3cgWWJt6gKAQCwxwOmK5xdevl+VL25UDjAmYDj0GeBjFEbdB1eZWTR9u4pGIRSzEKoGSTwBXxKNf1LPN7cucdp25Oee/pUw1m4lYRTXVxKjNgqXJBHYjJ/SqMT7RhmiuIllhlSWNhlXRgwP0IqSs/RLSGx0KwtbYDyYrdEUhNmQFHO3sT1q/QAtZXiWwTVPDGp2T42y2zryMjp6GtWq2o/8gy7/AOuL/wDoJoA8Ai+Hls5RWltxI+SqeUCSASOyexqwfhlGByyfhbH/AOIr0HQo0e+kYjLJAu3PbMsufzwPypl/4rs7O/a3EMtyiqwL2qmUiVWwYyoH3upAzk4PHTMtlqN9jzdvhvbm4WD7RAJG6I0OD9cFen+FYPjS1+0ahpdnu2hxBHnGeuBXtl/Er3+mzKCGExGeRwUY4/MD8q8c8S/N4n0UH/nrb/8AoS1nN6o6sOvcmZvxOsktb8pGpAVh1Yt1Ve5ro9P0/fGpwORWZ8WRnVZR/tr/AOgJXUJd2ukaMb27bbFGgJwMk+w96uPUxqq1vREY0oEfdFRf2Ukd1bzlPmSeIg/8DUVl2nxIt5LlRPpjxW7kAOJAWGcjOPqK7W5WOSziniYMjSRMrDuN6miWzFS/iR9UeNa1Hv8AFTj0b/2YVYN1BZRQwzQsSGcq2SB1PTH1FJqI3eLX+v8A7MtV/F8YSKy44Lt/7LUwSlFF4h2qsnm1K1lEfLAIfugH9DTjq1j82YSAewZ/8fpWT5MGM+Uq+mPSojHEZmGwbcfL+dPkRlzM1Y9Vs4hIpBIdgccjH5detaWjXkN5qNjFGjYF1G24k44PPX6iuVeKDkeWOnH1/wA5rovCEYN3aP6Tp/JaUoqzZpTb54rzRT8ZoP8AhJ0GP+W2P1qC2uI7dPnjOASSeenH+FW/GXPihT/03H86wLdne8u0Z2KhXwpJwOaKavArFO1ZmtcapbXFusIj2ANuyATnr6/WiLVrRIkjaHO1Au7c2Tj/APXWTbxxtChZFx3OOetOeOLDFYwPQ8U+RGPMzTuNUtJkCrGY8NkEEn8OapeKBH9ssSOhsIDx/u1XCQYTMeT/ABdOlWPE6qLiwwORYQf+g1UY2FJ3MTywcjBp6W2bQyf7RFIxP2rGTj0/CrsX/IJb/fND0CCuygIwq5IpCqHvViVf3Cn6fyqIKv8AdqiRpVKTame9PIUHkU0hc8ikAgjDfd7VJDbb4JH/ALpFOtRlWq5bjFlP9R/WhlQV2Z3l7NvvXsNrZb9I0ttvWxgP/kNa8jk4SM+1elDximm2WmwSWLPDHY24aQOM/wCrXt+NBPUdrtps0q7OP+WL/wDoJrM8C6fFeSyQyA4e26g4Iycf1rqdXe31DwrcXls26OW2dlP/AAE1jfDhM6iP+vQf+hCk90aQXuspi383R9FgJxvfy92OmXIrQt/BUU8jxx6gWaM4YC2zioANtrogPa6A/wDItej2rw2WnzXT4WOIPJIfYZJP6VEGb4lao41Ph1uHF64/7dKl0rwPEdR0+eHUVliNwnIgA6MK7nStastS1Kext2LSQxJKxAyAGz8p/usMcg88/XFhoki1K32qFBug2B6naTVpnK1Y9O60UUVRIVV1M40m9P8A0wf/ANBNWqqapxpF7/17yf8AoJoA4zw//wAfk/8A17x/+jZq5O5029h8d3Bs4YpPKh3w2ysYnaMOHbbIRldztjOSGywyP4Os0HCyzMcKv2ePknp+8mrI1Xx4lteSw2C2t3Es0aLOZtkfKksu48MeBjbk/N0JHzRI2hfodFKWn+wyvC8LeaGMblSy/KeDtJGeexNeI678/ivQ/ee3/wDQlr3SU7jETgYcEivCdSPmeLdAA/5+bYf+PLWc/iR0Yf8Ahz/ruS/FfnWpF/6aoP8AxxKtePowPAkX/XxED+TVV+KPzeInX/p4jH/jqVpeObO6vfBsFvaQSTStcIQEGSAFbmrh1Mq/2fRHnrqVt3J3/eH97H329q9l09dvgiwY8nbFz/wMV5K2i6mUb/iV3hYnI/cdfmJ9fQivYY42h8C2SupRgsIKkYI+YUS+Fk0v4kfVHkl4N3i4j1Yf+hrUfjZdsViP9t//AGWpJufGAx/e/wDZhTfHRwlgMfxP/JaKfwlYr+KzOZy0bZdmBbOD/F15Pv8A41HvJupiZXwyHnJ+f5s8/wA6PNYq+XGSwOPXrzUjKi3N0xuVLbR2/wBZk5OPT/PvTMyJyfn+dsFQP97px9P8K6PweuEgf0nT+S1zXmNiTDAblAIPcccfp+ldT4QAFrCe/nr/AOy0pfCy6f8AEj6ozPF//IzRf9dh/OuftP8Aj+vP9yT+ddB4v/5GSMj/AJ7D+dc9aH/Trw/9M5P50qXwFYv+MS2hxBH8xXBzkduafMQRJh2OT1Pfryff/GorVj5KfNjByPzqcgSQTO0oDAghP7/WqZikRxsQIcTOu1gRj+A8cj3/AMKk8UH9/YDJ/wCPCHj/AIDSMRElsI51boxG37h4/P8A+t0qS+vLe6ht3aDfPHCsGHPyqFGAcDqT9e3SmmJowm/4+/8APpV2L/kFN/vmr010w1oRiODbwMeQnp9K2rXRLfVfCvnQqIL3zmRQp+ST0BB6E/lSm0rGlGDk3boclOMWqH6fyqEHg8npVm7Ro7ZUdSrqQCCOQcVVDdee1WYgT8w5PSm5wfvEcVIQNwO8dOv9KjB5644pATWf3X/D+Zq3CP8AQ5/w/rVWy6P+H8zVqH/j1npSLp7lGb/Up9K6e8BaK2yGK/YIexx/qo/auZm5gT6V1d3Y38sFmYrK4dGsYMMsO4H92nf8DQyVudB4eDP8OrsEk4WUDJ6DbT/htj+0x/15/wDswqbw7aT23w/vYbiF4pB5mVcYP3BVf4cHGrRj1sj/AOhrSlujWn8EiOQbbfSs9r3/ANq13t5ZyX/h27ghL78yMqJjLsrMVX6EgZ9RxXBXB/0bTye16f8A0ZXpFldQ2tjLNcSpFEkspZ3YKAPMPUms6fU3xW8WVNH0iW28a3txMxmypkWWVCT8wUDawwvGHyNoIyOTnNdBdD/TYD1xcr/IVkeGPEEuq3d7DK8EnkyFUNuhAVRjliWydxJ28chSfata9bbPC3rcpz+FaROWd+p6XRRRVmYVT1Y40a+P/TB//QTVys7XpBF4fv3OcCBug9qAOU0Ij5j6woP/AB+SsC48DSz6jLcteR4eRZCNuA7IpMe4DBGGbHysBsRRgk5EdtrdlbywTC7nSVIzG0YQMjck88g9/X+tW28ZWq4zOcn/AKY5/wDZqlq5opW2Z0MQkSzgWZY0lCIHWMkqrY5AJ5IrwyVt3i7w/n/n7tv/AENa9U0zX7PVNYghEszSPlQNgVcdfU88VheIvCGlaZdW99bve+fZyJLF5kilCUII3YQZ6eorOSd0bUakYxkn1OO+J0ijxK7McKLiMk+21K7rSPE+iJbqr38YIHQq3+FcZ4+0i61O3XWlmt1SbbIUy2QQoBHT1B71kadMGiU7uozzVwW5FaSdrdkex/8ACU6DjnUI/wDvlv8ACszW/EGlX1j9ntLxJZWkQhQpHRge4rgy42qQQa6XwboVtrdxcyXUkyrbhWUREDJOeuQfSnNe6yKUuWab7nm8x/4q0f73/swqLxueLEe7H+Vdj4s8M6dpF6L62a7Mm9R+8dWXlh6KK4XxfL5r2g6YB61MFaNi681OpdFMybllzIjZcHhcbuvI44H5dRx6LvJuZ/30ZBU5O04f5h93jj17cflUAJcNlo/mbJwuPXpxx1px3PK7loxuGDheOTnjjinYkkMhCyAOnKAcr16cDjg+/HQ/j1PhI4s4D/03H/stcnllVhujIYY5X+XHHTrXonwu0e31djFdNKEjBk/dMAcgrxkgik1o0VCVpxb6M5Txhxr6H/pqP61jR2vkvcTeZncjcbcdea9L+IfhPTbSKW/ga882M7x5kilePYKD+teZvOrQuM/wkUoJqNi68ozm5IjtHxHGN6qA3UjOOevTpUkrYD4YHJ6gdevIqvAD5a/Mgwc4I/8ArVK+WVvmTnnAH/1qoxHRt8tv+8jGGBGVzt6cnjkfn06VFI3+jIN6n5z8uORwOc4/r2P4uQMuz5ozsOQCufz45qKQERqu5ThicAc84749qAL8tqG1D7T5nf7u32x1zXQacf8AiiZOT/rmP6mufkuFyfm4+levJ4M0XTvBUQmubz94gldvMVQCRk/w8Dn/APXWc05JI6aFSNOTb7Hk/i9cXUM3G65t4J3wMfM0YLfmcn8a54HryOnpWt4kuY7m9kMO4QptiiDHJCKu1c/gKxx9R+VbHH1FJ+cHcBx1xSA8jkDg9qQ53A5H5UdPT8qALFkflf8AD+ZqzAf9HnqpadH/AA/ma37bTYTYeYzSZkXJwQP6UmVB2ZgO67Ixnkda9+0fX9JtfDujwz3apIthBuUqeP3Y9q8BvI1hn2ITgetegs4Fjp4JPFjb/wDoC1RPU7LXvEekz6VcJFeIxMbADafT6Vyfw8bGqxH/AKc2/wDQ1rMvDvt3UPjK4re8KaPcaRd2s00sLK8BjAQknJIb09BUS3RrTl7sildnFrbf7N43/odehwQLe6fcWruyJJLKrFeuC5zj0+van3vgLR001XL374Yy8SoOSc/3Olc4PEFrYXdxEXmDGVmIKggZ545FRBNXNa9SM7WOh0Tw1Fp8slwspt5vtLSqbVsB4yFGxgwPBIJx2zwRWpqsuBAwz/x8p/KuWi8Z2y9ZJP8Av0P/AIqg+IbKaOOKN53cziVmkx9PWtEjmbue9miiiqJCuG8U6o1hq0ttcW91cQTIrBEIKEdMEFh3U9q7muK8WMBq6HdyIV4x7tSYHPLqOnnp4flGf+neL/4ql+26bnB8PuD6/Z4v8aljYeWS7LwTnIp4dV7jA780hjIb+zikSSDQ5Y5AflZIYlYduu6rc2pRXC4n0e4l9pEib+bVCHBZchcFgMgVZlC5wGGR0J6UAVDd2MsQhfQXaNeAjQwlQPYbqg36TGCR4bC49LaH/GrfmgnBYYznIPFLApuXMAByxxnHAHegYzT4NP1J2I0SOKNR96SCLk+gwTVLxbrNj4S0dmtIIILy5/dp5cYXHYscDtn9e/NdNObbSNOeWRhHbwLlm6Hp79Sf614tf6hqHiHxLLfmK7VVJSIRKflTsM4/yc0Ab+la99pso11GG2vJABmRkVt2O5yKvvPpEzAyaRZEj+9BESP0rNgj1ILnydRPHct/8TVpf7QAx9n1D/vpv/iaALAk0b/oEWA/7do/8KcJdG5/4k9j/wCA0f8AhVXOoE/8e2of99sP/ZacBqAOfs2oE+7t/wDE0AWd2i9TpFj/AOA0f+FWLbVLKwB+x2cdvnr5CImfyqhu1Edba/8A++2/+Jo3X54NtqH/AH03/wATSGacmqWd4CtzZpMp6iVUcfqKr40THOi2OT/07Rf4VTH9ojIFvqGP95v/AImng6gOttfZ/wB5v8KAuT7dF/6AtiP+3aL/AApGOiD/AJgtkP8At2j/AMKiLX45+z3/AODN/wDE03fqPa2v/wDvpv8A4mgCbOiH/mDWP/gNH/hXA+LPEbaB4pWO206xmsmt43+zTWyMoJHO3j5c+1dzuv8AP+ovv++m/wAK8/8AHkMkniCNnjl3fZYshs56d+KaEwHxBs5Y8L4Y09X9TDGR/wCg1T1LxVqOs4W6k2wD7sKcKP8AGsdYWXpHJ/n8KSWSSNfuyD8TTFclaWBvvQofqorUsPD13qESzRafGkLfdklCop+metTeB9IbVZ7jU7i2muLW0YIkSgkSSnkZ46ADJHuK9AZtRkO5oL8n2Zv0+WgEcBP4UvoVytnbT4GcQsjH8utYjiFGKtbRhgcEGMDFerF9Qz/x73+Pq3+FYnibSZ9R06a++y3KXduu9nYH94g6gnHUdfpmkM4HfCOkEY+iinNdlY9o4GOB2qP97/dl/Wmt5uPuy/maYjPvSkkquqjB9q9otbm30zRtD+2aXHPFPp8DRzLDGScRrkEnuP5EV4lOWE2GBH1r6T07SodX+H2jW0uFb+z7co+OVbyxg0AjKjvdKlUH+wVIP/TvCT/OryX1oSCNElJX7p8qLj6fNxXIC4n025e1uAUliba4PNbVnqIYZzz2OaQzfGsFowr6ddsB/eEZ/wDZqpfadMkkLNoBZiTktbwk/wA6EuAzKWPpxUsTLtJB7nrQBEZ9LXk+Huv/AE7w/wCNKLnThgr4fbOf+eEP/wAVVhmRTn9aYWUk9DQBZtvEF5e3UNtCupxtK6oC0oCjJwM4fpzXqdeVaWQuqWP3c+fH/wChCvVaaEzO+2N/frhdc1dLvUZS4wyEx4DjkAkZ6V3X2WPuo/KubutEshdSM9nEWZi2dg5yabEcsk8YPDMRnOA4/wAKk+0oqjkkjtv6fpXQDQ9NH/LjEP8AgAp40PT8cWMA/wCAClYZzkd0jgEg4z038j9Km+2q2Rg/Xd/9atxdC0//AJ8Yf++BTv7D0/GPsUGPTYKLAc4bhN/T5s9A3X9K6rS7A2tv5jr++kALZ6r7VRl0URpu060tRcqQ0ZdMAEHr0P8AKmzp4rNrKE+yI2w4bzG4OOv3KAOG+JniUXl2uhWzMYUYi4MfUt6fh/P6Vh6bZxRRqot76T3Dgf8Aslb+meC9UgeSRtaVC7lmVQev/fQrdTw3qG3H9tOfz/8Ai6AOYW3t8c6ffk/9dh/8bp3kQf8AQOv/APv8P/jddP8A8I3fDH/E3kz+P/xVB8N3uP8AkLyj8/8A4ulYLnM+RAef7Ov/APv8P/jdL5MOONNv8ennD/43XS/8I5e/9BmX9f8A4unf8I7eY/5DEv5H/wCKosM5jyYTwdOvyB0HnD/43R5EI5/sy/I/67D/AON103/CN33X+2JTzn7p/wDi6kHhy+PXVpfy/wDs6LBc5gRwDGdLv/8Av8P/AI3SGGAnP9magB/12H/xuun/AOEbvs8atJ+R/wDi6T/hG74OSdXlOR0wf/i6LBc5owW5/wCYbqH180f/ABuk8iD/AKBt+P8AtsP/AI3XUf8ACO32MHVpPy/+zpD4cvT/AMxaX8v/ALOiwXOVMMHfT7/6ecOP/IdcZ42RBrcIEEwH2WLgtyOP92vWx4cvf+gtL+R/+LrgvGvhjWJddRoILy5QW8a+bHAzAkDnkZppCZ57tU9Ypf8Avr/61UL4hVOEcfU//Wrrf+EU13/nw1H/AMBnqhqHg/X2QldL1F/pav8A4UAdH4CEEng4qbaeRkvpN5jkC4ykeM/KfQ/ka6ExwdrC8/7+j/43XHeCbfxNoOovDNo+sx2V1hJillKShHRwAOcZP4E1623h29P/ADFpf++SP03ZoaBM5No7fHFjef8Af0f/ABuo2+zQxTyPa3SIIJd2+QEf6tuvyCuuPhy9/wCgvL+X/wBnXI+LNP14btPto9QuVcDzJUgcrjrtB5B7ZP4UrBc8vIX/AJ5yf99f/WpCqYPySf8AfX/1q6A+Fdd/58dQ/wDAZ6Q+Fdcwf9B1D/wGemBxN2B5vRh9TX1D4Fu1vPCGlRAYkgsoFIz1Hlivnq98KeIfOGNG1Nx6i0kP9K9w8M6Jq1raaTMk5WRdOija2mUqUbYuQeuCMelAIl8e+HRc2h1a1XdNAn71VPLoO/1HP4fQCvPbXUUToGA/3/8A61esm28V7f8Al0J+p/8AiK5aDwVEZnNxbWwfcciMcDntwKAMq31RG2jB2j1b/wCtWhHqcaqAQxGePmz/AErZg8IafGwJtoyfdRVr/hFtPxgWkOP9wUWAxUv42XPIz3DDn9KUXSZ6MOf7w/wrdXw1YAY+yRn/AICKePD1gP8Al0iP/ABRYLmDBfi2u45wvKOGX5+Dg59K9MttYNxawzbseZGr4znGRnFcidAsWIX7LFknAGwV2FrpkENnBF5KLsjVcAegxTQjU21m3qj7Qc+grWxVO7jEUUk3BYetAGcFGOP50oUULd8cqtPF4P7q0ANCihkHrUi3algMDBNJPdqkoRQCSccigZEV6EdatxTbgFcfN6+tUpb9o327FPNK14VAOxeaBEV/poz5sSnHdRVRIAR90/nWmL92X7q4qOFyWPAwfQUAVvs4P8J/Ok+z9tp/Op3u3Em3C9fSmPdyo3yhenpQAwW/+zTzb+1AvZe+38qX7dJnov5UDGi265BpRbEfw/nR9ukHYH8KsQXDOAWwPoKAIPI/2aPs2ei5p816yvtXt6imC+k28AZ+lACfZSP4DSi2x1H6Un22U9h+VO+2SAZ4z9KAGm39qDb+1Avpc9B+VIb6THb8qAD7OOwz9aPsw9BS/b5emE/Kpxckwh8DP0oArfZ+vFILf2qSS8kCA8flUYvpccYoAPs3+zR9lPXFL9ulyOR+VIb2X1FADPs/tQbfP8NSJdzPk4FTQzl4mZzgj0FAFL7MGbaE6+9atrapaxZx8x6mqqXLAlxg49RStqBYcgEUALd3DyAxxZC9z61SS3Aq4t1uUsETj2pYbgyMBsUZ9qAIFhGOakEIx0P5Vb82NY923nOOlRi6PPyLQBCI1GeDTTGuehFWftX+wtH2jP8AyzWgRWWEeZHgfxDrXQbaqw26zwLIcDIzV0DAAoA//9k=","templateImage":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAG4AAADACAYAAADlTVZlAABIyElEQVR4Xu19B3RU17X2vLgkNjZFgJAAISEkIQGid4QQIKGG6L0KEIimAkggJJAAFUSVQPQueu+9996bbTDGLXacvJfkxYkT55Xv/75zGdvvLTt5STCs/Ovutfa6M3dmbjnf2Xt/e59z7jgctthiiy222GKLLbbYYosttthiiy222GKLLbbYYosttthiiy222GKLLbbYYosttthiiy222GKLLbbYYosttthiiy222GKLLbbYYosttthiiy222GKLLbbYYosttthiiy222GKLLbbYYosttthiiy222GKLLbbYYosttthiiy222GKLLbbYYosttthiiy222GKLLbbYYosttthiiy222GKLLbbYYosttthiiy222GKLLbbYYosttthiiy222PLPIf/yHf0J9ZXv6Ku2fq8620ftJf1uG/6o4jyJE5zXqT+jvkl9i/o2teQzLWXr/1C1idpHWsJhtdlPHVYbqi2dQD53cVqWTqIT6sS6oLLUCtRK1CpUL2rVZ+ptq1Fne3hSPagVqa4Oq+0EpNrSCeBzB08H1IFlYTqZTiqwqlFrUOtSG1GbUVtQg6gtbTWqtlCbNKU2pNahBjgsMN2pLg7LY8kgnjt4co86sEAr77B6T02HdTFtqe2p3ah9qP2pA239Rgc4rDbpRe1CjaKGOKyO7k+t7LAMQS5UlvcTx3MSp7XJpHUCT2ogtdWkSZMm7tu3b9v27dv3btiw4ci6deuOr1mz5tTq1atPU8/YavR0cXHxyfXr1x/btGnToR07duw5cODAlqysrPFsw+YOy2PJe5V2WB5NRvJcRMCpJyjA6gQ6UfAXX3zxCLb8Q/Lo0aPTDiu8+DksriCXKSN5LiLg5CZlbYppTZKSksbpxJ9//hluXL2Euzev4v6ta7h36yruXL/C99dwV+9vat813Oa+Rw9u4MKFC7hy+TLOnzuHs2fO4Pb5c/ji8SN8/u67eHrnNt65egW3uO/GpYu4cuUKLvO7N65dxv3bPO6Nq+aYt7m9feMabl67wuNar29du4jr167h6pXLuHr5Ih4/uIMHt6+b67rDazC/1TWafdeMPnl4C9euXuVvruAir0vXaB3bun69vsV9Os/VK5dw9dIFcz1S3ce5c2dx8ewZ3OP+X37wAT558ADv37qJ+5cv4fbFC7jB4965cwc3b97ELe63trdw5/ZtPHz4AH/+858NeLVr11aIqeewyJ0I32vfNv0/JvK5cpPqEbK2Nhs3bizWSe/duYnVi4uwjLp6+SKsWLIAq5cUYlHhTCycm495M6aheEEeimZOwZZl0zF69GhMnDAByUlJiB8yBHMSRuL6lk24tGYVds/Kx6pJ6Zg+ajimjk1GWloadQIK8yZj+dwpmDE1A7PzsjB/Vg6Wzc9HQf40LJyTi/kzspA3eSyyJk/GxPHj+DoF65bMQdGMKeY7c/IyUcjrmJufjYKZOSiYkY1Fc7KxZelM/iYDkzPSkT+VOiUNOVMzMW8Wjzk7F7NypyAvO5P7spCbmYZp6eMwYUIadQLGJCdj5PB4pI6Mx4KUMbi1cweOLV6ETTnTsDgtFQXjkpGfORnz58/HwoWLsGrVKtBVYvu2rdi2dQu2bt6MP/zhDwa48PDwUQ6LvPg4LKIiI3kuIuAUOEVjxYiieSFbdVL18gWFc7CoaC5WLeZFFszA1IwJmJI+HpNSefFTJmJyaiLGjozDhqJM9O/XDyOGDTPbnl26IHtAP1xetQLnFxVha2YG5hO0ib26I2VwLBITEzFq5AhMHp+ItfMm0RJ3Ye/GBQQwDevZ6KnJozAhcTimpI5GZko8xrEzJI4cjoH9+yKf4OVQp2WmE7ypSJ8wHpkTUzBhXBLGjIpHVtoYrJ47GeOSE5E4egTiBg9CetIwJI8mGEkjMS6RmjQK48ckYNSwwRg+dBDGjhqMkSNHIZ7XHxs7AH1790I8r3VOfBxubtqAQ7NmYnXqWOTHxSIrtj/SEkZh5sx8zJ9XiCUEdcO6tdi5fZsBT+oErnXr1qkOi+CJqIj4PTfgFCzlez0cFp3tsnTp0l066a0b17GteBF2bliC7asKsWlxnmmQlbMmYlH2GBROHo0FU0dhyphYbChMRbfOndGvd2+0j4pCaHAwJnWMwiWCdr5wNorZUNP79UZiVDvEdWiPAQP6o2+fXpgwejAyEvujaOpoLMwbhwW5Y7AweyxWzk7F3MzRyBkXi7HDemLooFjE9uuF5KH9sJafLeH3igvSsXJmKlbkj8PC3LEoyEpE5pghyEsbjnlZozBkYH8MI2j5afGYNHoApqcOw8rpPP7URMybloTFPMZU7ouP7YP4/t0waNAg9OnTG506dkD76Aj0iQpDTs+uuLFqJfbTMpfRArP4PrFDNFLih6Jg7mwsLJqHZUsWYS29yuaNG7Bn107spjqBa9my5WS2aSS1lsPyaiIozyUlEHBKA+SDRf97Lly4cL9O+u6Du1i7bB6WzM1DYW468jISaQHxyBzL3hvfH/17dURcn45IHtIVG2cloF1oW3TmTYe0CkaTunUwKaINQZuF41MnYWX8EEyKiUBs08bo1aolevbsgZj27fnbXpia2B0TRvTC2LjuSBveHUN7d8C4+N5IGNQdU5IGYmTfGPTq0hldYyL5/Z4oyIhDeuIAjI7tjuS43ojrGY2kuF5szF4YE9eD2guzJsSid/cu6NW1MzJ4zPHx3dkB+H0ed1RsF4zgb0fFdsPgft3Qo3MU+nRoh65duyE6OhptWrdGSHAQuga3QGb7cFxliNg9fizms+NNiA7HyHZtMIYdaUZ+HubOnoVFC4qwasUybFhbjB20tr17duGrryzgmjdvns02jXFY3syN+objOQPn5bDoa1/67kM66cO7t7B8Xj7mZqdh6oQETEqKxfABXTGidzQGdW2HgR1D0DOyBTqGNUVOYje0bN4MEWGhaFi/Afy9vDC+ZWOcn5mL45MmYHG/nkhr2wr96geiQ4O66EDQQtu2pjuKQpfIVuhG7dguCH1jQtA5vAX6dAxF3w5t0SM6BL2ig9ApMgIx4a3Rv0Nr9ItpjYGd2mJg51D0jAhG36hm6NSuBXpEtkRvft6dx8qI74RuMdHoGBWOTuFB6BIRhO4RLdC+TVP0bd8SncNaoFdkEPrx+314vJiQpoimpwgJCUFTdq5mDesjqmFdZIQG4/L8OdiTkozZnTtgbJtWiG3WGCPpSvPysjFzBmP8vAIsX7oYxcWrDXD79+4hcF8Z4Jo1azadbdrJYREUJeTPFTilAsr0FUT7E7jDOuldMqRZeTnISB2HMSPiMbhvL/Sgm+vQLhThIcFo27wxghs1QKsmDdGmRTO6x5ZoQ60VEACfihWR2qAWLuZNxfG0ZCzq3hEpLRqhk29VhAX4IZS9ulnDBmjboinCWgahHbVtUDO+b4bwVkEIY4/Xfmkoe35EmzaIJNDhrVvx82BEsgHDW3Or97yWdjxveEhLtKM1R/CzML0Ptt63M8dqweM3NcdvTWBaNW3EbROE6HWTxgjitYS3a4fmzZqhYb16qFPDHxF1/DGxZVNcmJmHfcmjMbtjNBKaNkCv2gEY2qUTsqdlYhaBm18wF0sXLcDa1auxbctmHDyw/xvgmjZtOsNhJeb1HRaPEBH8cYG7cf060sanYvDAgejSqQMi24WhWaNGaMuGaFKvLvyqVUVggD9qVa9OsKqjXu1AtGjciPu9UcmlDMbVro6zWRk4njIai7pEI7lxHcR4VkQbX28Et2yJxuzVzfn91gRCvwnwrQbfqlXh512VWy9Uo9X6+1SDD1/7eXub/X7VfJ5pNVT3sb5f3bz2Ma99+Vq/8+X3fbx0LG9U8/TkMfiZfq9je3nCu0plVKXqfZMGvI4mTcw1CbjAmgEIqOaF1uxkaU3q4crsGdhDYjWTMS++bi109vFEXMcY5JKlzpieg8K5s7CEsXzd2jXYTuAOEbg//vGPLw+4myQnKaTucfTnXQlcG/bewFo1GWuiDXj1A2uhaYN6aFy/DhozpjVlA7Ro1NA0VPm33sS4WtVwJiMVJ8aMwsL2YUhgT410K4cWHu6mgYJpEU3YYG1pfVU9vVClshqzCjyllSvxOFVQ1aOSaWAfNbaHB197GFAFtA8Bklb14O88qpjfe/L7VWjtVSq6o4q7GzzM1h2eFS2t5FoO7uVd4F7OBW7USvysWdOmaBHUAi1atKCbr48a1X1Q3dMDrapWQkbT+rg2Zzp2DiPpCWuFuNr+6OBVGYNiophOTMGcmTOMqxRBUYzbuX0rDr9s4K4ygU0YNQo9u3dHWNu2JoY1pKUp3rSke6nHntmgVg3U8fdDbT8f1OX7ZvXrwrNSJZT+6WsYL+AmjMGZcQlYROBG0wLDy5dCMzdXA1zzFs0VA9CieXNUqlgJFVzL4803foa3SryJt98ugVIl30aZUiVRmtvSpUuhfNmycClT2uwrV7YM37ugvEtpahmULWNpORcXuJQqzd++hVJvv42Sb7+Ft0uUoL5p6Vsl8NPXX0MJnkfAViZwzZtb1yBtUL8eavBealT1QJifJybRS1ybPg17RgxFTusWiKvphyiPCoiNaIeZDCNzZ81gujQPq5YtwcZ1xdi1YxuOHDrwcoFTNWBsUjKGDB6MbmR1IUFBxh12iY5k3AlCSTbCKz99E45XX4fjlVcRQNfVnBboReDKELiJtbxxIW0sTo8bhcXRoRhdi7GtXEkCVx5BPFYQezlvDKFko5UrVYa7mxsqsiGrGbdGi6Ia9+dUujWfqt5GzefcetHaPGhhXpVljbJQD2N9XrRMTx6zSiV97o6KbhXg7uoKN3aOcqVL46evvYrKfK19uo62bdvQAwSjMV13HX9f1KJVhVb3QkbDQNxggr9n+BBMb9sSsdW90d7DDUOiIjB7eh7mMSVYsrAIa1av+CYdOH70CP70p5cI3I0bN5CaksIEdSi6kSqHkSA0pisRWxMxcGWvf/XNt/EK9Sc/K4EajFFBJCtyXS7s1RmBPjifZlnc0vahGFnDB+2MxZVDcKtWbLCWhsVFRkQY9+hO0CoRBAFW3dcXAX7VGff8GOd8nqkvrcGP+3wZ1+jO+N6HLlYgOcFVfJP7rFbFk2ASNHYiD1pzJTdZV0WqGyrQQmV1FcuVRYVyrmjJThQe3g7R7aONJ6hLVxlYxR3h1asai7s5Kxf7Rw5FXpuWGOTvjciKrhgcGY4CusmF8+eRUS7COjLKLUzU9zEVOHHs6MsF7iaBW1OUgxEErme3bmjHXtmAsawj3UQYmZ9c2Otvl7aAo9akywxijKvK+ORCi0sPrIYLE5JxPiUByzqEI6GWLyJdS6MFLS48PJzpQKjJmdrSDQs4twoVjNV5sdE7htRGLXaEAAJVq7o/hnUKMi5ZwPnS0to1rYGaPl7wZhysxvhXgyD3iaKrC6hmYqyssHUDX3YuF+MOZZH9o5owdrnD1aUU3nj9dXjyXJVcK6ANO2Q7Msp2TGWaN2/KY/ihHr8XU8sHWU3qGIvbPzIOuSHNMZBgRlUsj8ERBK5gDhaTTa5cvhTrCdzWzRuxf98enDAW96eXC9ysaekYNmQIenTtinC6tPqBgehE4KJocS5lSuG1t0rhlRKl8Opbpcks/S3gKgm415HOG78wPhEXySpXdYwgcNURVYHAVapgwBJocpPRUdEkFh4GtMpscLnByq5lDSHxp3XV9Odx6/nTCn0MQLI4X8/KlnUROFmWn3c11PKuhICqBFLkhtfgzfOUL1fOHLMK39esWhmVSUzKlX4bP6Or9K5U0Xwmq1dHCgsLQyumFk1q10QDfrdjbT9k0eKuT8/C7qGxmN46CIMIXDRj3GC6ynlMAxYtmG+S743r12L71i20uN04TotzVk5eCnC3GOPG01XGxw1B9y5dEM7GrsMcrWf7KETyBhX8XzWgETwCF+hfnflQfdMgZQlcBsnJBVrbpdQErKSrTAz0RZRrGQRVcmMPD0cEXaS0K4/twVhkLI5aje5PcU5UPYAuMsBHbtNykTW11XuqP5mlBVJl0vsqxlXKAr0Ny7SYpidBFcuUq6zkLgsrj/JlSuJNuspq/I6Ak8WpihPK+wsJtoBryE7QiXlcVlOmA7mTsY8Wl88YN9gA54ZB7LzK35YtXojiVSsI3Dor+d6319Qvf/e737084GRxE1JSEUdyIotrxxusX6c2enaIRnTbEDI7F7xesowFHAEMrO6HVo2cwL2GKbV9jbVdJnCrY8KQFOiH6Apl0JLARUVFIjoyCjEx7Rk/uxqLE3AV2cCyJL+qytu8CU41owJNGsicUfFOgGq/D91iZTd3A45imwAUeLJCERVZmlylSIi+9z+Aq1yR+y3r79ChgwFO7LlRYA00ruaBznVr0FXWxaXsydgpi2OMG+JfDe0rW6xyQeFcrFi6GGsI3KYN600qMHfObOTm5uDf//3fXx5wsrjUseNMHtetSydE8KYa1KljLC6idUu4lLZc5OslXfAatTaBCyFwPsy7XEu8gWl1fXB5QhKuUpdHy+LkKsugOYN7RzaUAY49vQstriobXcSkEgmEkuWqVZhEM4GuLqszCXnVZ8D5oaavkm2BykScOV2lCmSjFSxgFMsEpjcJixinLE773cUmy4qMlEX50iXxBl2lD2Oxe3lXQ466MYaHMcYJvMa1a6GZryc61Q1ARqPauDhtErbHDTQWN6ymD2JIXPq1bY3Fz2qU65kGbNm0ERs3bMCYMWMwMS0NX375Ei3uNoE7urGQrDLOkJNI3piS7r6dOyImtA0qlC+LN8qUx09LueANF1fmdDXRuklDNqYn3JiAT6vnhyvpY3FpfBJWd4pAMl1PezcXusoK6NK5Mzp36ohOHTuiB/NEJeACrqIan6B1bRtIMkJLMxbnjXF9W6IR04ladJX+jGfxXZl3MQYF8DMPWlRlWmpybAQaB1hWV5UA9g2vCw/GNBGVyhXcMWVYWwR6VYALc7yfvfoa/AiuzhfFGCvgVPKKZKxr0aAumjMV6Fq/JjJpceenpGNnvJUODAnwNhY3KDzMlLnWrFxB4NZiC4lJXm4ugUtG1qT0lwucsbhx40w6IFcZw8S7KdOB/szpukSE0b0x0XariNKu7nirfAU0ZI7XpmljNqwX3Eu9jeyGAbiQmoTzY0dhVUwoxtQJQIdK5ekqK6Bjxxh0jIkxxKQ7G82T8Ug5nPI5UX1ZTkA1uUQLPG9+bmLcMxU4NXxkcZ6mMuJF91zR1Q2epP5ePIYnwXItV95UYERUPPl5Bb53L18OZZjcv/bKK/DlZxVprTHtY8w1tG3dBh3oupsTuBb+VdGpXgDTgdo4nzUR24cOQk5IC8T6eRlWOYSuculipgGrVtPS1mPb5k1IT0/H2OQkZE1OZ4x7ya4yMyPDVE/69OyJLmzoYOY5Q3p1R2/GOT82aoUq3nCt7MUtk+/69QzbrE3aXoU9PbdRDVyeOBYXxowgq7TSgfYVXBDs4U7X29W4yM6dOqFPr14GOLFKdzakylnVVbskMDXEJLUVgNQaz2JdgI+3AVSuUqBWUy5HFqqtYpvAkiUKSMU5D3eByo5RoTzKslO9/uqr8KMrFXAdCJxct5ilvEBw44Zo7ueJHnSTWY0CcSErjenAMKYDLTDQpwrzuPIYyo67avlyrF2zGhvWrzfF5cmTJ2FMUgJyp03Bly+TnNy6eQNTMzORnJCAwQMGoEfnTmaAdHi/PhjYtQvqBdaEZ/Ua8PKvgSq+/ghhDtSB9L4pc73q7M1zWjXElYxxuJyWjNWdI5FSvxZieNMhjIE9e/RAD/ZyAdezR08CZ6UD3wInoJyA+Zi4ZhimcjuRFsa8GvyegFOy7ePpwdeMhVUsYmIsz1RULGur7FaBBMbNAOdaxopxAs6jYmW0p9V37NDRxNyunbuY0Y4gWlYPusmsRnVwYfJ4k4DntwlCrI8nIsqXwTB6n1V0k+vWrDHEZNvWzcicnGksbkbutJcN3E3s27ySzHIcRg2PR//evdCBgTwxbhBGxPZHUNOmqNWgMWo3aoKa9RuhPWNgrw7tEdKsKer5+WBeWBCuZqbh2uRUbOzZGROYE3WtWgltq3ka0HrRiuWiBrJTeMlVsqHdGYtU/W9Swws1q1V95hp9ENelFRNyb/Pan2SlWS2SFeZyNTQaYMpclRDTqg73+5kUQfvaNq6BSiQjsrSKZJWxXdsgwIvnKFsaJZmu+BNs9/LuxlUKsCiCIfcd0rQRWpL2d20YSOBq41LmeBwcHc88zrK4sHKlMDQyHCuWMw1Yt57AbWAqsA3Tpk1DKtsqkzHudy+BVfab/2wgVRa3bP4sBtsMjEtORjzTgn5s7AmjRyJlZDwDeTu0aBOK5iFtEdQmDD0Ztwb37kEA26Jl/bpY1CEUN3MzcS1rAjb07YaJzRqil19VhBG4fn37YcggHq9vXwxhgu/FxvagpSiPE4MM9K1CImIBJatrWYckhfGsBl1kDZ+qaFHHDw0CfFGLn1ku0BUNA6qayoiScmmzwGqoWknxz/q8SS1vVHUvDzeX0nhTFkdrdC1bnlbfGd279zCDux0InMbpWgb4oCctbloTWlxGCg4lDMPssGAMIWmJqexKiwtnGrAKm+gmdxK0PTu3Y+qUKcbi5s7IfWEW5xwB1xzAPgsWLDjgtLiZeXmYkZeLSWkTMDYxESPJMKdNHI8p41NoNV0R3bkrOnbviaiOXRAfOxCJQwejN8lLZHAQVvfpjBv5U3GddHrToN6YGhqMPmyQ6AA/M78jNjYWg9kZ4obEkVV6GlYpd6kcTjmarC3QT+pDEAmSr7QaQa3GrY/5rAbdpiddoIc7AfdSrGMyTkBUdvP2EFGpaD43wzqMc1X4PVncG8zjLODKkeF2ofX3MmlBJ7rMkGZNEExG250eIrtpXVykxe0fPhizwloxj6uKCJGTdqEoppvcvGkDgdtqapS5ZJVjySozJk5gHvfbFwac5pw0ofZYtGjRXidws/KnY8Z0gZeHqQy+E5inFOblYE7ONAwfNhQD4+hCBw9F7NB4pCYnIpOAjhgcS/LSHptHDMKdghm4mT8N2+MHITuqLQbXq4lOTHCHDx+BuDj+fsBADI2Lgw8pvnI4VfHNYCpjlyxNFqcUQPmbwAusboFXU7GP3/NR9YNWKssKoGtVrBNwAsloxQrfAqctgatUzsUk4AJOTFNEqRfjbFhoqElPNJrfmh2sS8OayGteD2cYow/T4uaGt0acgHMri0EEbl3xGmwlm9y5fTv279mDnBwBl4SCGTkvzOKcs7waUDstX758hwHuxg1sWLkAswjcTGp+Tg7mzpyB00eP6OOz1IfUD6mfUN+jXjxx5DBm5mYjaegQ7Ekbg3uLCnF3/mzsIrOc2a0D4ps3RLcGdZCUmETw4gneEMQOHIhqBE7lJ1XwVfEPbRqI2r5yi5a77BfZDA1q+JkkvzYBDGtWmzHQ01RpNDhahSBFhzRAU8Y4FQCqsAM0qlUNFZlreri5GmA7tG7AuMjvlytDV/kMOFpct27djcVFkZx0o/W1adEUrRjjupGYZNNVnp6QiIMjh2BeZBsMr+VDVlkOg5nHbVir/G0zdu/cSeB2IXsa75tELjNj4gupnPzveZWRxcXFG53AHdqyBLNn5KN41Urtukv9D3NFf12e3tq+GQ+LV+DB8gXYnz4Ohf16IpFJbL+gphifOp5pxmiMGDECyUnJ8Pf1M2NnKkF5020G166CenRXAkrAdW9Vg0SkirG0GiQnHVoGooG/9dqq8pdHTFANNPKTqySbpIVFNQ2Ae7nShkmqaD0guglqefG7ZV1Q4vVXUUOFAgHXpRsGM952Ykqg1CSidSu09fdGdxKTbOZxZ1JH48jooSiMaI0RGuGoWBbDoiOs+uTWrdizaxcO7N2D6XnTTW23YGbeC7G4785kDqC22rZtm0FJtcr5BXP08jr1v52IXLp06dOioqKLo0eP3tOzZ8/1nTp1Wjts2LAd+fn5p44dO/b+559/bpXGLfnw6YHdOE53uWREHCZ2icGQ0NZIGz8ByYyZowmexvxqBtQwUw80lcA5N0R5m4AScMY1Pit/+cuNMhWoSWtUvKsqckLWqNqjXnvRJXrRArUViMY9yvIIbmVu5SpL0OJqGeAsV9mvTz+Eh7Yju+yMULrKEKYD3ZnD5SgBT0sicENQFNWGwPkYVzk0qh0ZpYDbRovbZWZ28f4Nq5yePeWFJOA6iCZpau2AmGWDOXPmTNBJP/pQXhC/diJAUG7WqVNnLr8zgTqaOpQ6iBpLHUIdQU2hZhcWFp5///33f/Psp//x7rFDKE5NxtR+vTCKyfu0KVORmpqCZLLVlHEpqF2jliEoqi3KVapgXF31yWpWrFMSbkAUeASuOomIP7UGPzdAETC9tsBzM65SKhepOFe5fDlaZgVjbWKVP3v1FdRkPHQtUwadO3bCAKYkyuN60G2GtmyOYF9PdG4QgGmNA3EmZSQOjxyMeREhBrjICmUQFxGK9WuKsd0J3L59yJ8x03TCGTlTXkg6oIM4V+to3l91V1fX5l9++eXHTsAIwP2YmJh0hwVQH2pnagS1NTX4mYZQ21E7UrVeLJGae+jQocfmIP/937h99DBmjxyGsb260a3kISM9w8zTn5g2EXVq1oQPrUjA+Xn7wKeym3GBGnrxJaDN6aICCZzmttSmBTav7Yd+YQ1NvHIjGHWqeWBC72A0IF1X3KtYXtblgoq0LrlKWV2NKmSf/H6FMqXw+k9+ghrMA11Ll3lWveljUoEeTAs0la81j9O5fk26SgI3djgOxQ/E3HbBGBko4FwwODQEG9dqDI6McvduHNy/D9OnTzfrJgrpKl+UxTnXx7k4rKVW1d3c3BovWbIkqV+/fn35PsxhzX9v5bBSBl2EFj5q+VC1Z6pFDXK1Wr2qVZpaDClrnLRv3753LOz+G0fWFyOTKUPBnLnInjoV6RMnYvKkSWhUty78yR69vTxNhSS4rlhjZVPCEngRTfyYPFcySbkfLSVnZDS6tw7E8K7BcCn1Nrq2rouuwQEIb1ydqYA7XWM5JuNeKFuyBMlJeVpkBfQJb4hanhXg9gy4AJIYNxcX9GZc69enr2GUfUhSIkKC0YbssWP9GiQntXFqTDwODY/F3LAgDK/JdMaN5ITufsNajcHJ4nbigGGVOWSVYzFu7Bj89rc/fjogEbPUYgSxS7lMWZ7cZnWHtYJHIAkUX4eV7wlcLWAQ0FqwJy1DLeewLk5ACkCBLeDTneD9+es/YSlTiSWLFxurU5koIz0djRtoWpyfmQAk4HyNq/SCH4FUDdKHCbpfFblHT5MGNK1ZFZ1b14E/rUhTERoEeCKkAVMHHw9DVDyYcEvlHqtU0HvL6jw0Pc+4Sgu4sqVKMa51NROiNGKhQV1Nxm3FGNehrj9ymtbGieSh2B/XF3PaNsdouspod6YDbUNMjNu2ZYuxuEP79zPGTTerffLyZHE/PjmROK1O4IlhOhfuaxG65ruLuAgo7dNn+o7iotZ6OR8XodfO3wtEgSvQQ6j9qJnHjx9/qpt58vAhVixfjrlz5hgKPSUrE61aNKe7rMF8zssqHmtSqwZUGcdkYQLMxDUSiuqeVcy8x+pVVPLyMrO1KhCMqrSqirS0KgSsClMAAWXiHUFzL1vm2daFrrIkfvoKXSUT9PKlS6En3eOggbGMdR3RmxYX3qolgn2qEDjFuFo4OjoWh4b1Q0FoEIGrhvbuLogLa8M8rtgsqRKrPHTggAFx48aN2EMgX8T0PIkOJHb5XQDecFgncT7+Qe+13wmWvv+/L+C7HcAZM2WtodQ4Ly+veb/61a/MHSmYLyiab+KCemiroCDUqVXTFJhVNalLqu9LYKp5aO5IJbSsV8PkbQGaGERLbEyXFehNcuLtaWZriTU2UMmrirtxjYprdfw8CRytja5NE2DrKv65lzPAvf7Kv8Cf8bScLK5rV/Slq4yJbm8YZnSbELRk6tGR55zKJPx4wiAcoMUVhLXEKFpcDC1OzHhdMWMcwdpL4DQJ9tSJE7hw/pyZAe5c2PhjAyfRwZwASuU+ndak1879zu/9kOgzp+sVeErsddFa+JCyffv2+7qhD548wWK6y1mzZmHWjBlo07Il6gYGMhH3MjO5OrcJRB0mwb6Mbz4Eb1hMfdRnjuZkk93b1iVh8TaflXn7LZQt/TbiOrZA8wAPpgIVjIvsGVoX1SspAS9PYF2Q1L8tmvi5wY3fVYyrTgbq8nZJJuBd0bt3H3Tp1NnUK7UGQayyfe3qBrgjw/vj4LC+xuISAn3R0aMCBrUOxjqyys2bNmEPY9y3wF0w+e+LBO55ihM8Z5ohEhNCHRIcHLxCN/Tnr7/GihXLMWf2bORkT0NIMIGrHWjmU9ZkrNMQjZ9cogGKblPjbSoeVxHLlOvkfr0nESkt4EhQPCqI8jOWyU3SXSpN8HR3NaDJVSp/q+hCVkngXhM5MaC/baysL4GLidbwTgzaBDVHC7LU6MDqyKxXHYcJ2oG4Pih0WlzFcogluOtITrYSOFnc0cOHcObkSWvZ8q2b/7TASZxu01kH1eMjelAnO/M7xQLme5iem4ew1iFmlYyvEms/X0NIDDnxtCzMUk8T72RlvlJVSGhdZUuVREUC462YRkvzcid4IiNuinVW0l2RaYCISWWC50qQX/nJvyBA8z9Lvm1GB/r362+WWWl9XySvpTkZbHRtf0xlLifg9g/pTeCCTDoQXbEsYtu0xIZ16y3geB+aBHv21ClcvnSRwN36pwZOIteq2CiCo3KaFvilnDhx4olu6ty5c2YdteJcu7atySwbmLmTGt02TNKAVNkCjaSklk9VvvYwsU+AeT1jjGVpNQLHl/mbDxNxAaj3lQmUrE35XOXyZQxoyvsqELifMgGvweOUK1nKjAlq+bOAi9DE2FZBaOpZEZGMo1Ma1iAx6UNW2QvzZHF0lR08XAlcKzMOJ0IiV3ns8GGcPX0KVy5d+v8COF2gYp0YqUhKJDVx8+bNd3RT165dw7x58+gqsxHFBtNyJ38N1VBjY5qhgb+XcY2qfozv0xIN/ARkRcMaR/VohSAzvubKxn/bWFz6iBi0q1+NgLryvQv6tKsPHze6SH7mQdDGDmzH33jS4t4iq3yF8c8dZUqUMEM5KjJrCXT7qEi0pats7OGGdv7eyKTF7Yvthj2DumNOaHMCp3TABQNDgsxckx3PJsEePXTQcpXnz+P2zX9+4CRO4JQaqNqSQMp8Szd188ZNLFiwgKxyOiJDQ81KIM1j0aRXM67Ghq1KK1Lpysv9W3rvVD+C6Felkkmo3ZnLVXEta6zKnValiomsS6Aptqla4uZS0oAmV/k6XaWAK/tWCXSI6WDiXFRkJGLaR6NN82YGuPAAb0yuXx37B/ckcN1MHifgoghclwa1MaD/AIwcORIp48aZ+Sb59Bzz2RHXrl37QmYy/5jitDjlg1q8LotLcjJLPetENypyEvFsXUJ1DZJW9zWWphqj8jG5POMav6PeBM6HDe/HWFehdEkDXhUVkMtaREQqKxQZMWUxAluxbGm4MxVwLfkWyYmAc0Ppt95ERwOcpi5okm4ELa4FGlZyRTsCl9XAH7sHdsWegd0wlxanGBdFphpTyx8hbdqaaQ9ipMPjh5snSUybOhXzGLe//PLLf2rgnCMPyudUSVEdM/XChQsf6aZOnDhuWGVWZibahoSgUf16CKCb1CpUb620cVP1o7wZb1NlX2RDlubNBvchMfElcNX4vhwtyJXgGWtjIi5VIVlbK66psOwErhTKlyzBPO4n8KEVl37jDUSER5pZXu1o9Zp3IlbZjDFOq3Wy6vlh54Au2N6vM/JDmiBeCXgVV3RrUAsdOnYyM9U09WLkiJGmdKcRgqVLl+L3v//9Py1wTlapXE7PB9MTHfSgsqzPP//c3NX6detM8i030yY4GPWYx2lIRytRw5vVZCpQ2STWSrAHdQhCzaqVUE1gEbjOIfUQyPciIaXefIP52FuICQ5EU38PA5aACqnvxxjH12UFZCl0aVMXDZkLlleMe/UnTMDdUIYW16EDAejcFW3YedpHR6EdE3C5yghaXGY9H2zr3xk7aXV5BG5ozWqIYAIeHcC0IKajqXPGEbjEhCRTNFfZa9HCRS8FOB30h/T/Kk7QxCgV3wIcVt1yWGxsrHn4jVyJUoE8zWfJyEBwc5W8apqlwFqf7ebytmlsDcfIVdaqQndJai8XKTZZ1a2siVkC7q2f/RQuJd6AZ7lSBigPuUwySc/ypcg43zDxzZW5m3cFuk0etxwtTq7SlxYn0DWDWQVmrQHX66BmTVC3oitCq3sincBt7dcRu2O7ojCiBcbQdXbwrEDiUg3t2kWgF4HTkNDoUaPMhNic7By6//kvFDgd7LsVE5W2NNwjdZa5nBWUv3RiJ2hKvlV8lrU1dliPTUw7e/asGeT74P1HmDt7DqYyJqSkpqB5k8aoGeCPyhWttduyMpWtVOXX9rv0X/mY3KaHq2WNpQiagJGlVeZ7uUTFtcqKdXSXTuDKlypBN/kWXN56A68SOB8eo/SbbzJOdUD37t3Nqp3wdmFo2rgRxg6Lxa3zx/H43m18/umH+PDxu+a5L3q22JXzp3Fg52bMzJ1i5oT26dsXScnJ7ICTzAjBokWLXxhwOpBAcdYpdXAlzt99dK1GDrTfWa/U9797AU7gnZYm0PSsRjMdgjqid+/eW3Qz//Vf/4V9O7cgj2lABq1tHFmZUgGti6ukwU4REqrqixUJkpk38iyhNvGOYFpVEo1suxC4N40VOXM1NxKQioppVDFIgaatviM3WY7uUa7St2J5lCbokZFKvDsitE1r5nFhxuL2blyFKycP4f271/H+vRu4d+UMLh3bhz0bVmHTiiJsXbMEs3MmY9jQIRg8eAgSEhKQnjbRALdw4cIXBpwaXGCowQWSylRKmtXwqnqo3qhqv0YJnCMEugBZlYB0qvbp93KPXg4LNA2uDnn11Vdzb9++/UvdzCcfPsHWtaswhbFt/PjxSCAb0/ItH8Y3rdf2ZwKu6XeWkmX6PZu+YF77oG5AddTR4zlq+Js5Kap+aAFHdSbp/mZ0XCMJnqZUZkYX6HpNxUUznGm51rDOK/Ai+G/99HVjae2ZBmjBR3hYGJo1aYRd65bj2pkjeHjtPB7fuYoH3F44uheHtm/A1tWLsWL+DEzPmogBTNwHDowlORmB1JRUTJky9YW6SqdrEyBqdIFVvUSJEmKCOqFUIIjSa4yuqsO6CNF8fV/jcNqKPco1KqbJPcrShlCnnjhx4qlu5I9f/QGnjhzAxtXLMHFCqpntpeBet1bNZ0uiXM0cSTVwtcrupsjsy1xN+Zom99T09kJgtaoIICAa1lE64M3v+TDfM8RFdUq5UrpRqZXbMQGnNSoVEKMsSyt7+2evmaS8BIFrS0uLjAxHNIlJW1L8JgRu+5qluH72CN69cRHv3bqM2xdP4vLJwzi2dxs2rVqMxXNykTk+Gd27drGAYz6nsTgNU6ka9CLSAR1E1iZX6LSUWk+ePNmpE9+/f/9gWFhYrMMa0XZOV3COggtIJdZSvRbQzscBK6aNfP311/NOnTpl4tp//dd/4v6t6zi6fxeKlxUhZUwiBsfGmmVWqph4aPJqZT2jxI3MspIpbWm8rYZvVVpeVdRlflfX3w/1AvzMtjYtUTOZ/b09UYtgaohH09J9CaRSBhEZgehVoaxxo5brfMsQllJvvA7PCmKkP0MYc0gtaBSALZnDtQ4Ows71y3Hz3DE8vnsNTx/cwi2+PnNgOw7v2IAtBG5l0SzMyc1EbL++ZkgobshQszZO09AF3IuwOB1Ebk4xSS6x1uDBg3vopJpqYKzkj3/8/Zo1a7bxMz17URbUx2HNO4mihjssdyjrUp4myj+Mmka2te3u3bvGPf7nf/4n3n/nPs6dOIz927dg2YJCpCQnImHUSIwYPhzhoW0QzEYLbtEcIcyj2ujxTqTl4W1bIzo8DJ3aR6FLDGNR+0h01jaK2+hIsy9Ga9LD2iCqXSgiQkPQtlVLhFJDWjRFMONVcNMmaNG4PoJoSU0a1EWjenXQsG4gWjZpiGZ837NbV/Ts3pVxLgYd6TJ7de+MLYxhF44fwN0r5/AeXeXlk0dwbPdWbFq5ECvnz0LR7FzkT52M0cOGmsc3pqSMQxZdf8HcuVi5YsULqZzoIE7a7kNtunHjxgKd9A9f/g5f/cHqORJa3yfMuw7TirId1kyvZIcFplSTg/RsxiwmpNvPnz9vkmzJ13/6Ix4TtPs3rz57qux13L52EbeuXcKtqxdxk3rl3ClLz57AOTbS1QuncJmvr5zX/hO4SiZ38/I56nncuU7Xxd/evHIB1y+d43dPm+21C2dx6exJfv80LvN3504fw6mjB3GWLu78qaPmuBe579yJQ8ZdHzu4G4f37cTJw/tweO9OHD2wCycO7+f+vdy/G/t3bcP+nVuxc9M67N25DXt2bMWOzevx3r1buHzmBC6eOYkbvIZ7t2/i/p1beHCXyq2eNvsfL6BW6axuiIyoENx206ZNa3TS3/z6X02Df/apmfBlzX55Jjdu3Phs165dD2iJ11esWHFl27ZtdwXWF1988c2cSrHHX/z8EwPWRYJwmcBcOKWGO4xDu7fh+IHdOEbdt30jDu3aiiP7duAg2eYRutLjB/bg0N4dOMIGPcjPThzaQ91rGv/ovl04c/wwQTlgADh+aB8O7tluNTi/f4Sf7922CXu2bmCjr8fOLeuwdf1q7NiwBluKV2AT4+smbtctX4wNz14XL1+E4hWLsGbZQqxeUoTVSxdg+YJ5WMt9yxfoQavzsIJeYv7MHIJ2HGd5/stnT+MGQbp59bJ5DPGd69cMgHdu3XghUxcEnOKbWKMezdd+9erVm3XS3/7630wPevfeHTx6eBe//MVnTgv8ZnLs98mfeNGfE7Bb1y4TrNO4cuEMwbJ6/FFNjj24BwfoLo8w0AuYw2z0/Ts24fDuHTi2f7exgOO0hpMESt89T7AvEfgL7OWyKIF29sQRnD52CKdpUXp/+iit6NhBHNy93ViJwJfF7NmxmcCtx26CuG7lUmxas5wgFGHDqmVYt2IJQVuJzWS4qwiW9q9cOA8L587AooIZ5nUxwV2zgjFtyTwU5k/D1Ikp7EhbcJf53I3LF3GZVn7t4nlcu3QeD27fwPvvvYMnj96jl3kxzzn57mBnl2XLlhli8q+//AJXL55j0nnZPLBaD6S+Idd27Qree3gfHz99go8//ACffvQUHz19H+8wQb18/oxpwKO0mGO0BPX+Q3t2YPeWDdizbSO2MQ/avJaNVbwcW9auoOtZh920DvX6XfyOXNFO6h4Cu5+AHj2wF0cI9lFakyxLYJ1hJzh9/Ajd2R5amqbG7aR17sReurKD3O7cuomvt9DKio2uXELrIQhL5hdg8bw5KJiRi9wpkzA7bxpm5mQhb+okzJ1OUNLHm2c6z8zOwmwm13pus7azc6cil9R/ysRxSE2Mx1qCeObIXuPib7Nz6uHc92hl925b+uidB/iP/7Bm6r8I4Jz1xJ6LFi3ap5N+/vNPcfoEXdvpEzh76jgBOYwDcl3UffT5BwjIIcYCgXRo726+32ViwRY21pYNxdjEnrx29XKsXrYIy9hwyxYWYnHRXMybk4+CmdMxd0YOitizF8zNx9KiOVhUOBsrlxZh7UpawtrV2EBr2LdrO44fPsAOc0WXdJL6gKr46VxocuEuY8zJY0esOETdvmUj1hfTipYtxlKed96s6QasnKwMTJ+WiYy0cUhJGo2JKcmYPIGvE0eaZ0CnJo/GhDEJyBg/FplpqcgiUHo9iTo5LQUTxyWZ7w4d2AeFBPg8rf7SM49y8ewpPLh3G4/ffUiLe/eFAaek2cvx7AmxRUVFB3XSzwjcxXNncIG+/CRd0eH97OF76YJ2W4FaPVuWscv08G3YxiC+ed0auqRlKKYup+tZzF4+b/Z0zCdYs/OzMYs9ezZ1Dt1OkVwSLWAZY4msYgMbe9vGdXRtG40VXT6vBUHQ2N3/daHJh7dvXMdBWuJOWvFmdqD1q5ZjxZKFPM9czJ87C3PzyQazpyB78kRMzUgzAGUSPD2MOz01mToGaWMTuT8FaSlJSCe4Amtc0kgkjhiKsQlkwXGDMIXg79myFvfu3KDbvGoI1zv37xhr++DxoxcKnJJq51JisyL15yQl504fxxG6qT2MG9vowjasWcGAvpyNvBzrVi/FOsaKVbQoPd5+KS1qCS1KljRvtqwqFzPoZvLoltTT859pTlY6CmkFs/iZGnTZonlYs3wJtm/WiPImukZj8JfwDyw0kQVuKF6N9atX0ooXYcmC+ZiZl22uR4BNmpiKDAKWnjoWabQkWdbYhHha4iiMSxhh9iWPijevBdiIuIEYGtsPg/r3RvdOMXzdF5uZpF9jaBCbvHvzBoG7yxj30AD3ny8JOLMG/De/+bVhSTfJmERxL5w5jfNnTuHMyWM4ceQQYxhj2aH9dJd76FKPGms8SkJxmK5T8UdudP+uHdhHC929fTO2b1xvyMJuE7920nJ38Lt7+dsjPO5JY2H3797Wqf/NiYBmQAcGBi7gdU2kJjj+hoUmT5+8jyuXLrLzneJ1HTBr2ORK1TnWrdb67WIDrh7pJKtU51nO7aoVS7lvGTvjErN/NV3uEnqFpQuLTEebN2cGPUYO42K2iaUfPH7PxPwHd+Qq38FHHzx5aRZngPv5p5+YhjVxa8NaWtsqMqwltJAiLOWNFNH1LCqcwxg1k4E+x1iQXOBs3tTMvKnGunKnZGBGdqZlddzOIiEoZHxbyHi2kL9dw0batG41dm3bTJd8Sqc1DzO+evXq50FBQascFigCSjf/Ny00UQHhHhtz2+YNBoAFPGfBrHzk50xBVsYEK5alp5ptKmObdMzo4RibOArJ3CaPHsH3I5A0ahgtLhaj4gdj1LAhGDF0EEbHD6FFDsOqpQtNSvLeg3t4l/r4nXcMWVPB4aUB9xmB20tysJu9aiPJgolbi+abi11QMAtzGKu0pGgOXVDBjDy+zjKL+tQbZ5CNCay8aZMxndvsyWnmdX7OVMa5bCyYP4c9eD7Jix45sYLueD++/vprc7MnT558yutQkj/cYd10iMP6T4S/a6GJ/tZlC2OnXOZidrgCumk9T3kayUp66jiSlDF0kyMI2EiMHh6HpJHxGDlsMMbofXycAWskwUocOQxxdJfDBg8w4A0e0McQLeWOT5gGPH3yyBCTDz94n8C9RIv74he/wNnTJ82javfS5e1hXrR10wa6mDVYv5YkhBa4ntayRmSElrOWWwGxavlSrFxGN0OAlxLoFUsV/+ab12o8uaCtbEg9Bvfg/r04SrfrXCRx9uzZj3gN06iDHZaFKUURWBqp+LsWmije6D720T3rmZLrDONcYh7JO49eY746Ib2AdBbJi7V0mvGQHTKPHS2X6UEOvcfUTHbAqZPNA2imZU1C+vgUTJmUhrXM9c6Tef/844/w4ZMn+PknH71c4D768Cl2MR6tY4BX48+fM9MMHmZnpiMnM4MBPIGUOZU0ORGTx48xVHrSBFHpFIwnvU6T+yEbm8jviXZPJnMTMchnQxTqv3bYeDr2LSazEsUnV1fXObyGOIflAjUiofzS+e+Hf/dCEzHkYjFM3ofc5TQSpKz0CebR/HKRKUwFZFFygfGD+xsyMnzIwG+tiynA4AG9SUr6G5IybFB/DOJ7WaIsVSz2vYf3THz7iBb3UmPcJ+xB27dsMvmUrEQscWZuFqbTzSivydVz9+kuBZb+b2ci6fNkxgvlO+njkhk7xhg3JJqdp57KRFdUXMF90fxC05A7GNuc5aHJkycfdVgERC5PVRwVvWVZqqUKqO8bfdd7ffZXF5rIc4h46P+C9PSf3CmZmMK0QPmc2GTyqOEmjsldJpBJCsj4wQPN+2EGzFgMGdgPg/v3Mm5yyMC+ZJxknskJ2LR+LW4yGX/6/iNa3eOXyyo/+vADK5lds5KJcwEWMOcS6RCd158jybImjbcsSUnqhLFW8ppGC1QOpH16nc7XWSQB2YwpouPKp5YwMV5LNnfpwnlzg7dv3/4Fz63/odHogoaNdD2yIlmZgPlLN6vPdB9/caHJU1rDOnZC3YdJC+g5siZN5LUmI4kAiJAkMQVIIoAjCeBQWpViWhypf/yQAQSqj0kH+vfuZkAbxn2JIjG0uCISHyXhqqB8Tm7wUsnJb3/zGzy8dxd3bl7H9cuXcPniBTb0ORMvTh07imOHDzLn2otDTMwP7be2hw/uw4G9u3H08AEmwrtxnPHr1PGj1CMmZbh4Xr8/gSsXNeP3Ov792crNgoKCcw5rXXmk49s/ENJN/jXQnOIE7wcXmqgxVYFRSnOaKY1yvaOH9zN278BuMufdjLm7Gce3sbMqZdhMK1I8V2FA7FeVmOKVSw1JW0tvoXhdzJx23Zrlhrzt370d504dM7HupQL3MWOcku6N7KXLSCzmmBpfhrGezLRxmDZ5AsaPGYUxpMXjEobTbQwxdDppZBy38Uhgr82gtcmFqjKh+mDBrDxDy9UQyqGcD52uWbOmcrVYh0XzRTTkIhXD/pabdLrNH1xocojJ/WqSpyK6y9m6H7rwSaZKkmwSbiXbpkKSOIIucghd5xBjeSOGxhqXqb8sS9D3eH8pjOP6bTpVLncmU51F8wtMIq5B45cH3EcfMuhuMb1LpSs9o0oFWcWqqWRTmSQmcompdBW6aYGl3Eb/zyaXo4CvCkS6an2Mg/q/N5ESAacEWD1c8uWXX37N82ZRezqsKQ9qdDW+ru1vlb+40EQJ+eoVy1A4ZxZTk2mYTIKSTnevnC1hhP5jToDFGcAElgGOBCU+bhABHECS0o/vBxoARzBVGMPYOIHxfFLaeMNAC0jg7tJdvtQY9+HTD7CZifca3qiYmPIvJdGKXVaJaLgpzibyhhOGD6WlDWOsiDOBfjRzn0S+npDyrP7Hbe6UyaaXF86eaXr90cOmJIqnT5/+ludNd3x7g2KHf+8N6jc/uNDkLhPyFXR5c3k/BbNmYErGRGRMSDGJtojG0NgBiKOFjRw6GAP69TBkJX5ILONZLMEbSOD6kphI+/B9rCEvieykGbS6bN5ffl4OLp0/Q1b54w+k/iBwejzt+48f4eGDe3hw7w7u3b2FG9euMD6dM+Up/YfohXNnGLfO8GLP4tK5s6YgbVTFaVMi03t+rjGrq5dx8/o1U814cP8eLdoaJH/06NG/8bxpjud3gz+40OSXX3zB2HoDV69eMtd1lvHuhMbyGIOPHtpnSnH7dlF378Re6p5d281rs3/3DlPh2bppvRkBURF7y4Z1Zp/ipGK7Yv3J48e+KSa8FOCUx23ixSkoFxXMpsXlIoduMmviePO3lk72OJ5sUg9gm5gq6j8WKWNGm2GTMYwTsr6JTAeUM2UzaS2cLVc5B8sXLzC1Q8knn3zyO543w2FNMFKFRBan3Exu728Vp8V970KTO7dvPXOVMzGdrnJSmkpelquUWxzN+JYkpqjUYJjKWgoBw83fciarokILVH4nBppAj5I2NtnEN5X59K9WK5ctNanBH55N+XgpwMlV7iDDEotaQOBmkkJnZ6YRhBRDOtJTk0yCnUpwkhgXrPg2gjc1zPxXqZLZMXSnGYxvk/kbC7jp1jAOGdn2rRu/mZD06quvZvPcvR3WmKDGBhXj/q+M8rvyFxeanKM3kJs2wDGnzGEulz5hDN3/GHPtYxJ0/VZ8dg7jmKScYI0iWVF+l6B75X2NSxxFgpaOfJX3pmWZx2fp71peOnAiJ3IDIidLFhSa4RoVjrPIELMyxpv63nj9Way2ZJdm3ErAkVWaIZJkVU5GGUapoq4qLqrtFRWwZy5daIq/v382/zAyMrLYYVX7QxzW81RcHJbl/C1WpwYR2Lqf711osp/uTAXnhfPmmlxuCvM45ZciURpzU0cTYCnJI3lPiUgcrtfsmKMEmJJyi3Eqvo8fm2TuSyRNxyiYPcPkpxuYQryI6Xk/CNyvfvmFybdOMf9SfiYfr4LzDlqKRgs0ymzqlWtWmHrlOuY0sk5nnqPKiJJ3jYKL+m9hT9zG+LCXuZKGgpRH/eIXn5sb3LRp0x2ee4zDqpqIDcpi5C7/r1bnBE2M8nsXmvzxq6/M8I5ikWqu64tVyltoUp1FRQWYP3emSXmsIrlVDJ+ZO828VmF61vQcUySfOyPPDAarnimmXVQwE4vZsVVO28L7U1z86gVMz/tB4H7z61+b8THVEq9fuWRIyNnTx3H25HGcOHbIBPNDB/aYeSGHD+7FQTaIpjYoKT9Oxnj8yEFre/QgTjMJP3ZEU+WOm1H1azyeZkPJHUseP378G4dVXNbwTEuHVflXIi0g/hp4TtCc80O/d6HJv/7qVybp12iByJMKAioQaOhK0y5UMNijJHz7Fuo2pkKbn71Xcr7VjJTs27ODsXm3KbprRoAGmY8xidd9nj97CtdJwES+/vznF0dOvBwWcN88k/nTTz4yIJhpADR/Wc7yRZqOoGkAGkzMNuNrqlfO1JQE9lS9nsVeqp44U0M4eVNNXJvPvKmocJbp4bJIy/K2E8xD3zwG98CBAw8c1lxN5V6qVWpEQNPiBYiu84duWO5Uq4oUF53TDFWkHtanT59NOramCqpis3/vLmzeuM54Aw2MFhXONamOrEd1VMU9JeWzeC8aCZiRm23+mVF//p6XnWVSmjy+np49lbEt28Q2xUv9a6PSpk1kmfvYgb/zH6n5Dmvy8I8C3HcnC33zTGYBd+LYYeMiBd6mDcWMTQuMHxczVKzSkIhcjFzJAgZnDZCqiCwWKpdSxNcadF1cVGji2qrl+mfDNWZYR8cVcBphx7NpChEREQsd1rCMxtdkdRq2+Uulr++6SMVFeQ5jbSQ8affu3TO+WHVKnUvzUUTnNQao0W7pfF7rnJn5xk1qesPsmbmYS/Y7K1+PNGbuSlc5V3NmzOfTzGf6t0Z9XqQ5M0sWYTU741bGbD1U+8A+AffN30nnOiyCJKIk96/r/L77+JvFCZyqFbrh7s5nMivP0hNzdLPbt27CRjMGpxtewJhQaAL8AurCwgIztqXi8Tz2PiW3+sMggadG0XbZwgVYyN/o359WGvCKsXPrFt4k3RSPLyIk+eijj37RuHHj8Q7LYkTndbO6PlnU992wEzjNDVV9099hEZzYixcvmhRA+eiRgwfozg+YfGszrUL/Z7p6xXLGtwWm/FWoUfwZ+QZAkYyZ0xXnppl9+gNbJev6bC47qMbw9BebundZ7Krl+n+dNeb/day657cxrkWLFlMdVtyu7XjO/wP+3QmxMueOzmcy//7L39PqPsUnH3/CBv1EFQ48ef99vPfee3jnnXfx4MFDvPvOe3j48B28I33nHTx69z3rNfUhP39w/wGT9/vm9cMHD/DovUcad6MFPOUxP/5GdY6v/2TFhZ///Ocfd+nSJe7Z9ahDKW79EMNUIwhUuXvdQ+0333wzipZmBvnkInk8c74PpE+e4v3HuodHvO6HuHf3HmPSXdzndd67exd379yjWlsew3x+l/vv8R6M8vXtW7c1mmFUn+k47777rrmvJ+8/weNHj78ZjwsODlZ+qk6oYoBySxXBn4s48x71BvXwcOczmfVc4Y+efkjVhNcPSSSe4umTJ3jCGxcA7/FijRLI9wia2b73bN+71r53n+kjAi1QHz8icI8fm+M8/eADo3qulwHyw4++KTp//fXXv2N8inJYrk8uUDf8fcBpn5OUCOT6X3zxxT0naJ9++qlpTHU4AaZza6vrMNf20OpQ9wnKfYJ034B117zW1vna7KfeuXOHoN7hPu0naPfv8zjvmuM9Zps80vbRt9PzQkNDxZT1nE8tTxPZ0rU+F1GPdVJoDYW0dD6T+as/fGWmL/zi88//h37+2ef4jL1Y+nM2jPQz6Tf7tP3MvP78s89Mj3d+9j/V+o62zuPq+3/8yhpYPXjwoOqXAs7JLr+v6OwETmN3VXJycrrrt2q4X37xy2fnsNR5/M8/+/Y6de2ffvKJdc1UeRi9l8oLSBUy9P7jjz9WlYf7uKWXUKf4n8f4dusc1mnfvr1mpSkE6T50jfIOz0UE3Hd7bL3ExMTh5qwvUf785z//O/OvsGfX5BxQ/WsWJxZa61e/+pUpKL9sYYd9j9fTRtfksNy4sxr0XETAORc2ygeLyTVKSEgYwR716H9fzI8tBOxPvOHrQ4cOFYWWe5ELV/z6azFOjSJy4lOyZMkmjLeHfv3rX3/2v4//ouSzzz57NykpSdamtEbW9l12/NxELkg9Wr1WLE4uU8VeVR6UPCop1mJFjVDLZ49zWPMd/17V76U6VqLDmoYX67ASZlXzNXVBwdw5UUgVlB8aVHWySjWKYqF6tpil/rVEpKArdYDDun7NZ3Fe/z96D0513ofWCIpQKZUR/Q9xWKBVc1gd6i91vr9bdDA1jG5e4KmXezssE9coskalBaIaNdph0Vslyf+o6jgiIGrg1g6rACAmqYYXaIq7uuG/NO/ECZwa5W2H1bPlMmWtyp3UCUIc366a1Tmf1/VLdSy1idpGbaT8U22mIoCXwwLtuxOevu8e/m7RwWR1cjkCT42lBlCm7+Ww3KcaUxcT6LBykuel6hyyLjW0ziPAZPXOKXm6YXUqda4fuml9pkYRwPqNLE+dT8dSj9exdQ6d63+f/3mojqu2URvpHlTM+L57+EvVn79bnOA5e68A1EnVW9QQAlIWoDioXvS89LtPbtB5dD6dV+fXdeh6/hJoEn3mBE+dTw2lmK1ymYiNGvDHuHan/tA96Dr+r/fwD4kO7GwEJ4jqKWoMXcCPrTqPc8Krzv+33qzz+p3X/qKvX/pD9/C33Icttthiiy222GKLLbbYYosttthiiy3fK/8P8VLKPzvavEEAAAAASUVORK5CYII=","backgroundImageTag":"default","templateImageTag":"default","backgroundImageWidth":320,"backgroundImageHeight":192,"templateImageWidth":110,"templateImageHeight":192,"data":null},"expireSeconds":300}} \ No newline at end of file diff --git a/search-result.html b/search-result.html new file mode 100644 index 0000000..f10d2f5 --- /dev/null +++ b/search-result.html @@ -0,0 +1,116 @@ + + + + + + 搜索结果 - 代代相传 + + + + + +
                        +
                        +
                        +
                        +
                        Search Result
                        +

                        搜索公开家谱与姓氏资料

                        +

                        + 输入姓氏、地区、堂号或家谱名称,快速找到公开展示的家谱、文章和姓氏百科内容。 +

                        +
                        + +
                        +
                        +
                        + +
                        +
                        + + + + diff --git a/submit-ticket.html b/submit-ticket.html new file mode 100644 index 0000000..a6f08a6 --- /dev/null +++ b/submit-ticket.html @@ -0,0 +1,112 @@ + + + + + + 提交工单 - 代代相传 + + + + + +
                        +
                        +
                        +
                        +
                        Support Ticket
                        +

                        提交问题说明

                        +

                        + 遇到账号、权限、资料导入或家谱展示问题时,可以把情况整理成工单交给客服跟进。 +

                        +
                        +
                        + 24h +

                        工作日内优先处理账号和资料安全相关问题。

                        +
                        +
                        +
                        +
                        +
                        +
                        +

                        工单信息

                        + + + + + +
                        + +
                        +
                        +
                        + + + + + + diff --git a/surname-detail.html b/surname-detail.html new file mode 100644 index 0000000..e91f502 --- /dev/null +++ b/surname-detail.html @@ -0,0 +1,125 @@ + + + + + + 汤姓详情 - 姓氏详情 - 代代相传 + + + + + + +
                        +
                        +
                        +
                        +
                        Surname Detail
                        +

                        汤姓

                        +

                        + 从姓氏源流、郡望堂号、迁徙分布、名人故事和相关家谱出发,认识汤姓族人的文化脉络。 +

                        + +
                        +
                        + +

                        源流有序,字辈相承。

                        +
                        +
                        +
                        + +
                        +
                        +
                        +

                        姓氏源流

                        +

                        + 汤姓是中国传统姓氏之一,常见说法与商汤、封地、官职和族群迁徙有关。姓氏详情页用于集中展示可公开阅读的源流资料,并连接平台内相关公开家谱。 +

                        +
                        +
                        +

                        堂号与分布

                        +

                        + 汤姓族人分布广泛,不同支系可能有不同堂号、字辈和迁徙故事。平台可按地区、堂号和谱名继续关联检索。 +

                        +
                        +
                        +

                        字辈参考

                        +
                        + +
                        +
                        + +
                        +
                        +
                        + + + + + diff --git a/surname.html b/surname.html new file mode 100644 index 0000000..e8ed084 --- /dev/null +++ b/surname.html @@ -0,0 +1,134 @@ + + + + + + 姓氏百科 - 代代相传 + + + + + + +
                        +
                        +
                        +
                        +
                        Surname
                        +

                        从姓氏源流开始认识自己的来处

                        +

                        + 以姓氏为入口,整理姓氏源流、名人故事、地区分布、字辈参考和相关家谱。 +

                        +
                        + 百家姓氏源流家谱关联检索字辈参考线索 +
                        +
                        +
                        +
                        +

                        查询姓氏

                        +
                        + 查询 +
                        +
                        + +
                        +
                        +
                        +
                        + +
                        +
                        +
                        +

                        热门姓氏

                        +

                        姓氏百科可与公开家谱互相连接,帮助用户从姓氏进入家族资料。

                        +
                        + +
                        +
                        +
                        + + + + + diff --git a/tests/album-pages.test.js b/tests/album-pages.test.js new file mode 100644 index 0000000..11d5441 --- /dev/null +++ b/tests/album-pages.test.js @@ -0,0 +1,53 @@ +const assert = require('assert'); +const AlbumPages = require('../public/js/album-pages.js'); + +function run() { + assert.deepStrictEqual(AlbumPages.normalizeList({ rows: [{ albumId: 1 }] }), [{ albumId: 1 }]); + assert.deepStrictEqual(AlbumPages.normalizeList({ records: [{ albumId: 2 }] }), [{ albumId: 2 }]); + + assert.deepStrictEqual(AlbumPages.buildAlbumBody({ + albumName: ' 老照片 ', + albumDesc: ' 家族旧影 ', + coverOssId: '', + sortOrder: '', + status: '' + }), { + albumName: '老照片', + albumDesc: '家族旧影', + coverOssId: undefined, + sortOrder: 1, + status: '0' + }); + + assert.deepStrictEqual(AlbumPages.buildPhotoBody({ + ossId: ' 2060001 ', + photoTitle: ' 老宅 ', + photoDesc: ' 合影 ', + photographer: ' 汤明 ', + shootTime: '2026-07-09', + sortOrder: '', + status: '' + }), { + ossId: 2060001, + photoTitle: '老宅', + photoDesc: '合影', + photographer: '汤明', + shootTime: '2026-07-09', + sortOrder: 1, + status: '0' + }); + + assert.strictEqual( + AlbumPages.buildAlbumRow({ + albumId: 301, + albumName: '老照片', + photoCount: 6, + albumDesc: '家族旧影' + }, '900001001'), + '' + ); + + console.log('album-pages tests passed'); +} + +run(); diff --git a/tests/api-client.test.js b/tests/api-client.test.js new file mode 100644 index 0000000..b7a2ca7 --- /dev/null +++ b/tests/api-client.test.js @@ -0,0 +1,132 @@ +const assert = require('assert'); +const GenealogyApi = require('../public/js/api-client.js'); + +function createStore(initial) { + const values = Object.assign({}, initial); + + return { + getItem(key) { + return Object.prototype.hasOwnProperty.call(values, key) ? values[key] : null; + }, + setItem(key, value) { + values[key] = String(value); + }, + removeItem(key) { + delete values[key]; + }, + values + }; +} + +async function run() { + const calls = []; + const store = createStore({ + 'token-key': 'abc-token' + }); + + assert.strictEqual(GenealogyApi.DEFAULT_BASE_URL, 'http://test-genealogy-api.ddxcjp.cn'); + + const client = GenealogyApi.createClient({ + baseUrl: 'https://test-genealogy-api.ddxcjp.cn', + clientId: 'client-x', + tenantId: 'tenant-x', + tokenKey: 'token-key', + tokenHeaderName: 'Authorization', + tokenStore: store, + fetchImpl: async (url, options) => { + calls.push({ url, options }); + + return { + ok: true, + status: 200, + json: async () => ({ + code: 200, + msg: '操作成功', + data: { token: 'server-token', url } + }) + }; + } + }); + + await client.login({ phone: '13800000000', password: 'md5' }); + assert.strictEqual(calls[0].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/login'); + assert.strictEqual(calls[0].options.headers.clientid, 'client-x'); + assert.strictEqual(calls[0].options.headers.Authorization, undefined); + assert.strictEqual(JSON.parse(calls[0].options.body).tenantId, 'tenant-x'); + assert.strictEqual(store.values['token-key'], 'server-token'); + + await client.getProfile(); + assert.strictEqual(calls[1].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/profile'); + assert.strictEqual(calls[1].options.headers.Authorization, 'Bearer server-token'); + + await client.updateProfile({ + nickName: '张三', + provinceCode: '510000', + cityCode: '510100', + districtCode: '510104' + }); + assert.strictEqual(calls[2].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/profile'); + assert.strictEqual(calls[2].options.method, 'PUT'); + + await client.uploadFile('file-content'); + assert.strictEqual(calls[3].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/files/upload'); + assert.strictEqual(calls[3].options.headers['Content-Type'], undefined); + + await client.initResumableUpload({ + fileName: 'cover.jpg', + fileSize: 9532, + fileMd5: 'md5', + chunkSize: 4194304, + totalChunks: 1 + }); + assert.strictEqual(calls[4].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/files/resumable/init'); + + await client.getRegionChildren('51'); + assert.strictEqual(calls[5].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/region/children?parentCode=51'); + + await client.captchaRequire({ + sceneCode: 'WEB_H5_LOGIN', + subject: '13800000000' + }); + assert.strictEqual( + calls[6].url, + 'https://test-genealogy-api.ddxcjp.cn/captcha/require?tenantId=tenant-x&clientId=client-x&sceneCode=WEB_H5_LOGIN&subject=13800000000' + ); + assert.strictEqual(calls[6].options.headers.Authorization, undefined); + + await client.loginBySms({ + phone: '13800000000', + smsCode: '6666', + validToken: 'sms-ticket' + }); + assert.strictEqual(calls[7].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/login/sms'); + assert.strictEqual(calls[7].options.headers.Authorization, undefined); + assert.strictEqual(JSON.parse(calls[7].options.body).grantType, 'sms'); + assert.strictEqual(JSON.parse(calls[7].options.body).tenantId, 'tenant-x'); + assert.strictEqual(JSON.parse(calls[7].options.body).clientId, 'client-x'); + + await client.sendSmsCode({ + sceneCode: 'WEB_H5_LOGIN', + phone: '13800000000', + validToken: 'captcha-ticket' + }); + assert.strictEqual(calls[8].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/sms/code'); + assert.strictEqual(calls[8].options.headers.Authorization, undefined); + assert.strictEqual(JSON.parse(calls[8].options.body).grantType, 'sms'); + assert.strictEqual(JSON.parse(calls[8].options.body).sceneCode, 'WEB_H5_LOGIN'); + assert.strictEqual(JSON.parse(calls[8].options.body).validToken, 'captcha-ticket'); + + assert.throws( + () => client.listGenealogies(), + (error) => error.code === 'PC_API_NOT_AVAILABLE' + ); + assert.throws( + () => client.publicGenealogies(), + (error) => error.code === 'PC_API_NOT_AVAILABLE' + ); + assert.strictEqual(calls.length, 9); + + console.log('api-client tests passed'); +} + +run(); diff --git a/tests/article-pages.test.js b/tests/article-pages.test.js new file mode 100644 index 0000000..4f873e7 --- /dev/null +++ b/tests/article-pages.test.js @@ -0,0 +1,45 @@ +const assert = require('assert'); +const ArticlePages = require('../public/js/article-pages.js'); + +function run() { + assert.deepStrictEqual(ArticlePages.normalizeList({ rows: [{ articleId: 1 }] }), [{ articleId: 1 }]); + assert.deepStrictEqual(ArticlePages.normalizeList({ records: [{ articleId: 2 }] }), [{ articleId: 2 }]); + + assert.deepStrictEqual(ArticlePages.buildArticleBody({ + categoryId: ' 900040001 ', + articleTitle: ' 族谱源流 ', + articleSummary: ' 本支源流 ', + coverOssId: '', + articleContent: '

                        正文

                        ', + authorName: ' 管理员 ', + sortOrder: '', + status: '' + }), { + categoryId: 900040001, + articleTitle: '族谱源流', + articleSummary: '本支源流', + coverOssId: undefined, + articleContent: '

                        正文

                        ', + authorName: '管理员', + sortOrder: 1, + status: '0' + }); + + assert.strictEqual( + ArticlePages.buildArticleRow({ + articleId: 801, + articleTitle: '族谱源流', + categoryName: '谱序', + status: '0', + updateTime: '2026-07-09' + }, '900001001'), + '

                        族谱源流

                        谱序 · 已发布 · 2026-07-09

                        查看
                        ' + ); + + assert.strictEqual(ArticlePages.formatStatus('0'), '已发布'); + assert.strictEqual(ArticlePages.formatStatus('1'), '草稿'); + + console.log('article-pages tests passed'); +} + +run(); diff --git a/tests/auth-message.test.js b/tests/auth-message.test.js new file mode 100644 index 0000000..554b48e --- /dev/null +++ b/tests/auth-message.test.js @@ -0,0 +1,34 @@ +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +function readProjectFile(fileName) { + return fs.readFileSync(path.join(__dirname, '..', fileName), 'utf8'); +} + +function run() { + [ + 'public/js/auth-pages.js', + 'public/js/captcha-pages.js' + ].forEach((fileName) => { + const content = readProjectFile(fileName); + + assert.strictEqual( + content.includes('root.alert'), + false, + `${fileName} 不应再使用浏览器原生 alert 作为提示` + ); + }); + + const publicCss = readProjectFile('public/css/public.css'); + + assert.strictEqual( + publicCss.includes('.auth-layer-message'), + true, + 'public.css 应提供认证页 layui 提示皮肤' + ); + + console.log('auth-message tests passed'); +} + +run(); diff --git a/tests/auth-page-structure.test.js b/tests/auth-page-structure.test.js new file mode 100644 index 0000000..16aeb10 --- /dev/null +++ b/tests/auth-page-structure.test.js @@ -0,0 +1,35 @@ +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const pages = [ + 'login.html', + 'register.html', + 'forgot-password.html' +]; + +function readPage(fileName) { + return fs.readFileSync(path.join(__dirname, '..', fileName), 'utf8'); +} + +function run() { + pages.forEach((fileName) => { + const html = readPage(fileName); + const formCaptchaBox = //i.test(html); + const pageModalBox = /class="[^"]*\bauth-captcha-modal\b[^"]*"[^>]*data-captcha-box/.test(html) + || /data-captcha-box[^>]*class="[^"]*\bauth-captcha-modal\b[^"]*"/.test(html); + const hasLayuiCss = html.includes('href="public/layui/css/layui.css"'); + const layuiScriptIndex = html.indexOf('src="public/layui/layui.js"'); + const authScriptIndex = html.indexOf('src="public/js/auth-pages.js"'); + + assert.strictEqual(formCaptchaBox, false, `${fileName} 不应把 TAC 挂载点放在表单内部`); + assert.strictEqual(pageModalBox, true, `${fileName} 应提供页面级 TAC 弹窗挂载点`); + assert.strictEqual(hasLayuiCss, true, `${fileName} 应加载 layui.css 作为 layer 提示样式依赖`); + assert.notStrictEqual(layuiScriptIndex, -1, `${fileName} 应加载 layui.js 使用 layer.msg`); + assert.ok(layuiScriptIndex < authScriptIndex, `${fileName} 应在 auth-pages.js 之前加载 layui.js`); + }); + + console.log('auth-page-structure tests passed'); +} + +run(); diff --git a/tests/auth-pages.test.js b/tests/auth-pages.test.js new file mode 100644 index 0000000..b1c2a39 --- /dev/null +++ b/tests/auth-pages.test.js @@ -0,0 +1,65 @@ +const assert = require('assert'); +const AuthPages = require('../public/js/auth-pages.js'); + +function fakeHash(value) { + return `hashed:${value}`; +} + +function run() { + assert.deepStrictEqual(AuthPages.buildLoginBody({ + phone: '13800000000', + password: '123456', + validToken: 'ticket' + }, fakeHash), { + phone: '13800000000', + password: 'hashed:123456', + validToken: 'ticket' + }); + + assert.deepStrictEqual(AuthPages.buildSmsLoginBody({ + phone: '13800000000', + smsCode: '6666', + validToken: 'ticket' + }), { + phone: '13800000000', + smsCode: '6666', + validToken: 'ticket' + }); + + assert.deepStrictEqual(AuthPages.buildRegisterBody({ + phone: '13800000000', + password: '123456', + nickName: '汤小明', + validToken: 'ticket' + }, fakeHash), { + phone: '13800000000', + password: 'hashed:123456', + nickName: '汤小明', + validToken: 'ticket' + }); + + assert.deepStrictEqual(AuthPages.buildPasswordResetBody({ + phone: '13800000000', + smsCode: '8888', + newPassword: 'abcdef', + validToken: 'ticket' + }, fakeHash), { + phone: '13800000000', + smsCode: '8888', + newPassword: 'hashed:abcdef', + validToken: 'ticket' + }); + + assert.strictEqual(AuthPages.getCaptchaScene('login'), 'WEB_H5_LOGIN'); + assert.strictEqual(AuthPages.getCaptchaScene('register'), 'WEB_H5_REGISTER'); + assert.strictEqual(AuthPages.getCaptchaScene('password-reset'), 'WEB_H5_FORGOT_PASSWORD'); + assert.strictEqual(AuthPages.getFormCaptchaScene({ + getAttribute(name) { + return name === 'data-captcha-scene' ? 'WEB_H5_REGISTER' : ''; + } + }, 'login'), 'WEB_H5_REGISTER'); + + console.log('auth-pages tests passed'); +} + +run(); diff --git a/tests/captcha-pages.test.js b/tests/captcha-pages.test.js new file mode 100644 index 0000000..67a64ed --- /dev/null +++ b/tests/captcha-pages.test.js @@ -0,0 +1,105 @@ +const assert = require('assert'); +const CaptchaPages = require('../public/js/captcha-pages.js'); + +const fakeApi = { + clientId: 'client-x', + tenantId: 'tenant-x' +}; + +function run() { + assert.deepStrictEqual(CaptchaPages.buildChallengeBody({ + sceneCode: 'WEB_H5_LOGIN', + subject: '13800000000' + }, fakeApi), { + tenantId: 'tenant-x', + clientId: 'client-x', + sceneCode: 'WEB_H5_LOGIN', + subject: '13800000000' + }); + + const challenge = CaptchaPages.adaptChallengeResponse({ + code: 200, + data: { + required: true, + providerCode: 'tianai', + captchaType: 'SLIDER', + challengeId: 'challenge-1', + payload: { + id: 'tianai-1', + backgroundImage: 'bg.png', + templateImage: 'tpl.png', + backgroundImageWidth: 340 + } + } + }); + + assert.strictEqual(challenge.code, 200); + assert.strictEqual(challenge.data.type, 'SLIDER'); + assert.strictEqual(challenge.data.id, 'tianai-1'); + assert.strictEqual(challenge.data.challengeId, 'challenge-1'); + assert.strictEqual(challenge.data.backgroundImage, 'bg.png'); + assert.strictEqual(challenge.data.templateImage, 'tpl.png'); + + assert.deepStrictEqual(CaptchaPages.buildVerifyBody({ + id: 'tianai-1', + data: { + trackList: [{ x: 10, y: 2 }] + } + }, { + sceneCode: 'WEB_H5_LOGIN', + subject: '13800000000', + providerCode: 'tianai', + captchaType: 'SLIDER', + challengeId: 'challenge-1' + }, fakeApi), { + tenantId: 'tenant-x', + clientId: 'client-x', + sceneCode: 'WEB_H5_LOGIN', + subject: '13800000000', + challengeId: 'challenge-1', + providerCode: 'tianai', + captchaType: 'SLIDER', + payload: { + id: 'tianai-1', + data: { + trackList: [{ x: 10, y: 2 }] + } + } + }); + + assert.strictEqual(CaptchaPages.extractValidToken({ + code: 200, + data: { + passed: true, + validToken: 'captcha-ticket' + } + }), 'captcha-ticket'); + assert.strictEqual(CaptchaPages.shouldRequireCaptcha({ + required: false + }), false); + assert.strictEqual(CaptchaPages.shouldRequireCaptcha({ + required: true + }), true); + + const modalBox = { nodeName: 'modal' }; + const formBox = { nodeName: 'form-box' }; + const originalDocument = global.document; + + global.document = { + querySelector(selector) { + return selector === '.auth-captcha-modal[data-captcha-box]' ? modalBox : null; + } + }; + + assert.strictEqual(CaptchaPages.getCaptchaBox({ + querySelector(selector) { + return selector === '[data-captcha-box]' ? formBox : null; + } + }), modalBox); + + global.document = originalDocument; + + console.log('captcha-pages tests passed'); +} + +run(); diff --git a/tests/ceremony-pages.test.js b/tests/ceremony-pages.test.js new file mode 100644 index 0000000..367f738 --- /dev/null +++ b/tests/ceremony-pages.test.js @@ -0,0 +1,62 @@ +const assert = require('assert'); +const CeremonyPages = require('../public/js/ceremony-pages.js'); + +function run() { + assert.deepStrictEqual(CeremonyPages.normalizeList({ rows: [{ ceremonyId: 1 }] }), [{ ceremonyId: 1 }]); + assert.deepStrictEqual(CeremonyPages.normalizeList({ records: [{ ceremonyId: 2 }] }), [{ ceremonyId: 2 }]); + + assert.deepStrictEqual(CeremonyPages.buildCeremonyBody({ + ceremonyType: ' wedding ', + ceremonyTitle: ' 婚礼邀请 ', + ceremonyDesc: ' 邀请亲友 ', + ceremonyTime: '2026-07-09 10:00:00', + location: ' 祠堂 ', + coverOssId: '', + sortOrder: '', + status: '' + }), { + ceremonyType: 'wedding', + ceremonyTitle: '婚礼邀请', + ceremonyDesc: '邀请亲友', + ceremonyTime: '2026-07-09 10:00:00', + location: '祠堂', + coverOssId: undefined, + sortOrder: 1, + status: '0' + }); + + assert.deepStrictEqual(CeremonyPages.buildMeritBody({ + donorName: ' 汤明 ', + meritType: '', + meritTitle: ' 修谱捐款 ', + meritContent: ' 支持修谱 ', + amount: ' 100 ', + meritTime: '', + sortOrder: '', + status: '' + }), { + donorName: '汤明', + meritType: 'donation', + meritTitle: '修谱捐款', + meritContent: '支持修谱', + amount: 100, + meritTime: undefined, + sortOrder: 1, + status: '0' + }); + + assert.strictEqual( + CeremonyPages.buildCeremonyRow({ + ceremonyId: 601, + ceremonyTitle: '婚礼邀请', + ceremonyType: 'wedding', + ceremonyTime: '2026-07-09', + giftCount: 2 + }, '900001001'), + '' + ); + + console.log('ceremony-pages tests passed'); +} + +run(); diff --git a/tests/config.test.js b/tests/config.test.js new file mode 100644 index 0000000..32719a3 --- /dev/null +++ b/tests/config.test.js @@ -0,0 +1,26 @@ +const assert = require('assert'); +const configFactory = require('../config.js'); + +const store = {}; +const config = configFactory({ + localStorage: { + getItem: (key) => Object.prototype.hasOwnProperty.call(store, key) ? store[key] : null, + setItem: (key, value) => { + store[key] = String(value); + } + } +}); + +assert.strictEqual(config.getApiBaseUrl(), 'http://test-genealogy-api.ddxcjp.cn'); +assert.strictEqual(config.getClientId(), 'ced7e5f0498645c6ec642dcf450b036f'); +assert.strictEqual(config.getTenantId(), '000000'); +assert.strictEqual(config.getTokenKey(), 'genealogy_auth_token'); +assert.strictEqual(config.getConfig().tokenHeaderName, 'Authorization'); + +store.genealogy_api_base_url = 'https://custom.example.test'; +assert.strictEqual(config.getApiBaseUrl(), 'https://custom.example.test'); + +store.genealogy_api_base_url = 'https://test-genealogy-api.ddxcjp.cn'; +assert.strictEqual(config.getApiBaseUrl(), 'http://test-genealogy-api.ddxcjp.cn'); + +console.log('config tests passed'); diff --git a/tests/feed-pages.test.js b/tests/feed-pages.test.js new file mode 100644 index 0000000..68c2b02 --- /dev/null +++ b/tests/feed-pages.test.js @@ -0,0 +1,45 @@ +const assert = require('assert'); +const FeedPages = require('../public/js/feed-pages.js'); + +function run() { + assert.deepStrictEqual(FeedPages.normalizeList({ rows: [{ feedId: 1 }] }), [{ feedId: 1 }]); + assert.deepStrictEqual(FeedPages.normalizeList({ records: [{ feedId: 2 }] }), [{ feedId: 2 }]); + + assert.deepStrictEqual(FeedPages.buildFeedBody({ + content: ' 家族聚会 ', + mediaOssIds: ' 1,2 ', + visibility: '', + status: '' + }), { + content: '家族聚会', + mediaOssIds: '1,2', + visibility: '2', + status: '0' + }); + + assert.deepStrictEqual(FeedPages.buildCommentBody({ + parentCommentId: '', + replyUserId: ' 9001 ', + content: ' 写得好 ' + }), { + parentCommentId: undefined, + replyUserId: 9001, + content: '写得好' + }); + + assert.strictEqual( + FeedPages.buildFeedRow({ + feedId: 901, + content: '家族聚会', + authorName: '汤明', + likeCount: 3, + commentCount: 2, + createTime: '2026-07-09' + }), + '

                        汤明

                        家族聚会

                        2026-07-09 · 3 赞 · 2 评论
                        ' + ); + + console.log('feed-pages tests passed'); +} + +run(); diff --git a/tests/feedback-pages.test.js b/tests/feedback-pages.test.js new file mode 100644 index 0000000..f896789 --- /dev/null +++ b/tests/feedback-pages.test.js @@ -0,0 +1,33 @@ +const assert = require('assert'); +const FeedbackPages = require('../public/js/feedback-pages.js'); + +function run() { + assert.deepStrictEqual(FeedbackPages.buildFeedbackBody({ + feedbackType: 'bug', + feedbackTitle: '登录异常', + feedbackContent: '页面打不开', + contactInfo: '13800000000' + }), { + feedbackType: 'bug', + feedbackContent: '【登录异常】页面打不开', + contactInfo: '13800000000' + }); + + assert.deepStrictEqual(FeedbackPages.normalizeList({ + rows: [{ id: 1 }] + }), [{ id: 1 }]); + + assert.strictEqual( + FeedbackPages.buildFeedbackRow({ + feedbackType: 'bug', + feedbackContent: '页面打不开', + status: '已受理', + createTime: '2026-07-09' + }), + '

                        bug

                        已受理 · 2026-07-09 · 页面打不开

                        查看
                        ' + ); + + console.log('feedback-pages tests passed'); +} + +run(); diff --git a/tests/genealogy-pages.test.js b/tests/genealogy-pages.test.js new file mode 100644 index 0000000..ad5d3e4 --- /dev/null +++ b/tests/genealogy-pages.test.js @@ -0,0 +1,70 @@ +const assert = require('assert'); +const GenealogyPages = require('../public/js/genealogy-pages.js'); + +function run() { + assert.deepStrictEqual(GenealogyPages.buildGenealogyBody({ + genealogyName: '四川武胜汤氏族谱', + surname: '汤', + regionCode: '', + addressDetail: '四川武胜', + intro: '家族简介' + }), { + genealogyName: '四川武胜汤氏族谱', + surname: '汤', + regionCode: '', + addressDetail: '四川武胜', + intro: '家族简介', + visibility: '1', + joinMode: '1' + }); + + assert.strictEqual(GenealogyPages.buildGenealogyBody({ + genealogyName: '成都陈氏族谱', + surname: '陈', + regionCode: '510104' + }).regionCode, '510104'); + + assert.strictEqual(GenealogyPages.pick({ + genealogyName: '汤氏族谱', + name: '备用名称' + }, ['genealogyName', 'name'], '未命名'), '汤氏族谱'); + + assert.deepStrictEqual(GenealogyPages.normalizeList({ rows: [{ id: 1 }] }), [{ id: 1 }]); + assert.deepStrictEqual(GenealogyPages.normalizeList([{ id: 2 }]), [{ id: 2 }]); + assert.strictEqual(GenealogyPages.getQueryParam('?id=900001001&from=plaza', 'id'), '900001001'); + assert.strictEqual(GenealogyPages.getQueryParam('?genealogyId=42', 'id'), ''); + assert.deepStrictEqual(GenealogyPages.buildJoinApplyBody({ + applicantName: ' 汤小明 ', + phone: ' 13800000000 ', + relationDesc: ' 本族成员 ', + applyReason: '' + }), { + applicantName: '汤小明', + phone: '13800000000', + relationDesc: '本族成员', + applyReason: '申请加入家谱', + inviterUserId: undefined + }); + assert.strictEqual( + GenealogyPages.buildFamilyHeroTitle({ + genealogyName: '汤氏族谱' + }), + '汤氏族谱' + ); + assert.strictEqual(GenealogyPages.formatJoinMode('0'), '关闭加入'); + assert.strictEqual(GenealogyPages.formatJoinMode('1'), '审核加入'); + assert.strictEqual(GenealogyPages.formatJoinMode('2'), '邀请码加入'); + assert.strictEqual( + GenealogyPages.buildMyGenealogyRow({ + genealogyId: 9, + genealogyName: '汤氏族谱', + roleName: '创建者', + memberCount: 18 + }), + '

                        汤氏族谱

                        创建者 · 共修入 18 人

                        进入主页
                        ' + ); + + console.log('genealogy-pages tests passed'); +} + +run(); diff --git a/tests/generation-pages.test.js b/tests/generation-pages.test.js new file mode 100644 index 0000000..aa5a399 --- /dev/null +++ b/tests/generation-pages.test.js @@ -0,0 +1,52 @@ +const assert = require('assert'); +const GenerationPages = require('../public/js/generation-pages.js'); + +function run() { + assert.deepStrictEqual(GenerationPages.normalizeList({ rows: [{ id: 1 }] }), [{ id: 1 }]); + assert.deepStrictEqual(GenerationPages.normalizeList({ records: [{ id: 2 }] }), [{ id: 2 }]); + + assert.deepStrictEqual(GenerationPages.buildGenerationBody({ + generationNo: ' 5 ', + generationText: ' 忠 ', + description: ' 第五代 ', + sortOrder: '', + status: '' + }), { + generationNo: 5, + generationText: '忠', + description: '第五代', + sortOrder: 5, + status: '0' + }); + + assert.strictEqual( + GenerationPages.buildGenerationRow({ + poemId: 77, + generationNo: 5, + generationText: '忠', + memberCount: 8, + description: '第五代' + }, '900001001'), + '

                        第 5 代:忠

                        8 人 · 第五代

                        ' + ); + + assert.deepStrictEqual(GenerationPages.buildGenerationBatchBody({ + content: ' 德承家亦 ', + stopMissingOldGeneration: 'on' + }), { + content: '德承家亦', + stopMissingOldGeneration: true + }); + + assert.deepStrictEqual(GenerationPages.buildGenerationBatchBody({ + content: '', + stopMissingOldGeneration: '' + }), { + content: '', + stopMissingOldGeneration: false + }); + + console.log('generation-pages tests passed'); +} + +run(); diff --git a/tests/growth-pages.test.js b/tests/growth-pages.test.js new file mode 100644 index 0000000..b249ce7 --- /dev/null +++ b/tests/growth-pages.test.js @@ -0,0 +1,43 @@ +const assert = require('assert'); +const GrowthPages = require('../public/js/growth-pages.js'); + +function run() { + assert.deepStrictEqual(GrowthPages.normalizeList({ rows: [{ recordId: 1 }] }), [{ recordId: 1 }]); + assert.deepStrictEqual(GrowthPages.normalizeList({ records: [{ recordId: 2 }] }), [{ recordId: 2 }]); + + assert.deepStrictEqual(GrowthPages.buildGrowthBody({ + lineagePersonId: ' 900020001 ', + recordType: '', + recordTitle: ' 出生记录 ', + recordContent: ' 成长内容 ', + recordDate: '2026-07-09', + remindTime: '', + mediaOssIds: ' 2060001 ', + sortOrder: '', + status: '' + }), { + lineagePersonId: 900020001, + recordType: 'growth', + recordTitle: '出生记录', + recordContent: '成长内容', + recordDate: '2026-07-09', + remindTime: undefined, + mediaOssIds: '2060001', + sortOrder: 1, + status: '0' + }); + + assert.strictEqual( + GrowthPages.buildGrowthRow({ + recordId: 701, + recordTitle: '出生记录', + recordType: 'birth', + recordDate: '2026-07-09' + }, '900001001'), + '

                        出生记录

                        birth · 2026-07-09

                        编辑
                        ' + ); + + console.log('growth-pages tests passed'); +} + +run(); diff --git a/tests/help-pages.test.js b/tests/help-pages.test.js new file mode 100644 index 0000000..e72f69d --- /dev/null +++ b/tests/help-pages.test.js @@ -0,0 +1,29 @@ +const assert = require('assert'); +const HelpPages = require('../public/js/help-pages.js'); + +function run() { + assert.deepStrictEqual(HelpPages.normalizeList({ rows: [{ id: 1 }] }), [{ id: 1 }]); + assert.deepStrictEqual(HelpPages.normalizeList({ records: [{ id: 2 }] }), [{ id: 2 }]); + + assert.strictEqual( + HelpPages.buildHelpItem({ + helpId: 88, + title: '如何创建家谱?', + summary: '填写名称、姓氏、地区即可创建。' + }, 0), + '
                        如何创建家谱?

                        填写名称、姓氏、地区即可创建。

                        ' + ); + + assert.strictEqual( + HelpPages.buildHelpItem({ + id: 89, + articleTitle: '资料填错了怎么办?', + content: '可以编辑成员资料' + }, 1), + '
                        资料填错了怎么办?

                        <b>可以编辑成员资料</b>

                        ' + ); + + console.log('help-pages tests passed'); +} + +run(); diff --git a/tests/join-apply-pages.test.js b/tests/join-apply-pages.test.js new file mode 100644 index 0000000..a25ead3 --- /dev/null +++ b/tests/join-apply-pages.test.js @@ -0,0 +1,44 @@ +const assert = require('assert'); +const JoinApplyPages = require('../public/js/join-apply-pages.js'); + +function run() { + assert.deepStrictEqual(JoinApplyPages.normalizeList({ rows: [{ id: 1 }] }), [{ id: 1 }]); + assert.deepStrictEqual(JoinApplyPages.normalizeList({ records: [{ id: 2 }] }), [{ id: 2 }]); + + assert.strictEqual(JoinApplyPages.formatApplyStatus('0'), '待审核'); + assert.strictEqual(JoinApplyPages.formatApplyStatus('1'), '已通过'); + assert.strictEqual(JoinApplyPages.formatApplyStatus('2'), '已拒绝'); + + assert.deepStrictEqual(JoinApplyPages.buildAuditBody('approve', ''), { + status: '1', + auditRemark: '信息核验通过' + }); + assert.deepStrictEqual(JoinApplyPages.buildAuditBody('reject', '资料不完整'), { + status: '2', + auditRemark: '资料不完整' + }); + + assert.strictEqual( + JoinApplyPages.buildPendingRow({ + applyId: 66, + applicantName: '汤小明', + relationDesc: '本族成员', + createTime: '2026-07-09' + }, '900001001'), + '

                        汤小明申请加入家谱

                        2026-07-09 · 本族成员

                        ' + ); + + assert.strictEqual( + JoinApplyPages.buildMineRow({ + applyId: 67, + genealogyName: '汤氏族谱', + status: '0', + relationDesc: '本族成员' + }), + '

                        汤氏族谱

                        待审核 · 本族成员

                        ' + ); + + console.log('join-apply-pages tests passed'); +} + +run(); diff --git a/tests/lineage-pages.test.js b/tests/lineage-pages.test.js new file mode 100644 index 0000000..2cee4ef --- /dev/null +++ b/tests/lineage-pages.test.js @@ -0,0 +1,77 @@ +const assert = require('assert'); +const LineagePages = require('../public/js/lineage-pages.js'); + +function run() { + assert.deepStrictEqual(LineagePages.normalizeList({ rows: [{ personId: 1 }] }), [{ personId: 1 }]); + assert.deepStrictEqual(LineagePages.normalizeList({ records: [{ personId: 2 }] }), [{ personId: 2 }]); + + assert.deepStrictEqual(LineagePages.buildLineagePersonBody({ + appUserId: '', + personNo: ' P001 ', + personName: ' 汤明 ', + aliasName: ' 明公 ', + sex: '0', + generationNo: ' 3 ', + generationName: '忠', + birthDate: '1990-01-02', + deathDate: '', + introduction: ' 三世成员 ' + }), { + appUserId: undefined, + personNo: 'P001', + personName: '汤明', + aliasName: '明公', + sex: '0', + generationNo: 3, + generationName: '忠', + avatarOssId: undefined, + birthDate: '1990-01-02', + deathDate: undefined, + introduction: '三世成员' + }); + + assert.strictEqual(LineagePages.formatSex('0'), '男'); + assert.strictEqual(LineagePages.formatSex('1'), '女'); + assert.strictEqual(LineagePages.formatSex('9'), '未知'); + + assert.strictEqual( + LineagePages.buildPersonRow({ + personId: 7001, + personName: '汤明', + generationNo: 3, + generationName: '忠', + sex: '0', + boundAccount: true + }), + '' + ); + + assert.strictEqual( + LineagePages.buildTreeNode({ + personId: 7001, + personName: '汤明', + generationNo: 3, + children: [ + { + personId: 7002, + personName: '汤小明', + generationNo: 4 + } + ] + }), + '
                      • ' + ); + + assert.strictEqual( + LineagePages.buildHomeSummary({ + genealogyNo: 'JP001', + hallName: '敦本堂', + memberCount: 18 + }, 3), + 'JP001 · 堂号 敦本堂 · 共 18 人 · 3 个世系根节点' + ); + + console.log('lineage-pages tests passed'); +} + +run(); diff --git a/tests/member-admin-pages.test.js b/tests/member-admin-pages.test.js new file mode 100644 index 0000000..f937a57 --- /dev/null +++ b/tests/member-admin-pages.test.js @@ -0,0 +1,49 @@ +const assert = require('assert'); +const MemberAdminPages = require('../public/js/member-admin-pages.js'); + +function run() { + assert.deepStrictEqual(MemberAdminPages.normalizeList({ rows: [{ memberId: 1 }] }), [{ memberId: 1 }]); + assert.deepStrictEqual(MemberAdminPages.normalizeList({ records: [{ memberId: 2 }] }), [{ memberId: 2 }]); + + assert.deepStrictEqual(MemberAdminPages.buildMemberUpdateBody({ + memberName: ' 汤明 ', + relationName: ' 族亲 ', + roleType: ' admin ', + lineagePersonId: ' 90002 ' + }), { + memberName: '汤明', + relationName: '族亲', + roleType: 'admin', + lineagePersonId: 90002 + }); + + assert.strictEqual( + MemberAdminPages.buildOverviewSummary({ + genealogyName: '汤氏族谱', + memberCount: 18, + articleCount: 3, + albumCount: 2 + }), + '汤氏族谱 · 18 位成员 · 3 篇谱文 · 2 个相册' + ); + + assert.strictEqual( + MemberAdminPages.buildMemberRow({ + memberId: 501, + memberName: '汤明', + relationName: '族亲', + roleType: 'admin', + lineagePersonName: '忠字辈' + }), + '

                        汤明

                        族亲 · 管理员 · 绑定:忠字辈

                        ' + ); + + assert.strictEqual( + MemberAdminPages.buildInviteLink('https://example.test/profile-invite.html?id=900001001', '900001001'), + 'https://example.test/join-genealogy.html?id=900001001' + ); + + console.log('member-admin-pages tests passed'); +} + +run(); diff --git a/tests/memo-pages.test.js b/tests/memo-pages.test.js new file mode 100644 index 0000000..a063ea3 --- /dev/null +++ b/tests/memo-pages.test.js @@ -0,0 +1,39 @@ +const assert = require('assert'); +const MemoPages = require('../public/js/memo-pages.js'); + +function run() { + assert.deepStrictEqual(MemoPages.normalizeList({ rows: [{ memoId: 1 }] }), [{ memoId: 1 }]); + assert.deepStrictEqual(MemoPages.normalizeList({ records: [{ memoId: 2 }] }), [{ memoId: 2 }]); + + assert.deepStrictEqual(MemoPages.buildMemoBody({ + memoTitle: ' 修谱事项 ', + memoContent: ' 联系族亲 ', + remindTime: '', + completed: '', + mediaOssIds: ' 2060001 ', + sortOrder: '', + status: '' + }), { + memoTitle: '修谱事项', + memoContent: '联系族亲', + remindTime: undefined, + completed: '0', + mediaOssIds: '2060001', + sortOrder: 1, + status: '0' + }); + + assert.strictEqual( + MemoPages.buildMemoRow({ + memoId: 801, + memoTitle: '修谱事项', + completed: '0', + remindTime: '2026-08-01' + }, '900001001'), + '

                        修谱事项

                        未完成 · 2026-08-01

                        编辑
                        ' + ); + + console.log('memo-pages tests passed'); +} + +run(); diff --git a/tests/notification-pages.test.js b/tests/notification-pages.test.js new file mode 100644 index 0000000..09eccb4 --- /dev/null +++ b/tests/notification-pages.test.js @@ -0,0 +1,32 @@ +const assert = require('assert'); +const NotificationPages = require('../public/js/notification-pages.js'); + +function run() { + assert.strictEqual(NotificationPages.formatNotificationStatus({ + readStatus: '0' + }), '未读'); + assert.strictEqual(NotificationPages.formatNotificationStatus({ + readStatus: '1' + }), '已读'); + assert.strictEqual(NotificationPages.formatNotificationStatus({ + readFlag: true + }), '已读'); + + assert.strictEqual( + NotificationPages.buildNotificationRow({ + notificationId: 77, + title: '族人申请加入汤氏族谱', + content: '需要管理员审核', + createTime: '2026-07-09 12:00:00', + readStatus: '0' + }), + '

                        族人申请加入汤氏族谱

                        2026-07-09 12:00:00 · 需要管理员审核

                        ' + ); + + assert.deepStrictEqual(NotificationPages.normalizeList({ rows: [{ id: 1 }] }), [{ id: 1 }]); + assert.deepStrictEqual(NotificationPages.normalizeList({ records: [{ id: 2 }] }), [{ id: 2 }]); + + console.log('notification-pages tests passed'); +} + +run(); diff --git a/tests/profile-pages.test.js b/tests/profile-pages.test.js new file mode 100644 index 0000000..da50013 --- /dev/null +++ b/tests/profile-pages.test.js @@ -0,0 +1,67 @@ +const assert = require('assert'); +const ProfilePages = require('../public/js/profile-pages.js'); + +function run() { + assert.strictEqual(ProfilePages.pick({ + nickName: '汤小明', + userName: '备用' + }, ['nickName', 'userName'], '未设置'), '汤小明'); + + assert.strictEqual(ProfilePages.getAvatarText({ + nickName: '汤小明' + }), '汤'); + + assert.deepStrictEqual(ProfilePages.buildProfileView({ + nickName: '汤小明', + phone: '13800000000', + sex: '1', + birthday: '2026-07-09', + provinceCode: '510000', + cityCode: '510100', + districtCode: '510104' + }), { + displayName: '汤小明', + avatarText: '汤', + phone: '13800000000', + sex: '1', + birthday: '2026-07-09', + regionText: '510000 / 510100 / 510104' + }); + + assert.deepStrictEqual(ProfilePages.buildProfileUpdateBody({ + nickName: ' 汤小明 ', + avatarOssId: '2060001', + sex: '1', + birthday: '2026-07-09', + provinceCode: '510000', + cityCode: '510100', + districtCode: '510104' + }), { + nickName: '汤小明', + avatarOssId: 2060001, + sex: '1', + birthday: '2026-07-09', + provinceCode: '510000', + cityCode: '510100', + districtCode: '510104' + }); + + assert.deepStrictEqual(ProfilePages.buildProfileUpdateBody({ + nickName: ' ', + avatarOssId: '', + sex: '', + birthday: '' + }), { + nickName: undefined, + avatarOssId: undefined, + sex: undefined, + birthday: undefined, + provinceCode: undefined, + cityCode: undefined, + districtCode: undefined + }); + + console.log('profile-pages tests passed'); +} + +run(); diff --git a/tests/promotion-pages.test.js b/tests/promotion-pages.test.js new file mode 100644 index 0000000..962935d --- /dev/null +++ b/tests/promotion-pages.test.js @@ -0,0 +1,53 @@ +const assert = require('assert'); +const PromotionPages = require('../public/js/promotion-pages.js'); + +function run() { + const promotions = [ + { + promotionId: 11, + promotionTitle: '平台公告', + promotionContent: '

                        公开展示规范

                        ', + summary: '保护家族资料安全', + coverUrl: 'cover.jpg', + linkUrl: 'download.html', + publishTime: '2026-07-09' + } + ]; + + assert.deepStrictEqual(PromotionPages.normalizeList({ rows: promotions }), promotions); + assert.deepStrictEqual(PromotionPages.normalizeList({ records: promotions }), promotions); + + assert.strictEqual( + PromotionPages.buildPromotionCard(promotions[0]), + '平台公告

                        平台公告

                        保护家族资料安全

                        2026-07-09
                        ' + ); + + assert.strictEqual( + PromotionPages.buildDownloadCard({ + promotionId: 12, + title: 'Android 安装包', + description: '适用于安卓手机', + linkUrl: 'https://example.test/app.apk' + }), + '

                        Android 安装包

                        适用于安卓手机

                        ' + ); + + assert.deepStrictEqual( + PromotionPages.pickPromotion(promotions, '11'), + promotions[0] + ); + + assert.deepStrictEqual( + PromotionPages.buildNoticeDetail(promotions[0]), + { + title: '平台公告', + summary: '保护家族资料安全', + content: '

                        公开展示规范

                        ', + time: '2026-07-09' + } + ); + + console.log('promotion-pages tests passed'); +} + +run(); diff --git a/tests/region-pages.test.js b/tests/region-pages.test.js new file mode 100644 index 0000000..42080ac --- /dev/null +++ b/tests/region-pages.test.js @@ -0,0 +1,50 @@ +const assert = require('assert'); +const RegionPages = require('../public/js/region-pages.js'); + +function run() { + assert.deepStrictEqual(RegionPages.normalizeList({ rows: [{ regionCode: '51' }] }), [{ regionCode: '51' }]); + assert.deepStrictEqual(RegionPages.normalizeList({ records: [{ regionCode: '510100' }] }), [{ regionCode: '510100' }]); + + assert.strictEqual( + RegionPages.buildRegionOption({ + regionCode: '510000', + regionName: '四川省' + }), + '' + ); + + assert.strictEqual( + RegionPages.buildPathText([ + { regionName: '四川省' }, + { regionName: '成都市' }, + { name: '锦江区' } + ]), + '四川省 / 成都市 / 锦江区' + ); + + assert.strictEqual(RegionPages.getFinalRegionCode({ + provinceCode: '510000', + cityCode: '510100', + districtCode: '510104' + }), '510104'); + + assert.strictEqual(RegionPages.getFinalRegionCode({ + provinceCode: '510000', + cityCode: '', + districtCode: '' + }), '510000'); + + assert.deepStrictEqual(RegionPages.buildProfileRegionBody({ + provinceCode: '510000', + cityCode: '510100', + districtCode: '' + }), { + provinceCode: '510000', + cityCode: '510100', + districtCode: undefined + }); + + console.log('region-pages tests passed'); +} + +run(); diff --git a/tests/security-pages.test.js b/tests/security-pages.test.js new file mode 100644 index 0000000..c3e8f11 --- /dev/null +++ b/tests/security-pages.test.js @@ -0,0 +1,51 @@ +const assert = require('assert'); +const SecurityPages = require('../public/js/security-pages.js'); + +function hash(value) { + return `hashed:${value}`; +} + +function run() { + assert.deepStrictEqual(SecurityPages.buildPasswordChangeBody({ + oldPassword: '123456', + newPassword: 'abcdef' + }, hash), { + oldPassword: 'hashed:123456', + newPassword: 'hashed:abcdef' + }); + + assert.deepStrictEqual(SecurityPages.buildPhoneChangeBody({ + newPhone: ' 13900000000 ', + smsCode: ' 123456 ', + validToken: ' ticket ' + }), { + newPhone: '13900000000', + smsCode: '123456', + validToken: 'ticket' + }); + + assert.deepStrictEqual(SecurityPages.buildSmsLoginBody({ + phone: '13900000000', + smsCode: '123456', + validToken: '' + }), { + phone: '13900000000', + smsCode: '123456', + validToken: undefined + }); + + assert.deepStrictEqual(SecurityPages.buildDeactivateBody({ + password: '123456', + reason: ' 不再使用 ' + }, hash), { + password: 'hashed:123456', + reason: '不再使用' + }); + + assert.strictEqual(SecurityPages.isDangerConfirmed('确认注销'), true); + assert.strictEqual(SecurityPages.isDangerConfirmed('注销'), false); + + console.log('security-pages tests passed'); +} + +run(); diff --git a/tests/upload-pages.test.js b/tests/upload-pages.test.js new file mode 100644 index 0000000..9e8fafa --- /dev/null +++ b/tests/upload-pages.test.js @@ -0,0 +1,33 @@ +const assert = require('assert'); +const UploadPages = require('../public/js/upload-pages.js'); + +function run() { + assert.deepStrictEqual(UploadPages.normalizeUploadResult({ + ossId: 2060001, + fileName: 'cover.jpg', + url: 'https://cdn.example.test/cover.jpg' + }), { + ossId: '2060001', + fileName: 'cover.jpg', + url: 'https://cdn.example.test/cover.jpg' + }); + + assert.deepStrictEqual(UploadPages.normalizeUploadResult({ + id: 2060002, + originalName: 'old.png', + fileUrl: '/old.png' + }), { + ossId: '2060002', + fileName: 'old.png', + url: '/old.png' + }); + + assert.strictEqual(UploadPages.mergeOssIds('', '2060001', false), '2060001'); + assert.strictEqual(UploadPages.mergeOssIds('2060001', '2060002', true), '2060001,2060002'); + assert.strictEqual(UploadPages.mergeOssIds('2060001', '2060001', true), '2060001'); + assert.strictEqual(UploadPages.buildUploadStatus('cover.jpg', '2060001'), 'cover.jpg 上传完成,OSS ID:2060001'); + + console.log('upload-pages tests passed'); +} + +run(); diff --git a/tests/utils.test.js b/tests/utils.test.js new file mode 100644 index 0000000..ee24aaf --- /dev/null +++ b/tests/utils.test.js @@ -0,0 +1,54 @@ +const assert = require('assert'); +const StorageUtil = require('../utils/StorageUtil.js'); +const FormUtil = require('../utils/FormUtil.js'); +const RequestUtil = require('../utils/RequestUtil.js'); + +assert.strictEqual(FormUtil.trimOrUndefined(' 张三 '), '张三'); +assert.strictEqual(FormUtil.trimOrUndefined(' '), undefined); +assert.strictEqual(FormUtil.toNumberOrUndefined('12'), 12); +assert.strictEqual(FormUtil.toNumberOrUndefined(''), undefined); +assert.strictEqual(FormUtil.toBoolean('on'), true); +assert.strictEqual(FormUtil.toBoolean('0'), false); + +const store = {}; +const storage = { + getItem: (key) => Object.prototype.hasOwnProperty.call(store, key) ? store[key] : null, + setItem: (key, value) => { + store[key] = String(value); + }, + removeItem: (key) => { + delete store[key]; + } +}; + +StorageUtil.write(storage, 'token', 'abc'); +assert.strictEqual(StorageUtil.read(storage, 'token'), 'abc'); +StorageUtil.remove(storage, 'token'); +assert.strictEqual(StorageUtil.read(storage, 'token'), null); + +const calls = []; +const requester = RequestUtil.createRequester({ + baseUrl: 'https://test-genealogy-api.ddxcjp.cn', + clientId: 'pc-client', + tokenHeaderName: 'Authorization', + getToken: () => 'token-x', + fetchImpl: async (url, options) => { + calls.push({ url, options }); + + return { + ok: true, + status: 200, + json: async () => ({ code: 200, data: { ok: true } }) + }; + } +}); + +requester('GET', '/genealogy/pc/auth/profile', { + query: { keyword: '汤 氏' } +}).then((data) => { + assert.deepStrictEqual(data, { ok: true }); + assert.strictEqual(calls[0].url, 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/profile?keyword=%E6%B1%A4+%E6%B0%8F'); + assert.strictEqual(calls[0].options.headers.clientid, 'pc-client'); + assert.strictEqual(calls[0].options.headers.Authorization, 'Bearer token-x'); + console.log('utils tests passed'); +}); diff --git a/tests/vip-pages.test.js b/tests/vip-pages.test.js new file mode 100644 index 0000000..c9f10bb --- /dev/null +++ b/tests/vip-pages.test.js @@ -0,0 +1,52 @@ +const assert = require('assert'); +const VipPages = require('../public/js/vip-pages.js'); + +function run() { + assert.deepStrictEqual(VipPages.normalizeList({ rows: [{ packageId: 1 }] }), [{ packageId: 1 }]); + assert.deepStrictEqual(VipPages.normalizeList({ records: [{ packageId: 2 }] }), [{ packageId: 2 }]); + + assert.deepStrictEqual(VipPages.buildVipOrderBody({ + packageId: ' 900060001 ', + genealogyId: ' 900001001 ', + payType: 'wechat' + }), { + packageId: 900060001, + genealogyId: 900001001, + payType: 'wechat' + }); + + assert.deepStrictEqual(VipPages.buildVipOrderBody({ + packageId: '900060002', + genealogyId: '', + payType: '' + }), { + packageId: 900060002, + genealogyId: undefined, + payType: undefined + }); + + assert.strictEqual( + VipPages.buildPackageCard({ + packageId: 900060001, + packageName: '年度会员', + price: 199, + durationDays: 365, + description: '适合长期维护家谱' + }), + '' + ); + + assert.strictEqual( + VipPages.buildOrderRow({ + orderNo: 'VIP202607090001', + packageName: '年度会员', + payType: 'wechat', + orderStatus: 'created' + }), + '

                        年度会员

                        VIP202607090001 · 微信支付 · created

                        订单
                        ' + ); + + console.log('vip-pages tests passed'); +} + +run(); diff --git a/utils/DateUtil.js b/utils/DateUtil.js new file mode 100644 index 0000000..2a1a633 --- /dev/null +++ b/utils/DateUtil.js @@ -0,0 +1,258 @@ +/** + * 日期时间工具类。 + * + * 说明: + * - 页面只需要展示日期时,统一使用这里的方法,避免每个页面重复处理格式。 + * - 后端常见日期格式为 `YYYY-MM-DD` 或 `YYYY-MM-DD HH:mm:ss`。 + * - 纯日期字符串用本地时区解析,避免浏览器按 UTC 解析导致日期偏移。 + */ +class DateUtil { + /** + * 转为“5月9日”格式。 + * + * @param {string|Date} dateStr 后端时间字符串或 Date 对象 + * @returns {string} + */ + static formatDateToMonthDay(dateStr) { + try { + // 第一步:把传入的字符串或 Date 对象统一转成 Date。 + const date = DateUtil._parseDate(dateStr); + + // 第二步:无效日期直接返回空字符串,页面不显示错误内容。 + if (!DateUtil._isValidDate(date)) { + return ''; + } + + // 第三步:取月份和日期,拼接成中文展示格式。 + const month = date.getMonth() + 1; + const day = date.getDate(); + return `${month}月${day}日`; + } catch (error) { + return ''; + } + } + + /** + * 转为“2026年5月9日”格式。 + * + * @param {string|Date} dateStr 后端时间字符串或 Date 对象 + * @returns {string} + */ + static formatDateToYearMonthDay(dateStr) { + try { + // 第一步:统一解析日期。 + const date = DateUtil._parseDate(dateStr); + + // 第二步:无效日期不继续格式化。 + if (!DateUtil._isValidDate(date)) { + return ''; + } + + // 第三步:分别取年月日,拼成完整中文日期。 + const year = date.getFullYear(); + const month = date.getMonth() + 1; + const day = date.getDate(); + return `${year}年${month}月${day}日`; + } catch (error) { + return ''; + } + } + + /** + * 转为“YYYY-MM-DD”格式,适合列表右侧日期展示。 + * + * @param {string|Date} dateStr 后端时间字符串或 Date 对象 + * @returns {string} + */ + static formatDateToYMD(dateStr) { + try { + // 第一步:统一解析日期。 + const date = DateUtil._parseDate(dateStr); + + // 第二步:无效日期返回空字符串。 + if (!DateUtil._isValidDate(date)) { + return ''; + } + + // 第三步:月份和日期补零,保证列表展示宽度稳定。 + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; + } catch (error) { + return ''; + } + } + + /** + * 转为“YYYY-MM-DD HH:mm:ss”格式,适合资金流水、订单时间等完整时间展示。 + * + * @param {string|Date} dateStr 后端时间字符串或 Date 对象 + * @returns {string} + */ + static formatDateTimeToYMDHMS(dateStr) { + try { + // 第一步:统一解析日期。 + const date = DateUtil._parseDate(dateStr); + + // 第二步:无效日期返回空字符串,避免页面显示 Invalid Date。 + if (!DateUtil._isValidDate(date)) { + return ''; + } + + // 第三步:分别补齐年月日时分秒,保证表格宽度稳定。 + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hour = String(date.getHours()).padStart(2, '0'); + const minute = String(date.getMinutes()).padStart(2, '0'); + const second = String(date.getSeconds()).padStart(2, '0'); + + // 第四步:拼接成后端和页面都常用的完整时间格式。 + return `${year}-${month}-${day} ${hour}:${minute}:${second}`; + } catch (error) { + return ''; + } + } + + /** + * 转为“HH:mm:ss”格式,适合日志或轮询状态时间展示。 + * + * @param {string|number|Date} dateStr 后端时间、时间戳或 Date 对象 + * @returns {string} + */ + /** + * 转为“YYYY-MM-DD HH:mm”格式,适合评论发布时间等不需要精确到秒的场景。 + * + * @param {string|Date} dateStr 后端时间字符串或 Date 对象 + * @returns {string} + */ + static formatDateTimeToYMDHM(dateStr) { + try { + // 第一步:统一解析传入的时间。 + const date = DateUtil._parseDate(dateStr); + + // 第二步:无效时间返回空字符串,避免页面显示 Invalid Date。 + if (!DateUtil._isValidDate(date)) { + return ''; + } + + // 第三步:只补齐到分钟,不展示秒。 + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hour = String(date.getHours()).padStart(2, '0'); + const minute = String(date.getMinutes()).padStart(2, '0'); + + return `${year}-${month}-${day} ${hour}:${minute}`; + } catch (error) { + return ''; + } + } + + static formatTimeToHMS(dateStr) { + try { + // 第一步:时间戳直接转 Date,字符串和 Date 继续走统一解析。 + const date = typeof dateStr === 'number' ? new Date(dateStr) : DateUtil._parseDate(dateStr); + + // 第二步:无效时间返回空字符串。 + if (!DateUtil._isValidDate(date)) { + return ''; + } + + // 第三步:补齐时分秒,保持展示稳定。 + const hour = String(date.getHours()).padStart(2, '0'); + const minute = String(date.getMinutes()).padStart(2, '0'); + const second = String(date.getSeconds()).padStart(2, '0'); + + return `${hour}:${minute}:${second}`; + } catch (error) { + return ''; + } + } + + /** + * 获取当前时间。 + * + * @returns {Date} + */ + static getCurrentDate() { + return new Date(); + } + + /** + * 判断两个日期是否为同一天。 + * + * @param {string|Date} date1 第一个日期 + * @param {string|Date} date2 第二个日期 + * @returns {boolean} + */ + static isSameDay(date1, date2) { + try { + // 第一步:两个入参都先转成 Date。 + const d1 = DateUtil._parseDate(date1); + const d2 = DateUtil._parseDate(date2); + + // 第二步:任意一个日期无效,就判定不是同一天。 + if (!DateUtil._isValidDate(d1) || !DateUtil._isValidDate(d2)) { + return false; + } + + // 第三步:同时比较年、月、日。 + return d1.getFullYear() === d2.getFullYear() + && d1.getMonth() === d2.getMonth() + && d1.getDate() === d2.getDate(); + } catch (error) { + return false; + } + } + + /** + * 统一解析日期。 + * + * @param {string|Date} dateStr 日期字符串或 Date 对象 + * @returns {Date|null} + */ + static _parseDate(dateStr) { + // 第一步:如果已经是 Date 对象,直接返回。 + if (dateStr instanceof Date) { + return dateStr; + } + + // 第二步:非字符串不尝试解析。 + if (typeof dateStr !== 'string') { + return null; + } + + // 第三步:去掉首尾空格,兼容接口偶发空白。 + const value = dateStr.trim(); + + if (!value) { + return null; + } + + // 第四步:纯日期用斜杠格式解析,避免浏览器按 UTC 导致日期前后偏移。 + if (/^\d{4}-\d{2}-\d{2}$/.test(value)) { + return new Date(value.replace(/-/g, '/')); + } + + // 第五步:带时间的字符串交给浏览器 Date 解析。 + return new Date(value); + } + + /** + * 判断 Date 对象是否有效。 + * + * @param {Date|null} date 日期对象 + * @returns {boolean} + */ + static _isValidDate(date) { + return date instanceof Date && !Number.isNaN(date.getTime()); + } +} + +if (typeof module !== 'undefined' && module.exports) { + module.exports = DateUtil; +} else if (typeof window !== 'undefined') { + window.DateUtil = DateUtil; +} diff --git a/utils/FormUtil.js b/utils/FormUtil.js new file mode 100644 index 0000000..cee92b6 --- /dev/null +++ b/utils/FormUtil.js @@ -0,0 +1,61 @@ +(function (root, factory) { + // 表单工具只处理跨页面通用转换,具体业务字段留在页面脚本中组织。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(); + return; + } + + root.FormUtil = factory(); +})(typeof globalThis !== 'undefined' ? globalThis : window, function () { + 'use strict'; + + function trimOrUndefined(value) { + if (value === undefined || value === null) return undefined; + + var text = String(value).trim(); + return text ? text : undefined; + } + + function toNumberOrUndefined(value) { + var text = trimOrUndefined(value); + if (text === undefined) return undefined; + + var numberValue = Number(text); + return Number.isNaN(numberValue) ? undefined : numberValue; + } + + function toBoolean(value) { + if (value === true || value === false) return value; + + var text = trimOrUndefined(value); + if (text === undefined) return false; + + return !['0', 'false', 'off', 'no'].includes(text.toLowerCase()); + } + + function getControlValue(control) { + if (!control || control.disabled || !control.name) return undefined; + if (control.type === 'checkbox') return control.checked ? (control.value || 'on') : undefined; + if (control.type === 'radio') return control.checked ? control.value : undefined; + return control.value; + } + + function getFormValues(form) { + var values = {}; + if (!form || !form.elements) return values; + + Array.prototype.forEach.call(form.elements, function (control) { + var value = getControlValue(control); + if (value !== undefined) values[control.name] = value; + }); + + return values; + } + + return { + trimOrUndefined: trimOrUndefined, + toNumberOrUndefined: toNumberOrUndefined, + toBoolean: toBoolean, + getFormValues: getFormValues + }; +}); diff --git a/utils/MessageUtil.js b/utils/MessageUtil.js new file mode 100644 index 0000000..56a46e0 --- /dev/null +++ b/utils/MessageUtil.js @@ -0,0 +1,34 @@ +(function (root, factory) { + // 消息工具同时支持浏览器页面和 Node 测试,避免页面脚本直接使用原生 alert。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.MessageUtil = factory(root); +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + var DEFAULT_OPTIONS = { + skin: 'auth-layer-message', + time: 2200 + }; + + function show(message, options) { + var text = String(message || ''); + var settings = Object.assign({}, DEFAULT_OPTIONS, options || {}); + + if (root.layui && root.layui.layer && root.layui.layer.msg) { + root.layui.layer.msg(text, settings); + return; + } + + if (root.console && root.console.warn) { + root.console.warn(text); + } + } + + return { + show: show + }; +}); diff --git a/utils/RequestUtil.js b/utils/RequestUtil.js new file mode 100644 index 0000000..8faa813 --- /dev/null +++ b/utils/RequestUtil.js @@ -0,0 +1,120 @@ +(function (root, factory) { + // 请求工具封装 URL、Header 和响应解包,业务路径由 api-client 统一提供。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(root); + return; + } + + root.RequestUtil = factory(root); +})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { + 'use strict'; + + function trimSlashes(value) { + return String(value || '').replace(/^\/+|\/+$/g, ''); + } + + function joinUrl(baseUrl, path) { + var base = String(baseUrl || '').replace(/\/+$/g, ''); + var cleanPath = '/' + trimSlashes(path); + return base + cleanPath; + } + + function appendQuery(url, query) { + if (!query) return url; + + var params = new URLSearchParams(); + Object.keys(query).forEach(function (key) { + var value = query[key]; + if (value === undefined || value === null || value === '') return; + params.append(key, value); + }); + + var queryText = params.toString(); + return queryText ? url + '?' + queryText : url; + } + + function isFormData(body) { + return typeof FormData !== 'undefined' && body instanceof FormData; + } + + function createHttpError(message, detail) { + var error = new Error(message); + Object.keys(detail || {}).forEach(function (key) { + error[key] = detail[key]; + }); + return error; + } + + async function parseJson(response) { + try { + return await response.json(); + } catch (error) { + return null; + } + } + + function unwrapResponse(payload) { + if (!payload || typeof payload !== 'object') return payload; + + if (payload.code !== undefined && payload.code !== 200) { + throw createHttpError(payload.msg || '接口请求失败', { + code: payload.code, + response: payload + }); + } + + if (Object.prototype.hasOwnProperty.call(payload, 'data')) return payload.data; + if (Object.prototype.hasOwnProperty.call(payload, 'rows')) { + return { + rows: payload.rows, + total: payload.total + }; + } + + return payload; + } + + function createRequester(options) { + var settings = options || {}; + var fetchImpl = settings.fetchImpl || root.fetch; + + return async function request(method, path, requestOptions) { + var req = requestOptions || {}; + var headers = Object.assign({}, req.headers || {}); + var token = settings.getToken ? settings.getToken() : ''; + var tokenHeaderName = settings.tokenHeaderName || 'Authorization'; + var url = appendQuery(joinUrl(settings.baseUrl, path), req.query); + var body = req.body; + + headers.clientid = settings.clientId; + if (token && req.auth !== false) headers[tokenHeaderName] = 'Bearer ' + token; + + if (body !== undefined && body !== null && !isFormData(body)) { + headers['Content-Type'] = headers['Content-Type'] || 'application/json'; + body = JSON.stringify(body); + } + + var response = await fetchImpl(url, { + method: method, + headers: headers, + body: body + }); + var payload = await parseJson(response); + + if (!response.ok) { + throw createHttpError((payload && payload.msg) || '接口请求失败', { + status: response.status, + response: payload + }); + } + + return unwrapResponse(payload); + }; + } + + return { + createRequester: createRequester, + joinUrl: joinUrl, + appendQuery: appendQuery + }; +}); diff --git a/utils/StorageUtil.js b/utils/StorageUtil.js new file mode 100644 index 0000000..676810c --- /dev/null +++ b/utils/StorageUtil.js @@ -0,0 +1,41 @@ +(function (root, factory) { + // 存储工具同时支持浏览器页面和 Node 测试。 + if (typeof module === 'object' && module.exports) { + module.exports = factory(); + return; + } + + root.StorageUtil = factory(); +})(typeof globalThis !== 'undefined' ? globalThis : window, function () { + 'use strict'; + + function read(storage, key) { + try { + return storage && storage.getItem(key); + } catch (error) { + return null; + } + } + + function write(storage, key, value) { + try { + if (storage) storage.setItem(key, value); + } catch (error) { + // localStorage 可能因为隐私模式或配额限制失败,调用方无需因此中断页面。 + } + } + + function remove(storage, key) { + try { + if (storage) storage.removeItem(key); + } catch (error) { + // 删除失败时保持静默,避免影响退出登录等后续流程。 + } + } + + return { + read: read, + write: write, + remove: remove + }; +});