Commit f55c6aab by pangchong

Initial commit

parents
# 项目开发规范与思考规则约束
> [!IMPORTANT]
> **全局核心规则**:AI 助手在执行任务、思考过程以及与用户交流时,**必须**统一使用 **中文**。
> 本项目是有风格皮肤设置的,具体颜色与样式类名定义请遵循以下详细规则。
---
## 1. 思考与语言规范
- AI 助手在分析问题、编写规划、输出日志及用户沟通的整个生命周期中,**必须**统一使用**中文**
- 自动生成的 git Commit Message 或文件注释也必须使用中文。
---
## 2. 目录规范与模块解耦 (三层标准结构)
为了确保项目结构的绝对统一,所有业务页面 (Page) 及其下属子组件 (Component) 必须采用以下目录树结构,严格执行代码逻辑、类型与常量的拆分:
### 页面级模块目录结构
```text
[ParentDir]/ # 父级目录 (若有多个模块并列)
├── [module_name]/ # 该模块的逻辑隔离子目录 (强制)
│ ├── constants/ # 页面级常量与类型目录
│ │ └── index.ts # 存放该模块的所有静态常量、数据源及接口类型定义
│ ├── functionals/ # 页面级逻辑目录
│ │ └── index.ts # 存放业务逻辑、请求方法等 (改用以 use 开头的逻辑 Hook 封装,移除 Class)
│ └── components/ # 模块专有组件目录 (强制)
│ └── [ComponentName]/ # 子组件自内聚目录 (强制)
│ ├── index.vue # 组件模板与核心视图
│ ├── constants/ # 组件级常量与类型目录
│ │ └── index.ts # 存放子组件专属的常量与 TypeScript 类型/接口
│ └── functionals/ # 组件级逻辑目录
│ └── index.ts # 存放子组件专属逻辑方法 (强制使用以 use 开头的 Hook 函数归类,如 useListEditor)
└── [module_name].vue # 页面主入口 (必须在父目录,适配路由)
```
- **Vue 单文件 TS 提取要求**:Vue 页面主入口或下属组件的 `.vue` 文件中,禁止书写冗长的业务处理函数,必须将其全部提取到各自目录下的 `functionals/index.ts` 中。
- **避免硬编码与混杂 Class**:在 `functionals/index.ts` 中编写业务逻辑时,组件级及非跨页面的功能逻辑**不要使用 Class 进行封装**,而应当采用 Vue 风格的以 `use` 开头的 **逻辑 Hook 方法**(例如 `export function useListEditor() { ... }`),并在 `.vue` 中通过解构调用。Class 应当仅被用来定义一类具备具体强实体特征或需要持久状态维持的特定全局/跨模块方法服务。
- **TypeScript 类型存放约束**:模块或组件相关的类型/接口(如 `interface``type`)必须统一放在同级 `constants/index.ts` 中,严禁在 `functionals/index.ts``.vue` 单文件中硬编码声明。
---
## 3. Vue 单文件结构规范
所有 `.vue` 文件的内部结构必须严格遵循以下顺序:
1. **`<template>`**:视图模板放在最上方。
2. **`<script>`**:逻辑脚本紧随其后。
3. **`<style>`**:CSS 样式(如果有)放在最下方。
---
## 4. 自动导入 (Auto Import) 规范
- **禁止手动导入 Vue 常用 API**:如 `ref`, `computed`, `reactive`, `h`, `nextTick`, `onMounted` 等,以及 `useTableSearch`, `useDictStore`, `service`, `toCamelCase`, `openUploadModal` 等常用 Hooks 与 Utils,严禁手动编写 `import` 语句。
- **禁止手动导入 formatDate**:全局工具函数 `formatDate` 已通过自动导入机制全局生效,在所有 `.vue``.ts` 文件中直接调用即可。
---
## 5. 异步编程规范
- 全项目所有异步操作(如 API 请求、弹窗确认、文件上传等)**必须**统一使用 `async/await` 语法。
- **严禁**使用 `.then()``.catch()``.finally()` 链式调用,以确保业务代码逻辑呈线性流。
---
## 6. Tailwind CSS 优先与颜色值使用限制
- **基础样式**:间距、对齐、扁平布局等应优先使用 Tailwind CSS 类名。
- **严禁硬编码颜色值**:严禁硬编码任何 Hex、RGB 或原生 CSS 颜色(如 `#a8a297``red`)。
- **必须使用系统主题色**:可通过 `useThemeVars()` 获取并绑定(如 `themeVars.textColorDisabled``themeVars.primaryColor` 等),或利用 CSS 变量传递给 scoped style。
- **禁止硬编码灰度/普通 Tailwind 颜色类**:背景、边框及文字颜色,严禁使用固定的 Tailwind 灰度梯度类(例如 `bg-gray-50`, `bg-gray-100`, `border-gray-200`, `text-gray-700` 等),必须使用全局自适应的主题变量类:
- **背景色**`bg-fill-1` ~ `bg-fill-4`
- **边框色**`border-color1` ~ `border-color4``border-divider``border-base`
- **文字色**`text-color1` ~ `text-color4``text-primary``text-regular``text-secondary`
- **状态/提示色**:使用 `text-danger` (或 `text-danger-x``text-danger-6` 对应系统错误色)、`text-success` (成功色)、`text-warning` (警示色),严禁使用 `text-red-600` 等固定原生色。
### 主题配置参考
系统内置的亮暗主题颜色配置如下,可通过主题变量自适应:
| 属性名称 | 亮色主题 (Light) | 暗色主题 (Dark) |
|---|---|---|
| **primaryColor** | `#165DFF` | `#3c7eff` |
| **successColor** | `#00B42A` | `#3c7eff` |
| **warningColor** | `#FF7D00` | `#f2c97d` |
| **errorColor** | `#F53F3F` | `#e88080` |
| **dividerColor** | `#e8eaed` | `rgba(255, 255, 255, 0.09)` |
| **borderColor** | `#dee2e6` | `rgba(255, 255, 255, 0.24)` |
| **bodyColor** | `#f5f7fa` | `rgb(16, 16, 20)` |
| **cardColor** | `#fff` | `rgb(24, 24, 28)` |
| **inputColor** | `rgba(255, 255, 255, 1)` | `rgba(255, 255, 255, 0.1)` |
---
## 7. Dialog 与 UI 反馈规范
- **Promise 风格确认弹窗 (强制)**:删除、禁用、重置等敏感确认操作**必须**使用 `await window.$dialog.warning({ ... })` 进行阻塞调用。
- **禁止 Callback 模式**:严禁使用 `onPositiveClick``onNegativeClick` 等回调函数形式。
- **配置约定**:底层 Hook 已封装默认配置,业务调用时**禁止**手动重复设置 `positiveText: '确定'``negativeText: '取消'`
- **代码书写约定**
```typescript
try {
await window.$dialog.warning({
title: '敏感操作确认',
content: '确定要执行此操作吗?'
})
// 执行业务逻辑
} catch (e) {
// 处理取消
}
```
---
## 8. 按钮组件与检索对齐规范
- **禁止使用 `n-button`**:在所有业务页面中,禁止使用 Naive UI 原生的 `n-button`**强制要求**统一使用项目封装的 `CommonButton`
- **搜索区域对齐与查询重置 (强制)**
- 检索区域内,操作按钮组**必须**直接使用 `<CommonQueryButtons />``<CommonSearchForm />` 组件,**禁止**在业务页面手动包裹 `n-form-item``div` 来排版查询和重置按钮。
- **禁止冗余刷新按钮**:在列表页面的操作区(如 `extra` 插槽)中,**禁止**额外添加名为“刷新”的按钮,因为“查询”按钮本身已具备刷新功能。
- **按钮图标规范**
- 表格上方工具栏区域(通常在 `CommonPageContainer``extra` 插槽内)的所有 `CommonButton` **必须**配置图标。使用 `#icon` 插槽配合 `n-icon` 渲染,图标库统一使用 `@vicons/ionicons5`
- 表格行内操作按钮推荐保持 `text` 属性,且不配置图标,保持界面简洁。
---
## 9. 表格与操作栏规范
- **操作栏按钮间距**:全局样式中已定义 `.n-button:not(:last-child) { margin-right: 10px; }`,自动生效。
- **禁止包装操作列**:在 `CommonTable``columns` 定义中,操作列的 `render` 函数**禁止**使用 `NSpace` 包装按钮,直接返回按钮数组:`[ h(CommonButton, ...), h(CommonButton, ...) ]`
- **禁用居中对齐**:表格列定义中**禁止**使用 `align: 'center'`(包括操作列,全项目表格统一不再居中对齐),禁止手动添加额外的居中样式。
- **表格操作按钮样式**:表格行内操作按钮**必须**开启 `text` 模式(即 `text: true`)。
---
## 10. 日期组件与格式化规范
- **禁用 `n-date-picker`**:强制统一使用封装好的 `CommonDatePicker` 组件。
- **强制使用全局 `formatDate`**:在日期回显、表格列 `render`、或表单详情初始化中,将日期格式化或截取为 `yyyy-MM-dd` 时,**禁止**使用原生 `.split(' ')[0]``.substring(0, 10)` 等截取写法,**强制要求统一使用全局工具方法 `formatDate(value)`**
- **合理使用日期格式**:必须根据业务场景选用 `formatDate``formatDateTime`。若该字段需要包含时分秒(如创建时间、修改时间),应直接回显或使用 `formatDateTime`,禁止盲目使用 `formatDate` 导致数据丢失。
---
## 11. Naive UI 全局组件属性
- **Clearable 默认开启**:已全局配置 `clearable``true`,在 `.vue` 模板中编写 `n-input``n-select` 等组件时,**禁止**手动添加 `clearable` 属性(特殊要求关闭的除外)。
---
## 12. 全局 Modal 封装与交互规范
- **统一组件**:所有业务弹窗必须使用封装的 `CommonModal` 组件。
- **宽度配置**:弹窗宽度定义**必须**使用 `:width="数字"` 形式(如 `:width="600"`),禁止使用非绑定的 `width="600"` 或手写样式。
- **双向绑定**:在业务逻辑中,**禁止**使用 `v-model:show="xxx"` 语法,必须统一简写为 **`v-model="xxx"`**
- **组件内置事件优先**:提交/确认动作**必须**绑定至组件的 **`@confirm`** 事件,严禁在业务层手动通过 `footer` slot 覆写确认按钮。配置确定文本必须通过 **`confirm-text`** 属性。
- **弹窗加载状态与反馈**
- **禁用全局 Loading**:弹窗内进行请求时,**禁止**调用 `window.$loadingBar`,且在请求配置中**严禁**开启 `showLoading: true`
- **使用组件 loading 属性**:统一利用 Modal 组件提供的 `loading` 属性进行同步,通常绑定为组合状态 **`:loading="fetching || saving"`**
- **加载时机 (先开窗后加载)**:执行 `async` 拉取详情前,必须**先**设置 `show.value = true` 立即打开弹窗,随后立即设置 `fetching.value = true` 开启加载动画。**禁止**等待接口返回后再显示弹窗。
- **状态管理**:必须在 `functionals/index.ts` 的业务类中定义 `fetching``saving` 两个响应式变量,并利用 `try...finally` 块确保状态闭环。
- **禁止手动包裹 `n-spin`**:弹窗内 `n-form` 等内容,禁止手动包裹 `n-spin` 标签,统一通过 `CommonModal` 自身的 `loading` 控制。
- **高度与滚动约束****禁止**`CommonModal` 内部的主容器/表单上显式设置 `overflow-y-auto`、最大高度限制(如 `max-h-[70vh]`)或任何硬编码的高度(如 `h-[500px]`)。这部分高度自适应和内部滚动已由 `CommonModal` 内置完成。
- **按钮 Loading 联动**:弹窗内的提交及控制按钮(包含关闭按钮)必须绑定 `saving``loading``disabled` 状态,确保在接口调用期间按钮被禁用,防止二次提交或意外关闭弹窗。
- **参数传递 (open 方法强制)**
- 父组件调用子弹窗时,**禁止**使用 `v-bind="modalParams"` 或定义过多的 `props` 来同步业务参数。
- 子弹窗**必须**通过 `defineExpose` 暴露一个 `open` 方法,供父组件调用,调用形式为 `modalRef.value?.open(row.PKID, 'edit', ...)`
- **数据选择器 (Picker) 交互**:单选类选择器弹窗必须开启 `CommonTable``choose` 模式,实现点击行即选中的交互,禁止使用 `selection` 单选框列。
- **附件上传**:必须通过 `src/utils/render.ts` 中提供的 `openUploadModal` 方法,严禁直接调用 `window.$uploadModal?.open`
---
## 13. API 请求与异常处理
- **非弹窗场景加载蒙层**:在列表、详情页面等非 Modal 环境中,点击操作直接发起网络请求,**必须**在请求配置中开启 `showLoading: true` 或指定具体加载文案。
- **禁止冗余报错**:拦截器已实现错误探测与底层自动弹窗,业务层**禁止**手动调用 `window.$message.error(res.msg)`
- **判定成功**:全项目统一规定,**仅且只有** `res.code === 200` 判定为请求成功,禁止包含 `res.code === 0` 等其他冗余判断。
---
## 14. Pinia 仓库目录规范
- **禁止单文件声明 Store**:所有的 Pinia 仓库(Store)必须拆分为独立文件夹,严禁使用单文件声明(例如 `store/editorStore.ts`)。
- **目录结构约束**:每个 Pinia 仓库必须遵循以下两文件结构:
```text
src/store/[store_name]/
├── index.ts # 存放仓库的定义(state、getters、actions 等)
└── types.ts # 存放该仓库相关的 TypeScript 类型与接口定义
```
- **禁止手动导入 `defineStore`**`defineStore` 已配置为全局自动导入,在 `index.ts` 中直接使用即可,无需编写 `import { defineStore } from 'pinia'`
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# public/shtml
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
pnpm-lock.yaml
components.d.ts
/src/components.d.ts
/src/auto-imports.d.ts
/dist/*
/html/*
.local
.antigravityrules
.rules
/node_modules/**
**/*.svg
**/*.sh
/public/*
/shtml/*
\ No newline at end of file
{
"singleQuote": true,
"semi": false,
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "ignore",
"endOfLine": "auto",
"trailingComma": "none",
"arrowParens": "always",
"tabWidth": 4,
"printWidth": 150
}
module.exports = {
extends: ['@commitlint/config-conventional'],
// 校验规则
rules: {
'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'revert', 'build']],
'type-case': [0],
'type-empty': [0],
'scope-empty': [0],
'scope-case': [0],
'subject-full-stop': [0, 'never'],
'subject-case': [0, 'never'],
'header-max-length': [0, 'always', 72]
}
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AMRO 系统</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "editor",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"dev:prod": "vite --mode prod",
"dev:test": "vite --mode test",
"build": "vue-tsc -b && vite build",
"build:prod": "vue-tsc -b && vite build --mode prod",
"preview": "vite preview",
"prepare": "husky install",
"commitlint": "commitlint --config commitlint.config.cjs -e -V",
"format": "prettier --write \"./**/*.{html,vue,ts,js,json,md}\""
},
"dependencies": {
"@alova/scene-vue": "^1.6.2",
"@bpmn-io/properties-panel": "^3.40.4",
"@vicons/ionicons5": "^0.13.0",
"alova": "^3.5.0",
"bpmn-js": "^17.11.1",
"bpmn-js-properties-panel": "^5.53.0",
"camunda-bpmn-moddle": "^7.0.1",
"dayjs": "^1.11.19",
"docx-preview": "^0.3.7",
"less": "^4.5.1",
"lodash-es": "^4.18.1",
"mammoth": "^1.11.0",
"mitt": "^3.0.1",
"naive-ui": "^2.43.2",
"pinia": "^3.0.4",
"pinia-plugin-persistedstate": "^4.7.1",
"vfonts": "^0.0.3",
"vue": "^3.5.25",
"vue-i18n": "^11.2.8",
"vue-router": "^5.0.3",
"vuedraggable": "^4.1.0",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@commitlint/cli": "^19.8.0",
"@commitlint/config-conventional": "^19.8.0",
"@types/lodash-es": "^4.17.12",
"@types/node": "^24.10.14",
"@vitejs/plugin-vue": "^6.0.2",
"@vue/tsconfig": "^0.8.1",
"autoprefixer": "^10.4.27",
"husky": "^8.0.0",
"postcss": "^8.5.6",
"postcss-import": "^16.1.1",
"prettier": "^3.3.2",
"tailwindcss": "^3.4.19",
"typescript": "~5.9.3",
"unplugin-auto-import": "^21.0.0",
"unplugin-vue-components": "^31.0.0",
"vite": "^7.3.1",
"vue-tsc": "^3.1.5"
}
}
<?xml version="1.0"?>
<!-- ========================================================= -->
<!-- -->
<!-- (c) 2003, RenderX -->
<!-- -->
<!-- Author: Alexander Peshkov <peshkov@renderx.com> -->
<!-- -->
<!-- Permission is granted to use this document, copy and -->
<!-- modify free of charge, provided that every derived work -->
<!-- bear a reference to the present document. -->
<!-- -->
<!-- This document contains a computer program written in -->
<!-- XSL Transformations Language. It is published with no -->
<!-- warranty of any kind about its usability, as a mere -->
<!-- example of XSL technology. RenderX shall not be -->
<!-- considered liable for any damage or loss of data caused -->
<!-- by use of this program. -->
<!-- -->
<!-- ========================================================= -->
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:svg="http://www.w3.org/2000/svg">
<!-- ========================================================= -->
<!-- This stylesheet exports a named template used to draw -->
<!-- a 3 of 9 barcode (also known as code 39) as an SVG image. -->
<!-- When used stand-alone, it creates SVG from a code -->
<!-- sequence passed in a parameter -->
<!-- -->
<!-- Template "draw-barcode" can take a number of parameters, -->
<!-- of which only the first one is mandatory: -->
<!-- -->
<!-- Mandatory parameters are: -->
<!-- -->
<!-- "sequence" - sequence of barcode states -->
<!-- to be drawn. -->
<!-- -->
<!-- Optional parameters are: -->
<!-- -->
<!-- "string" - a human readable string; -->
<!-- represents data encoded in -->
<!-- the barcode in a human-readable form -->
<!-- Default is empty string. -->
<!-- "print-text" - boolean, defines if a human -->
<!-- readable label should be printed. -->
<!-- "module" - width of the elementary unit -->
<!-- bar/space; -->
<!-- Default is 0.012in -->
<!-- "wide-to-narrow" - width ratio for bars/spaces; -->
<!-- Default is 3.0 -->
<!-- "height" - pattern height (= bar length). -->
<!-- Default is 0.5in -->
<!-- "quiet-horizontal" - quiet zone horizontal margin width -->
<!-- Default is 0.24in -->
<!-- "quiet-vertical" - quiet zone vertical margin width -->
<!-- Default is 0.12in -->
<!-- "font-family" - a font family used to print textual -->
<!-- representation of a barcode. -->
<!-- Default is 'Courier' -->
<!-- "font-height" - a height of the font used to print -->
<!-- textual representation of a barcode. -->
<!-- Default is 10pt -->
<!-- "value" start value-->
<!-- ========================================================= -->
<!-- Bar states code sequence -->
<xsl:param name="sequence" select="''"/>
<!-- Information encoded by given barcode states -->
<xsl:param name="string" select="''"/>
<!-- Defines if checksum should be added by the barcode generator -->
<xsl:param name="addchecksum" select="'false'"/>
<!-- Is human readable string printed under barcode? -->
<xsl:param name="print-text" select="'true'"/>
<!-- Optional parameters for drawing -->
<xsl:param name="module" select="'0.012in'"/>
<xsl:param name="wide-to-narrow" select="3.0"/>
<xsl:param name="height" select="'0.5in'"/>
<xsl:param name="quiet-horizontal" select="'0.24in'"/>
<xsl:param name="quiet-vertical" select="'0.12in'"/>
<xsl:param name="font-family" select="'Courier'"/>
<xsl:param name="font-height" select="'10pt'"/>
<xsl:param name="value" select="''"/>
<!-- Main template. -->
<!-- Normalizes all lengths and calculates all widths/heights -->
<!-- Creates SVG element, prints numerical barcode representation and recursevely draws all bars -->
<xsl:template name="draw-barcode">
<!-- Bar states code sequence -->
<xsl:param name="sequence" select="''"/>
<!-- Information encoded by given barcode states -->
<xsl:param name="string" select="''"/>
<!-- Defines if checksum should be added by the barcode generator -->
<xsl:param name="addchecksum" select="'false'"/>
<!-- Is human readable string printed under barcode? -->
<xsl:param name="print-text" select="'true'"/>
<!-- Optional parameters for drawing -->
<xsl:param name="module" select="'0.012in'"/>
<xsl:param name="wide-to-narrow" select="3.0"/>
<xsl:param name="height" select="'0.5in'"/>
<xsl:param name="quiet-horizontal" select="'0.24in'"/>
<xsl:param name="quiet-vertical" select="'0.12in'"/>
<xsl:param name="font-family" select="'Courier'"/>
<xsl:param name="font-height" select="'10pt'"/>
<xsl:param name="value" select="''"/>
<!-- Parse narrow bar/space width specifier -->
<xsl:variable name="narrow-real">
<xsl:call-template name="convert-to-basic-units">
<xsl:with-param name="length" select="$module"/>
</xsl:call-template>
</xsl:variable>
<!-- Calculate wide bar/space width -->
<xsl:variable name="wide-real" select="round($narrow-real * $wide-to-narrow)"/>
<!-- Parse bar height specifier -->
<xsl:variable name="height-real">
<xsl:call-template name="convert-to-basic-units">
<xsl:with-param name="length" select="$height"/>
</xsl:call-template>
</xsl:variable>
<!-- Parse quiet zone vertical/horizontal margins specifiers -->
<xsl:variable name="quiet-horizontal-real">
<xsl:call-template name="convert-to-basic-units">
<xsl:with-param name="length" select="$quiet-horizontal"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="quiet-vertical-real">
<xsl:call-template name="convert-to-basic-units">
<xsl:with-param name="length" select="$quiet-vertical"/>
</xsl:call-template>
</xsl:variable>
<!-- Calculate font height and line-height-->
<xsl:variable name="font-height-real">
<xsl:call-template name="convert-to-basic-units">
<xsl:with-param name="length" select="$font-height"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="line-height-real" select="round($font-height-real*1.2)*number($print-text='true')"/>
<!-- Useful variable - number of characters in code string -->
<xsl:variable name="length">
<xsl:value-of select="string-length($sequence) div 9"/>
</xsl:variable>
<!-- Calculate codesequence length -->
<xsl:variable name="code-width-real" select="$wide-real*(3*$length) + $narrow-real*(6*$length + $length - 1)"/>
<!-- Calculate drawing area dimensions -->
<xsl:variable name="area-width-real" select="$code-width-real + 2*$quiet-horizontal-real"/>
<xsl:variable name="area-height-real" select="$height-real + $line-height-real + 2*$quiet-vertical-real"/>
<!-- Establish SVG drawing area -->
<!-- Drawing unit is equal to 1/360mm -->
<svg:svg width="{concat($area-width-real div 360, 'mm')}"
height="{concat($area-height-real div 360, 'mm')}"
viewBox="{concat('0 0 ', $area-width-real, ' ', $area-height-real)}">
<desc xmlns:mydoc="http://example.org/mydoc">
<barcode value="{$value}" type="code39" addchecksum="{$addchecksum}"></barcode>
</desc>
<!-- Prepare actual path-->
<xsl:variable name="path">
<!-- Call template to reqursively draw all bars -->
<xsl:call-template name="recursive-draw">
<xsl:with-param name="sequence" select="$sequence"/>
<xsl:with-param name="narrow-real" select="$narrow-real"/>
<xsl:with-param name="wide-real" select="$wide-real"/>
<xsl:with-param name="height-real" select="$height-real"/>
</xsl:call-template>
</xsl:variable>
<!-- Position a pen at the beggining of the barcode and make the actual drawing -->
<xsl:variable name="full-path" select="concat('M ', $quiet-horizontal-real, ' ' , $quiet-vertical-real, $path)"/>
<svg:path d="{$full-path}" fill="black"/>
<xsl:if test="$print-text='true'">
<svg:text x="{$area-width-real div 2}" y="{$quiet-vertical-real*2 + $height-real + $font-height-real}" text-anchor="middle" font-family="{$font-family}" font-size="{$font-height-real}" fill="black"><xsl:value-of select="$string"/></svg:text>
</xsl:if>
</svg:svg>
</xsl:template>
<!-- Draws single bar and calls itself if there are more bars to be drawn -->
<xsl:template name="recursive-draw">
<xsl:param name="sequence"/>
<xsl:param name="narrow-real"/>
<xsl:param name="wide-real"/>
<xsl:param name="height-real"/>
<xsl:param name="position" select="1"/>
<xsl:variable name="barstate" select="substring($sequence, 1, 1)"/>
<!-- Select bar width -->
<xsl:variable name="width">
<xsl:choose>
<xsl:when test="$barstate='1'"><xsl:value-of select="$wide-real"/></xsl:when>
<xsl:otherwise><xsl:value-of select="$narrow-real"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- Draw black or white bar -->
<xsl:choose>
<xsl:when test="$position mod 9 = 0 or (($position mod 9) mod 2) = 1">
<!-- Create appropriate path segment -->
<xsl:value-of select="concat(' l 0 ', $height-real, ' ', $width, ' 0 0 -', $height-real, ' z m ', $width, ' 0')"/>
</xsl:when>
<xsl:otherwise>
<!-- Create appropriate path segment -->
<xsl:value-of select="concat(' m ', $width, ' 0')"/>
</xsl:otherwise>
</xsl:choose>
<!-- Add intercharacter space -->
<xsl:if test="($position mod 9) = 0 and string-length($sequence) &gt; 1">
<!-- Create appropriate path segment -->
<xsl:value-of select="concat(' m ', $narrow-real, ' 0')"/>
</xsl:if>
<xsl:if test="string-length($sequence) &gt; 1">
<xsl:call-template name="recursive-draw">
<xsl:with-param name="sequence" select="substring($sequence,2)"/>
<xsl:with-param name="narrow-real" select="$narrow-real"/>
<xsl:with-param name="wide-real" select="$wide-real"/>
<xsl:with-param name="height-real" select="$height-real"/>
<xsl:with-param name="position" select="$position + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- Utility templates -->
<!-- ========================================================= -->
<!-- Convert any lengths to the basic units. -->
<xsl:template name="convert-to-basic-units">
<xsl:param name="length"/>
<xsl:variable name="length-numeric-value" select="translate ($length, 'ptxcinme ', '')"/>
<xsl:variable name="length-unit" select="translate ($length, '-0123456789. ', '')"/>
<xsl:variable name="length-scale-factor">
<xsl:call-template name="get-unit-scaling-factor">
<xsl:with-param name="unit" select="$length-unit"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="round(number($length-numeric-value) * $length-scale-factor)"/>
</xsl:template>
<!-- ========================================================= -->
<!-- This template expresses all length units in 1/360s of mm. -->
<!-- This is the largest unit in which both 1pt and 1 mm get -->
<!-- integer values. Also spellchecks length units. -->
<xsl:template name="get-unit-scaling-factor">
<xsl:param name="unit"/>
<xsl:choose>
<xsl:when test="$unit = 'cm'">3600</xsl:when>
<xsl:when test="$unit = 'mm'">360</xsl:when>
<xsl:when test="$unit = 'in'">9144</xsl:when>
<xsl:when test="$unit = 'pt'">127</xsl:when>
<xsl:when test="$unit = 'pc'">1524</xsl:when>
<xsl:when test="$unit = 'em'">
<xsl:text>1524</xsl:text> <!-- defaulting to 12pt -->
<xsl:message>
[BARCODE GENERATOR] Units of 'em' should not be mixed with other units;
assuming 1 em = 1 pica.
</xsl:message>
</xsl:when>
<xsl:when test="$unit = 'ex'">
<xsl:text>700</xsl:text> <!-- defaulting to 12pt x 0.46 -->
<xsl:message>
[BARCODE GENERATOR] Units of 'ex' should not be mixed with other units;
assuming 1 ex = 0.46 pica.
</xsl:message>
</xsl:when>
<xsl:otherwise>
<xsl:text>360</xsl:text> <!-- defaulting to 1mm -->
<xsl:message>
[BARCODE GENERATOR] Unknown unit '<xsl:value-of select="$unit"/>' should not be mixed with other units;
assuming 1 <xsl:value-of select="$unit"/> = 1 mm.
</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
\ No newline at end of file
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- ========================================================= -->
<!-- -->
<!-- (c) 2003, RenderX -->
<!-- -->
<!-- Author: Alexander Peshkov <peshkov@renderx.com> -->
<!-- -->
<!-- Permission is granted to use this document, copy and -->
<!-- modify free of charge, provided that every derived work -->
<!-- bear a reference to the present document. -->
<!-- -->
<!-- This document contains a computer program written in -->
<!-- XSL Transformations Language. It is published with no -->
<!-- warranty of any kind about its usability, as a mere -->
<!-- example of XSL technology. RenderX shall not be -->
<!-- considered liable for any damage or loss of data caused -->
<!-- by use of this program. -->
<!-- -->
<!-- ========================================================= -->
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="3of9-data">
<!-- ========================================================= -->
<!-- This stylesheet contains two named templates: -->
<!-- barcode-3of9 and barcode-3of9-extended aimed to encode -->
<!-- sequence of character using 3-of-9 barcode scheme (also -->
<!-- known as code 39). Both templates have the same set of -->
<!-- parameters (described below) and can generate optional -->
<!-- checksum character. However second template produce -->
<!-- extended Barcode 39 (full-ASCII) and thus treats supplied -->
<!-- data in a different way. -->
<!-- -->
<!-- Mandatory parameters are: -->
<!-- -->
<!-- "value" - a string of characters to encode; -->
<!-- details on 'value' data treatment -->
<!-- are presented below. -->
<!-- -->
<!-- Optional parameters are: -->
<!-- -->
<!-- "string" - a human readable string; -->
<!-- represents data encoded in -->
<!-- the barcode in a human-readable form -->
<!-- Optional parameter. -->
<!-- Default is: 'value' with start/stop -->
<!-- and checksum added when necessary. -->
<!-- "print-text" - boolean, defines if a human -->
<!-- readable label should be printed. -->
<!-- Default is: 'true'. -->
<!-- "addchecksum" - boolean, defines if checksum should -->
<!-- be added by the barcode generator; -->
<!-- Default is 'false' -->
<!-- "module" - width of the elementary unit -->
<!-- bar/space; -->
<!-- Default is 0.012in -->
<!-- "wide-to-narrow" - width ratio for bars/spaces; -->
<!-- Default is 3.0 -->
<!-- "height" - pattern height (= bar length). -->
<!-- Default is 0.5in -->
<!-- "quiet-horizontal" - quiet zone horizontal margin width -->
<!-- Default is 0.24in -->
<!-- "quiet-vertical" - quiet zone vertical margin width -->
<!-- Default is 0.12in -->
<!-- "font-family" - a font family used to print textual -->
<!-- representation of a barcode. -->
<!-- Default is 'Courier' -->
<!-- "font-height" - a height of the font used to print -->
<!-- textual representation of a barcode. -->
<!-- Default is 10pt -->
<!-- Alphabet of standart Barcode 3 of 9 includes only -->
<!-- capital latin letters, digits and several punctuation -->
<!-- symbols. When 'barcode-39' template is used only those -->
<!-- characters considered as a valid input. -->
<!-- Extended code 3 of 9 can encode full ASCII table. -->
<!-- When 'barcode-3of9-extended' template is used, data found -->
<!-- in the 'value' field is treated as follows: -->
<!-- whole standard code 39 character set can be used as it is -->
<!-- except for percent sign. All other ASCII characters -->
<!-- including '%' should be encoded using URL encoding: -->
<!-- percent sign followed be two hexadecimal digits. -->
<!-- Examples: -->
<!-- %0A - Line Feed; -->
<!-- %78 - lowercase 'x'; -->
<!-- %25 - percent sign. -->
<!-- -->
<!-- Notes: -->
<!-- 1. It's an error if '%' is not followed by two hex digits.-->
<!-- In this case warning will be issued, percent sign will -->
<!-- be treated as itself and following data will be -->
<!-- encoded as if there was no '%'. -->
<!-- 2. You can pass any characters present in standard code 39-->
<!-- charset in a URL-encoded form, they will be decoded -->
<!-- in the same way as if passed be itselfs. -->
<!-- 3. In standard code 3 of 9 asterisks are reserved for -->
<!-- start/stop signals and thus cannot be present in a -->
<!-- 'value' data. -->
<!-- ========================================================= -->
<xsl:import href="3of9-svg.xsl"/>
<xsl:output method="xml"
version="1.0"
indent="yes"/>
<xsl:param name="value"/>
<xsl:param name="string"/>
<xsl:param name="print-text" select="'true'"/>
<xsl:param name="addchecksum" select="'false'"/>
<xsl:param name="module" select="'0.012in'"/>
<xsl:param name="wide-to-narrow" select="3.0"/>
<xsl:param name="height" select="'0.5in'"/>
<xsl:param name="quiet-horizontal" select="'0.24in'"/>
<xsl:param name="quiet-vertical" select="'0.12in'"/>
<xsl:param name="font-family" select="'Courier'"/>
<xsl:param name="font-height" select="'10pt'"/>
<!-- Main template used to create a standard barcode 3 of 9 -->
<xsl:template name="barcode-3of9">
<xsl:param name="value"/>
<xsl:param name="string"/>
<xsl:param name="print-text" select="'false'"/>
<xsl:param name="addchecksum" select="'false'"/>
<xsl:param name="module" select="'0.012in'"/>
<xsl:param name="wide-to-narrow" select="3.0"/>
<xsl:param name="height" select="'0.5in'"/>
<xsl:param name="quiet-horizontal" select="'0.24in'"/>
<xsl:param name="quiet-vertical" select="'0.12in'"/>
<xsl:param name="font-family" select="'Courier'"/>
<xsl:param name="font-height" select="'10pt'"/>
<!-- Add checksum character to the value if necessary -->
<!-- Add start/stop character ('*') -->
<xsl:variable name="value-real">
<xsl:text>*</xsl:text>
<xsl:value-of select="$value"/>
<xsl:if test="$addchecksum='true'">
<xsl:call-template name="checksum">
<xsl:with-param name="string" select="$value"/>
</xsl:call-template>
</xsl:if>
<xsl:text>*</xsl:text>
</xsl:variable>
<!-- Encode string in bars -->
<xsl:variable name="value-encoded">
<xsl:call-template name="char2bar_codec">
<xsl:with-param name="string" select="$value-real"/>
</xsl:call-template>
</xsl:variable>
<!-- Call backend to generate SVG image of the barcode -->
<xsl:call-template name="draw-barcode">
<xsl:with-param name="sequence" select="$value-encoded"/>
<xsl:with-param name="string">
<xsl:choose>
<xsl:when test="string-length($string)=0">
<xsl:value-of select="$value-real"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
<xsl:with-param name="addchecksum" select="$addchecksum"/>
<xsl:with-param name="print-text" select="$print-text"/>
<xsl:with-param name="module" select="$module"/>
<xsl:with-param name="wide-to-narrow" select="$wide-to-narrow"/>
<xsl:with-param name="height" select="$height"/>
<xsl:with-param name="quiet-horizontal" select="$quiet-horizontal"/>
<xsl:with-param name="quiet-vertical" select="$quiet-vertical"/>
<xsl:with-param name="font-family" select="$font-family"/>
<xsl:with-param name="font-height" select="$font-height"/>
<xsl:with-param name="value" select="$value"/>
</xsl:call-template>
</xsl:template>
<!-- Template used to create an extended barcode 3 of 9 -->
<xsl:template name="barcode-3of9-extended">
<xsl:param name="value"/>
<xsl:param name="string"/>
<xsl:param name="print-text" select="'true'"/>
<xsl:param name="addchecksum" select="'false'"/>
<xsl:param name="module" select="'0.012in'"/>
<xsl:param name="wide-to-narrow" select="3.0"/>
<xsl:param name="height" select="'0.5in'"/>
<xsl:param name="quiet-horizontal" select="'0.24in'"/>
<xsl:param name="quiet-vertical" select="'0.12in'"/>
<xsl:param name="font-family" select="'Courier'"/>
<xsl:param name="font-height" select="'10pt'"/>
<!-- Transcode data from URL-encoding to 3 of 9 extended code -->
<xsl:variable name="value-transcoded">
<xsl:call-template name="ascii2extended">
<xsl:with-param name="value" select="$value"/>
</xsl:call-template>
</xsl:variable>
<!-- Call main template to produce barcode 3 of 9 -->
<xsl:call-template name="barcode-3of9">
<xsl:with-param name="value" select="$value-transcoded"/>
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="print-text" select="$print-text"/>
<xsl:with-param name="addchecksum" select="$addchecksum"/>
<xsl:with-param name="module" select="$module"/>
<xsl:with-param name="wide-to-narrow" select="$wide-to-narrow"/>
<xsl:with-param name="height" select="$height"/>
<xsl:with-param name="quiet-horizontal" select="$quiet-horizontal"/>
<xsl:with-param name="quiet-vertical" select="$quiet-vertical"/>
<xsl:with-param name="font-family" select="$font-family"/>
<xsl:with-param name="font-height" select="$font-height"/>
</xsl:call-template>
</xsl:template>
<!-- Helper templates -->
<!-- Creates checksum character -->
<!-- Recursively convert characters to their codes, sum them up, -->
<!-- devide sum by 43 and return devision reminder coded as character -->
<xsl:template name="checksum">
<xsl:param name="string"/>
<xsl:param name="sum" select="0"/>
<xsl:variable name="num" select="document('')//my:char2num/entry[@char=substring($string, 1, 1)]/text()"/>
<xsl:choose>
<xsl:when test="string-length($string) &gt; 1">
<xsl:call-template name="checksum">
<xsl:with-param name="string" select="substring($string, 2)"/>
<xsl:with-param name="sum" select="$sum+$num"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="document('')//my:num2char/entry[@num=(($sum+$num) mod 43)]/text()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Codes alpha-numerical string into bar states using appropriate table -->
<xsl:template name="char2bar_codec">
<xsl:param name="string"/>
<xsl:value-of select="document('')//my:char2bar/entry[@char=substring($string, 1, 1)]/text()"/>
<xsl:if test="string-length($string) &gt; 1">
<xsl:call-template name="char2bar_codec">
<xsl:with-param name="string" select="substring($string, 2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- Codes URL-encoded data into a sequence of code 39 characters -->
<xsl:template name="ascii2extended">
<xsl:param name="value"/>
<xsl:variable name="charlen">
<xsl:choose>
<xsl:when test="starts-with($value, '%') and document('')//my:code2char/entry[@code=substring($value, 2,2)]">
<xsl:text>3</xsl:text>
</xsl:when>
<xsl:otherwise><xsl:text>1</xsl:text></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$charlen &gt; 1">
<xsl:value-of select="document('')//my:code2char/entry[@code=substring($value, 2,2)]"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($value, 1,1)"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="string-length($value) &gt; $charlen">
<xsl:call-template name="ascii2extended">
<xsl:with-param name="value" select="substring($value, $charlen+1)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- Code tables defined in the standard -->
<my:char2bar>
<entry char="1">100100001</entry>
<entry char="2">001100001</entry>
<entry char="3">101100000</entry>
<entry char="4">000110001</entry>
<entry char="5">100110000</entry>
<entry char="6">001110000</entry>
<entry char="7">000100101</entry>
<entry char="8">100100100</entry>
<entry char="9">001100100</entry>
<entry char="0">000110100</entry>
<entry char="A">100001001</entry>
<entry char="B">001001001</entry>
<entry char="C">101001000</entry>
<entry char="D">000011001</entry>
<entry char="E">100011000</entry>
<entry char="F">001011000</entry>
<entry char="G">000001101</entry>
<entry char="H">100001100</entry>
<entry char="I">001001100</entry>
<entry char="J">000011100</entry>
<entry char="K">100000011</entry>
<entry char="L">001000011</entry>
<entry char="M">101000010</entry>
<entry char="N">000010011</entry>
<entry char="O">100010010</entry>
<entry char="P">001010010</entry>
<entry char="Q">000000111</entry>
<entry char="R">100000110</entry>
<entry char="S">001000110</entry>
<entry char="T">000010110</entry>
<entry char="U">110000001</entry>
<entry char="V">011000001</entry>
<entry char="W">111000000</entry>
<entry char="X">010010001</entry>
<entry char="Y">110010000</entry>
<entry char="Z">011010000</entry>
<entry char="-">010000101</entry>
<entry char="&#183;">110000100</entry>
<entry char=" ">011000100</entry>
<entry char="*">010010100</entry>
<entry char="$">010101000</entry>
<entry char="/">010100010</entry>
<entry char="+">010001010</entry>
<entry char="%">000101010</entry>
</my:char2bar>
<my:char2num>
<entry char="0">0</entry>
<entry char="1">1</entry>
<entry char="2">2</entry>
<entry char="3">3</entry>
<entry char="4">4</entry>
<entry char="5">5</entry>
<entry char="6">6</entry>
<entry char="7">7</entry>
<entry char="8">8</entry>
<entry char="9">9</entry>
<entry char="A">10</entry>
<entry char="B">11</entry>
<entry char="C">12</entry>
<entry char="D">13</entry>
<entry char="E">14</entry>
<entry char="F">15</entry>
<entry char="G">16</entry>
<entry char="H">17</entry>
<entry char="I">18</entry>
<entry char="J">19</entry>
<entry char="K">20</entry>
<entry char="L">21</entry>
<entry char="M">22</entry>
<entry char="N">23</entry>
<entry char="O">24</entry>
<entry char="P">25</entry>
<entry char="Q">26</entry>
<entry char="R">27</entry>
<entry char="S">28</entry>
<entry char="T">29</entry>
<entry char="U">30</entry>
<entry char="V">31</entry>
<entry char="W">32</entry>
<entry char="X">33</entry>
<entry char="Y">34</entry>
<entry char="Z">35</entry>
<entry char="-">36</entry>
<entry char="&#183;">37</entry>
<entry char=" ">38</entry>
<entry char="$">39</entry>
<entry char="/">40</entry>
<entry char="+">41</entry>
<entry char="%">42</entry>
</my:char2num>
<my:num2char>
<entry num="0">0</entry>
<entry num="1">1</entry>
<entry num="2">2</entry>
<entry num="3">3</entry>
<entry num="4">4</entry>
<entry num="5">5</entry>
<entry num="6">6</entry>
<entry num="7">7</entry>
<entry num="8">8</entry>
<entry num="9">9</entry>
<entry num="10">A</entry>
<entry num="11">B</entry>
<entry num="12">C</entry>
<entry num="13">D</entry>
<entry num="14">E</entry>
<entry num="15">F</entry>
<entry num="16">G</entry>
<entry num="17">H</entry>
<entry num="18">I</entry>
<entry num="19">J</entry>
<entry num="20">K</entry>
<entry num="21">L</entry>
<entry num="22">M</entry>
<entry num="23">N</entry>
<entry num="24">O</entry>
<entry num="25">P</entry>
<entry num="26">Q</entry>
<entry num="27">R</entry>
<entry num="28">S</entry>
<entry num="29">T</entry>
<entry num="30">U</entry>
<entry num="31">V</entry>
<entry num="32">W</entry>
<entry num="33">X</entry>
<entry num="34">Y</entry>
<entry num="35">Z</entry>
<entry num="36">-</entry>
<entry num="37">&#183;</entry>
<entry num="38"> </entry>
<entry num="39">$</entry>
<entry num="40">/</entry>
<entry num="41">+</entry>
<entry num="42">%</entry>
</my:num2char>
<my:code2char>
<entry code="00">%U</entry>
<entry code="01">$A</entry>
<entry code="02">$B</entry>
<entry code="03">$C</entry>
<entry code="04">$D</entry>
<entry code="05">$E</entry>
<entry code="06">$F</entry>
<entry code="07">$G</entry>
<entry code="08">$H</entry>
<entry code="09">$I</entry>
<entry code="0A">$J</entry>
<entry code="0B">$K</entry>
<entry code="0C">$L</entry>
<entry code="0D">$M</entry>
<entry code="0E">$N</entry>
<entry code="0F">$O</entry>
<entry code="10">$P</entry>
<entry code="11">$Q</entry>
<entry code="12">$R</entry>
<entry code="13">$S</entry>
<entry code="06">$T</entry>
<entry code="15">$U</entry>
<entry code="16">$V</entry>
<entry code="17">$W</entry>
<entry code="18">$X</entry>
<entry code="19">$Y</entry>
<entry code="1A">$Z</entry>
<entry code="1B">%A</entry>
<entry code="1C">%B</entry>
<entry code="1D">%C</entry>
<entry code="1E">%D</entry>
<entry code="1F">%E</entry>
<entry code="20"> </entry>
<entry code="21">/A</entry>
<entry code="22">/B</entry>
<entry code="23">/C</entry>
<entry code="24">/D</entry>
<entry code="25">/E</entry>
<entry code="26">/F</entry>
<entry code="27">/G</entry>
<entry code="28">/H</entry>
<entry code="29">/I</entry>
<entry code="2A">/J</entry>
<entry code="2B">/K</entry>
<entry code="2C">/L</entry>
<entry code="2D">-</entry>
<entry code="2E">.</entry>
<entry code="2F">/O</entry>
<entry code="30">0</entry>
<entry code="31">1</entry>
<entry code="32">2</entry>
<entry code="33">3</entry>
<entry code="34">4</entry>
<entry code="35">5</entry>
<entry code="36">6</entry>
<entry code="37">7</entry>
<entry code="38">8</entry>
<entry code="39">9</entry>
<entry code="3A">/Z</entry>
<entry code="3B">%F</entry>
<entry code="3C">%G</entry>
<entry code="3D">%H</entry>
<entry code="3E">%I</entry>
<entry code="3F">%J</entry>
<entry code="40">%V</entry>
<entry code="41">A</entry>
<entry code="42">B</entry>
<entry code="43">C</entry>
<entry code="44">D</entry>
<entry code="45">E</entry>
<entry code="46">F</entry>
<entry code="47">G</entry>
<entry code="48">H</entry>
<entry code="49">I</entry>
<entry code="4A">J</entry>
<entry code="4B">K</entry>
<entry code="4C">L</entry>
<entry code="4D">M</entry>
<entry code="4E">N</entry>
<entry code="4F">O</entry>
<entry code="50">P</entry>
<entry code="51">Q</entry>
<entry code="52">R</entry>
<entry code="53">S</entry>
<entry code="54">T</entry>
<entry code="55">U</entry>
<entry code="56">V</entry>
<entry code="57">W</entry>
<entry code="58">X</entry>
<entry code="59">Y</entry>
<entry code="5A">Z</entry>
<entry code="5B">%K</entry>
<entry code="5C">%L</entry>
<entry code="5D">%M</entry>
<entry code="5E">%N</entry>
<entry code="5F">%O</entry>
<entry code="60">%W</entry>
<entry code="61">+A</entry>
<entry code="62">+B</entry>
<entry code="63">+C</entry>
<entry code="64">+D</entry>
<entry code="65">+E</entry>
<entry code="66">+F</entry>
<entry code="67">+G</entry>
<entry code="68">+H</entry>
<entry code="69">+I</entry>
<entry code="6A">+J</entry>
<entry code="6B">+K</entry>
<entry code="6C">+L</entry>
<entry code="6D">+M</entry>
<entry code="6E">+N</entry>
<entry code="6F">+O</entry>
<entry code="70">+P</entry>
<entry code="71">+Q</entry>
<entry code="72">+R</entry>
<entry code="73">+S</entry>
<entry code="74">+T</entry>
<entry code="75">+U</entry>
<entry code="76">+V</entry>
<entry code="77">+W</entry>
<entry code="78">+X</entry>
<entry code="79">+Y</entry>
<entry code="7A">+Z</entry>
<entry code="7B">%P</entry>
<entry code="7C">%Q</entry>
<entry code="7D">%R</entry>
<entry code="7E">%S</entry>
<entry code="7F">%T</entry>
</my:code2char>
</xsl:stylesheet>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- ========================================================== -->
<!-- Appendix module. -->
<!-- Author: ZJX -->
<!-- Version: 1.0 -->
<!-- ========================================================== -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="2.0">
<xsl:template match="APPEND">
<xsl:if test="not (@CHG) or @CHG !='D'">
<xsl:choose>
<xsl:when test="//EOTK-HEADER">
<xsl:call-template name="eotkAppendixPageSet"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
<fo:block id="lastpage"/>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template name="eotkAppendixPageSet">
<fo:page-sequence master-reference="otk-otherpages" break-before="page" id="{generate-id()}" font-size="{$g_font_size}" font-family="{$g_font_family}">
<fo:static-content flow-name="xsl-region-before">
<fo:block xsl:use-attribute-sets="blockWrap">
<xsl:call-template name="otkFirstPageHeader"/>
</fo:block>
</fo:static-content>
<fo:static-content flow-name="xsl-region-after">
<fo:block xsl:use-attribute-sets="blockWrap">
<xsl:call-template name="otkCommonPageFooter"/>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<xsl:call-template name="appendixDisplay"/>
</fo:flow>
</fo:page-sequence>
</xsl:template>
<xsl:template name="appendixDisplay">
<fo:block xsl:use-attribute-sets="blockWrap" margin-top="4pt">
<xsl:call-template name="generateID"/>
<!--<xsl:call-template name="showRevMarker"/>-->
<xsl:apply-templates/>
</fo:block>
<fo:block id="lastpage"/>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
</xsl:stylesheet>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="2.0">
<xsl:template match="revst | REVST">
<fo:block>
<fo:change-bar-begin change-bar-class="b" change-bar-style="solid" change-bar-width="2pt" change-bar-color="black" change-bar-placement="outside"/>
</fo:block>
</xsl:template>
<xsl:template match="revend | REVEND">
<fo:block>
<fo:change-bar-end change-bar-class="b" change-bar-style="solid" change-bar-width="2pt" change-bar-color="black" change-bar-placement="outside"/>
</fo:block>
</xsl:template>
</xsl:stylesheet>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:dtm="http://syntext.com/Extensions/DocumentTypeMetadata-1.0"
extension-element-prefixes="dtm"
version="2.0">
<!-- Calculates the following cells' span mnemonic, removing
span related to current cell.
-->
<xsl:template name="calculate.following.spans">
<xsl:param name="colspan" select="1"/>
<xsl:param name="spans" select="''"/>
<xsl:choose>
<xsl:when test="$colspan &gt; 0">
<xsl:call-template name="calculate.following.spans">
<xsl:with-param name="colspan" select="$colspan - 1"/>
<xsl:with-param name="spans" select="substring-after($spans,':')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$spans"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="get-attribute">
<xsl:param name="element" select="."/>
<xsl:param name="attribute" select="''"/>
<xsl:for-each select="$element/@*">
<xsl:if test="local-name(.) = $attribute">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="copy-string">
<!-- returns 'count' copies of 'string' -->
<xsl:param name="string"/>
<xsl:param name="count" select="0"/>
<xsl:param name="result"/>
<xsl:choose>
<xsl:when test="$count&gt;0">
<xsl:call-template name="copy-string">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="result">
<xsl:value-of select="$result"/>
<xsl:value-of select="$string"/>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="blank.spans">
<xsl:param name="cols" select="1"/>
<xsl:if test="$cols &gt; 0">
<xsl:text>0:</xsl:text>
<xsl:call-template name="blank.spans">
<xsl:with-param name="cols" select="$cols - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="border">
<xsl:param name="side" select="'left'"/>
<xsl:attribute name="border-{$side}-width">
<xsl:value-of select="$table.cell.border.thickness"/>
</xsl:attribute>
<xsl:attribute name="border-{$side}-style">
<xsl:value-of select="$table.cell.border.style"/>
</xsl:attribute>
<xsl:attribute name="border-{$side}-color">
<xsl:value-of select="$table.cell.border.color"/>
</xsl:attribute>
</xsl:template>
<xsl:template name="generate.colgroup.raw">
<xsl:param name="cols" select="1"/>
<xsl:param name="count" select="1"/>
<xsl:choose>
<xsl:when test="$count>$cols"></xsl:when>
<xsl:otherwise>
<xsl:call-template name="generate.col.raw">
<xsl:with-param name="countcol" select="$count"/>
</xsl:call-template>
<xsl:call-template name="generate.colgroup.raw">
<xsl:with-param name="cols" select="$cols"/>
<xsl:with-param name="count" select="$count+1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="generate.colgroup">
<xsl:param name="cols" select="1"/>
<xsl:param name="count" select="1"/>
<xsl:choose>
<xsl:when test="$count>$cols"></xsl:when>
<xsl:otherwise>
<xsl:call-template name="generate.col">
<xsl:with-param name="countcol" select="$count"/>
<xsl:with-param name="colspecs" select="COLSPEC"/>
</xsl:call-template>
<xsl:call-template name="generate.colgroup">
<xsl:with-param name="cols" select="$cols"/>
<xsl:with-param name="count" select="$count+1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="generate.col.raw">
<!-- generate the table-column for column countcol -->
<xsl:param name="countcol">1</xsl:param>
<xsl:param name="colspecs" select="./colspec"/>
<xsl:param name="count">1</xsl:param>
<xsl:param name="colnum">1</xsl:param>
<xsl:choose>
<xsl:when test="$count>count($colspecs)">
<fo:table-column column-number="{$countcol}"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="colspec" select="$colspecs[$count=position()]"/>
<xsl:variable name="colspec.colnum">
<xsl:choose>
<xsl:when test="$colspec/@COLNUM">
<xsl:value-of select="$colspec/@COLNUM"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$colnum"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="colspec.colwidth">
<xsl:choose>
<xsl:when test="$colspec/@COLWIDTH">
<xsl:value-of select="$colspec/@COLWIDTH"/>
</xsl:when>
<xsl:otherwise>1*</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$colspec.colnum=$countcol">
<fo:table-column column-number="{$countcol}">
<xsl:attribute name="column-width">
<xsl:value-of select="$colspec.colwidth"/>
</xsl:attribute>
</fo:table-column>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="generate.col.raw">
<xsl:with-param name="countcol" select="$countcol"/>
<xsl:with-param name="colspecs" select="$colspecs"/>
<xsl:with-param name="count" select="$count+1"/>
<xsl:with-param name="colnum">
<xsl:choose>
<xsl:when test="$colspec/@COLNUM">
<xsl:value-of select="$colspec/@COLNUM + 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$colnum + 1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="generate.col">
<!-- generate the table-column for column countcol -->
<xsl:param name="countcol">1</xsl:param>
<xsl:param name="colspecs"/>
<xsl:param name="count">1</xsl:param>
<xsl:param name="colnum">1</xsl:param>
<xsl:choose>
<xsl:when test="$count>count($colspecs)">
<fo:table-column column-number="{$countcol}">
<xsl:variable name="colwidth">
<xsl:call-template name="calc.column.width"/>
</xsl:variable>
<xsl:message select="$colwidth">==1</xsl:message>
<xsl:if test="$colwidth != 'proportional-column-width(1)'">
<xsl:attribute name="column-width">
<xsl:value-of select="$colwidth"/>
</xsl:attribute>
</xsl:if>
</fo:table-column>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="colspec" select="$colspecs[$count=position()]"/>
<xsl:variable name="colspec.colnum">
<xsl:choose>
<xsl:when test="$colspec/@COLNUM">
<xsl:value-of select="$colspec/@COLNUM"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$colnum"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="colspec.colwidth">
<xsl:choose>
<xsl:when test="$colspec/@COLWIDTH">
<xsl:value-of select="$colspec/@COLWIDTH"/>
</xsl:when>
<xsl:otherwise>1*</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$colspec.colnum=$countcol">
<fo:table-column column-number="{$countcol}">
<xsl:variable name="colwidth">
<xsl:call-template name="calc.column.width">
<xsl:with-param name="colwidth">
<xsl:value-of select="$colspec.colwidth"/>
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:message select="$colwidth">==2</xsl:message>
<xsl:if test="$colwidth != 'proportional-column-width(1)'">
<xsl:attribute name="column-width">
<xsl:value-of select="$colwidth"/>
</xsl:attribute>
</xsl:if>
</fo:table-column>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="generate.col">
<xsl:with-param name="countcol" select="$countcol"/>
<xsl:with-param name="colspecs" select="$colspecs"/>
<xsl:with-param name="count" select="$count+1"/>
<xsl:with-param name="colnum">
<xsl:choose>
<xsl:when test="$colspec/@COLNUM">
<xsl:value-of select="$colspec/@COLNUM + 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$colnum + 1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="calc.column.width">
<xsl:param name="colwidth">1*</xsl:param>
<!-- Ok, the colwidth could have any one of the following forms: -->
<!-- 1* = proportional width -->
<!-- 1unit = 1.0 units wide -->
<!-- 1 = 1pt wide -->
<!-- 1*+1unit = proportional width + some fixed width -->
<!-- 1*+1 = proportional width + some fixed width -->
<xsl:variable name="lower-case-colwidth">
<xsl:call-template name="lower-case">
<xsl:with-param name="parameter" select="$colwidth"/>
</xsl:call-template>
</xsl:variable>
<xsl:text>proportional-column-width(</xsl:text>
<xsl:variable name="total-colwidth">
<xsl:variable name="width">
<xsl:for-each select="child::COLSPEC">
<xsl:choose>
<xsl:when test="@COLWIDTH">
<xsl:value-of select="concat(@COLWIDTH, '+')"/>
</xsl:when>
<xsl:otherwise>1*+</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:call-template name="lower-case">
<xsl:with-param name="parameter" select="$width"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<!-- 无 COLSPEC 标签时,设每列列宽相同。 modify by:ZJX-->
<xsl:when test="not(child::COLSPEC)">
<xsl:value-of>1</xsl:value-of>
</xsl:when>
<xsl:when test="contains($total-colwidth, '*')">
<xsl:variable name="proportional-colwidth">
<xsl:call-template name="output-tokens">
<xsl:with-param name="list" select="$total-colwidth"/>
<xsl:with-param name="separator">+</xsl:with-param>
<xsl:with-param name="type">P</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="fixed-colwidth">
<xsl:call-template name="output-tokens">
<xsl:with-param name="list" select="$total-colwidth"/>
<xsl:with-param name="separator">+</xsl:with-param>
<xsl:with-param name="type">F</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<!-- 表格总宽度:210mm - 2*10mm -3pt = 537pt -->
<xsl:variable name="X">
<xsl:value-of select="(537 - number($fixed-colwidth)) div number($proportional-colwidth)"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains($lower-case-colwidth, '+')">
<xsl:variable name="first-width">
<xsl:value-of select="normalize-space(substring-before($lower-case-colwidth, '+'))"/>
</xsl:variable>
<xsl:variable name="second-width">
<xsl:value-of select="normalize-space(substring-after($lower-case-colwidth, '+'))"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains($first-width, '*')">
<xsl:variable name="first-width-after-conversion">
<xsl:call-template name="proportional-measure">
<xsl:with-param name="colwidth" select="$first-width"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="second-width-after-conversion">
<xsl:call-template name="fixed-measure">
<xsl:with-param name="colwidth" select="$second-width"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="number($first-width-after-conversion) * number($X) + number($second-width-after-conversion)"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="first-width-after-conversion">
<xsl:call-template name="fixed-measure">
<xsl:with-param name="colwidth" select="$first-width"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="second-width-after-conversion">
<xsl:call-template name="proportional-measure">
<xsl:with-param name="colwidth" select="$second-width"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="number($first-width-after-conversion) + number($second-width-after-conversion) * number($X) "/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="not(contains($lower-case-colwidth, '+')) and contains($lower-case-colwidth, '*')">
<xsl:variable name="proportional-colwidth">
<xsl:call-template name="proportional-measure">
<xsl:with-param name="colwidth" select="$lower-case-colwidth"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="number($proportional-colwidth) * number($X)"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="fixed-measure">
<xsl:with-param name="colwidth" select="$lower-case-colwidth"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="fixed-measure">
<xsl:with-param name="colwidth" select="$lower-case-colwidth"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
<xsl:text>)</xsl:text>
</xsl:template>
<xsl:template name="output-tokens">
<xsl:param name="list"/>
<xsl:param name="separator"/>
<xsl:param name="type"/>
<xsl:variable name="newlist" select="concat($list, $separator)"/>
<xsl:variable name="first" select="substring-before($newlist, $separator)" />
<xsl:variable name="remaining" select="substring-after($newlist, $separator)" />
<xsl:variable name="current-width">
<xsl:choose>
<xsl:when test="contains($first, '*') and contains($type, 'P')">
<xsl:call-template name="proportional-measure">
<xsl:with-param name="colwidth" select="$first"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="not(contains($first, '*')) and contains($type, 'F')">
<xsl:call-template name="fixed-measure">
<xsl:with-param name="colwidth" select="$first"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of>0</xsl:value-of>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="next-width">
<xsl:choose>
<xsl:when test="substring-before($remaining, $separator) != ''">
<xsl:call-template name="output-tokens">
<xsl:with-param name="list" select="$remaining" />
<xsl:with-param name="separator" select="$separator" />
<xsl:with-param name="type" select="$type"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of>0</xsl:value-of>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="number($current-width) + number($next-width)"/>
</xsl:template>
<xsl:template name="proportional-measure">
<xsl:param name="colwidth"/>
<xsl:choose>
<xsl:when test="'*' = $colwidth">1</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-before($colwidth, '*')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="fixed-measure">
<xsl:param name="colwidth"/>
<xsl:choose>
<xsl:when test="contains($colwidth, 'cm')">
<xsl:variable name="width">
<xsl:value-of select="substring-before($colwidth, 'cm')"/>
</xsl:variable>
<xsl:value-of select="$width * 28"/>
</xsl:when>
<xsl:when test="contains($colwidth, 'mm')">
<xsl:variable name="width">
<xsl:value-of select="substring-before($colwidth, 'mm')"/>
</xsl:variable>
<xsl:value-of select="$width * 3"/>
</xsl:when>
<!-- " pi/pc " (picas) -->
<xsl:when test="contains($colwidth, 'pi') or contains($colwidth, 'pc')">
<xsl:variable name="width">
<xsl:value-of select="substring-before($colwidth, 'p')"/>
</xsl:variable>
<xsl:value-of select="$width * 12"/>
</xsl:when>
<!-- " in " (inches) -->
<xsl:when test="contains($colwidth, 'in')">
<xsl:variable name="width">
<xsl:value-of select="substring-before($colwidth, 'in')"/>
</xsl:variable>
<xsl:value-of select="$width * 72"/>
</xsl:when>
<!-- pixel, px = pt * DPI / 72) -->
<xsl:when test="contains($colwidth, 'px')">
<xsl:variable name="width">
<xsl:value-of select="substring-before($colwidth, 'px')"/>
</xsl:variable>
<xsl:value-of select="$width * 0.75"/>
</xsl:when>
<xsl:when test="contains($colwidth, 'em')">
<xsl:variable name="width">
<xsl:value-of select="substring-before($colwidth, 'em')"/>
</xsl:variable>
<xsl:value-of select="$width * 12"/>
</xsl:when>
<xsl:when test="contains($colwidth, 'pt')">
<xsl:variable name="width">
<xsl:value-of select="substring-before($colwidth, 'pt')"/>
</xsl:variable>
<xsl:value-of select="$width"/>
</xsl:when>
<!-- 无单位,默认为" pt " (points) -->
<xsl:otherwise>
<xsl:value-of select="$colwidth"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="lower-case">
<xsl:param name="parameter"/>
<xsl:variable name="lcletters">abcdefghijklmnopqrstuvwxyz</xsl:variable>
<xsl:variable name="ucletters">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>
<xsl:value-of select="translate($parameter,$ucletters,$lcletters)"/>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<!--Note-->
<xsl:template match="note | NOTE">
<xsl:choose>
<xsl:when test="//LMJC-HEADER">
<fo:block space-before="5pt" space-after="3pt" color="blue" >
<fo:list-block provisional-label-separation="5pt" provisional-distance-between-starts="70pt">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block font-weight="bold" text-decoration="underline">注意 NOTE :</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates select="*[not(self::EFFECT)]"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block space-before="5pt" space-after="3pt" color="blue" >
<xsl:variable name="needToShow">
<xsl:call-template name="showAncestorEFFECT"/>
</xsl:variable>
<xsl:if test="$needToShow='true'">
<xsl:apply-templates select="ancestor::*[child::EFFECT][1]/EFFECT"/>
</xsl:if>
<xsl:apply-templates select="EFFECT"/>
<fo:list-block provisional-label-separation="5pt" provisional-distance-between-starts="70pt">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block font-weight="bold" text-decoration="underline">注意 NOTE :</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates select="*[not(self::EFFECT)]"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--warning-->
<xsl:template match="warning | WARNING">
<fo:block space-before="5pt" space-after="3pt" color="red" text-transform="uppercase">
<xsl:variable name="needToShow">
<xsl:call-template name="showAncestorEFFECT"/>
</xsl:variable>
<xsl:if test="$needToShow='true'">
<xsl:apply-templates select="ancestor::*[child::EFFECT][1]/EFFECT"/>
</xsl:if>
<xsl:apply-templates select="EFFECT"/>
<fo:list-block provisional-label-separation="5pt" provisional-distance-between-starts="82pt">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block font-weight="bold" text-decoration="underline">
<fo:inline >警告 WARNING</fo:inline>:</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates select="*[not(self::EFFECT)]"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block>
</xsl:template>
<!--Caution-->
<xsl:template match="caution | CAUTION">
<fo:block space-before="5pt" space-after="3pt" color="#FF6A00">
<!-- text-transform="uppercase" -->
<xsl:variable name="needToShow">
<xsl:call-template name="showAncestorEFFECT"/>
</xsl:variable>
<xsl:if test="$needToShow='true'">
<xsl:apply-templates select="ancestor::*[child::EFFECT][1]/EFFECT"/>
</xsl:if>
<xsl:apply-templates select="EFFECT"/>
<fo:list-block provisional-label-separation="5pt" provisional-distance-between-starts="82pt">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block font-weight="bold" text-decoration="underline"><fo:inline >警戒 CAUTION</fo:inline>:</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates select="*[not(self::EFFECT)]"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block>
</xsl:template>
<xsl:template match="CHGDESC|GROUP|DELETED|REGULATORY|TFMATR">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="ASSODATA">
<!-- ASSODATA隐藏不展示 -->
</xsl:template>
</xsl:stylesheet>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!-- ========================================================== -->
<!-- Configuration for all doctypes stylesheet -->
<!-- ========================================================== -->
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!--水印-->
<xsl:param name="v_watermark">file:D:/sumsoars/jc/stylesheet/graphics/watermarks/</xsl:param>
<!--营运人logo-->
<xsl:param name="v_logo">file:D:/sumsoars/jc/stylesheet/graphics/logos/</xsl:param>
<!--其他图标-->
<xsl:param name="v_icon">file:D:/sumsoars/jc/stylesheet/graphics/icon/</xsl:param>
<!--图片位置-->
<xsl:variable name="graphics_dir" select="''"/>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<!-- ========================================================== -->
<!-- Configuration for all doctypes stylesheet -->
<!-- ========================================================== -->
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!--水印-->
<xsl:param name="v_watermark">file:/mnt/ftp/tdms/stylesheet/graphics/watermarks/</xsl:param>
<!--营运人logo-->
<!--<xsl:param name="v_logo">file:/mnt/ftp/tdms/stylesheet/graphics/logos/</xsl:param>-->
<xsl:param name="v_logo">file:D:/work/workspace/git/tdms-ho/ho-amro-tdms/resources/tdms/stylesheet/graphics/logos/</xsl:param>
<!--其他图标-->
<!--<xsl:param name="v_icon">file:/mnt/ftp/tdms/stylesheet/graphics/icon/</xsl:param>-->
<xsl:param name="v_icon">file:D:/work/workspace/git/tdms-ho/ho-amro-tdms/resources/tdms/stylesheet/graphics/icon/</xsl:param>
<!--图片位置-->
<xsl:variable name="graphics_dir" select="''"/>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:param name="is_all" select="true()"/>
<xsl:param name="is_cn" select="false()"/>
<xsl:param name="is_en" select="false()"/>
<xsl:template name="generateID">
<xsl:param name="node" select="."/>
<xsl:attribute name="id">
<xsl:choose>
<xsl:when test="$node/@UID">
<xsl:value-of select="$node/@UID"/>
</xsl:when>
<xsl:when test="$node/@ID">
<xsl:value-of select="$node/@ID"/>
</xsl:when>
<xsl:when test="$node/@KEY">
<xsl:value-of select="$node/@KEY"/>
</xsl:when>
<xsl:when test="$node/@FTNOTEID">
<xsl:value-of select="$node/@FTNOTEID"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="generate-id($node)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:template>
<xsl:template name="translate-value">
<xsl:param name="value"/>
<xsl:param name="spec_lang"/>
<xsl:param name="wrapped" select="false()"/>
<xsl:variable name="cn_value">
<xsl:choose>
<xsl:when test="$value = 'jc_major_title'">工作单</xsl:when>
<xsl:when test="$value = 'subtask_title'">子任务</xsl:when>
<xsl:when test="$value = 'effect_prefix'">** ON A/C: </xsl:when>
<xsl:when test="$value = 'task_title'">任务</xsl:when>
<xsl:when test="$value = 'warning_title'">警告</xsl:when>
<xsl:when test="$value = 'note_title'">注意</xsl:when>
<xsl:when test="$value = 'caution_title'">警戒</xsl:when>
<xsl:when test="$value = 'cb_panel'">面板</xsl:when>
<xsl:when test="$value = 'cb_designation'">说明</xsl:when>
<xsl:when test="$value = 'cb_fin'">功能号</xsl:when>
<xsl:when test="$value = 'cb_loc'">位置</xsl:when>
<xsl:when test="$value = 'signBtnTitle'">签名</xsl:when>
<xsl:when test="$value = 'inapplicable'">不适用</xsl:when>
<xsl:when test="$value = 'mandatory_mess'">您还有必填项未填写</xsl:when>
<xsl:when test="$value = 'invalid_sign_off'">作废</xsl:when>
<xsl:when test="$value = 'memo'">附加说明</xsl:when>
<xsl:when test="$value = 'confirm_signoff'">签名</xsl:when>
<xsl:when test="$value = 'confirm_invalid_signoff'">作废</xsl:when>
<xsl:when test="$value = 'cancel'">取消</xsl:when>
<xsl:when test="$value = 'signOff_status_label'">签名状态</xsl:when>
<xsl:when test="$value = 'SIGN_STATUS_SIGNED'">已签名</xsl:when>
<xsl:when test="$value = 'SIGN_STATUS_UNSIGNED'">未签名</xsl:when>
<xsl:when test="$value = 'SIGN_STATUS_INVALID'">作废</xsl:when>
<xsl:when test="$value = 'SIGN_STATUS_INAPPLICABLE'">不适用</xsl:when>
<xsl:when test="$value = 'jc_ac_title'">机型</xsl:when>
<xsl:when test="$value = 'jc_tail_title'">机号</xsl:when>
<xsl:when test="$value = 'jc-title'">标题</xsl:when>
<xsl:when test="$value = 'jc-no'">工卡号</xsl:when>
<xsl:when test="$value = 'jc-task'">任务号</xsl:when>
<xsl:when test="$value = 'jc-zone'">区域</xsl:when>
<xsl:when test="$value = 'jc-level'">工卡级别</xsl:when>
<xsl:when test="$value = 'jc-insp-level'">定检级别</xsl:when>
<xsl:when test="$value = 'jc-author'">编写</xsl:when>
<xsl:when test="$value = 'smjc-author'">编写/修订</xsl:when>
<xsl:when test="$value = 'jc-author-date'">编写时间</xsl:when>
<xsl:when test="$value = 'jc-skill'">工种</xsl:when>
<xsl:when test="$value = 'mpd-mh'">理论工时</xsl:when>
<xsl:when test="$value = 'jc-actualmh'">实际工时</xsl:when>
<xsl:when test="$value = 'jc-reviewer'">审核</xsl:when>
<xsl:when test="$value = 'jc-review-date'">审核时间</xsl:when>
<xsl:when test="$value = 'jc-refs'">参考文件</xsl:when>
<xsl:when test="$value = 'cmjc-refs'">依据文件/版本</xsl:when>
<xsl:when test="$value = 'manual-rev'">版本</xsl:when>
<xsl:when test="$value = 'cmjc-manual-rev'">工卡版本</xsl:when>
<xsl:when test="$value = 'jc-approver'">批准</xsl:when>
<xsl:when test="$value = 'smjc-approver'">批准</xsl:when>
<xsl:when test="$value = 'jc-approve-date'">批准时间</xsl:when>
<xsl:when test="$value = 'jc-effect'">适用性</xsl:when>
<xsl:when test="$value = 'jc-tool'">工具</xsl:when>
<xsl:when test="$value = 'jc-equipment'">设备</xsl:when>
<xsl:when test="$value = 'jc-material'">材料</xsl:when>
<xsl:when test="$value = 'jc-tool-info'">工具信息</xsl:when>
<xsl:when test="$value = 'jc-material-info'">工具/航材/航化耗材件号信息:</xsl:when>
<xsl:when test="$value = 'jc-hhhc-info'">航化/耗材信息</xsl:when>
<xsl:when test="$value = 'jc-part-no'">参引</xsl:when>
<xsl:when test="$value = 'jc-serial-no'">序号</xsl:when>
<xsl:when test="$value = 'cmjc-part-no'">件号</xsl:when>
<xsl:when test="$value = 'jc-part-desc'">名称/规格</xsl:when>
<xsl:when test="$value = 'jc-part-qn'">数量</xsl:when>
<xsl:when test="$value = 'jc-remark'">备注</xsl:when>
<xsl:when test="$value = 'jc-caution'">注意事项</xsl:when>
<xsl:when test="$value = 'task-no'">任务号</xsl:when>
<xsl:when test="$value = 'card-no'">工作单编号</xsl:when>
<xsl:when test="$value = 'cmp-name'">部件名称</xsl:when>
<xsl:when test="$value = 'sn'">序列号</xsl:when>
<xsl:when test="$value = 'mx-level'">修理级别</xsl:when>
<xsl:when test="$value = 'mx-info'">本次完成改装</xsl:when>
<xsl:when test="$value = 'main-equip'">主要测试设备</xsl:when>
<xsl:when test="$value = 'applic-parts'">适用件号</xsl:when>
<xsl:when test="$value = 'effect-date'">生效日期</xsl:when>
<xsl:when test="$value = 'inspection'">检测</xsl:when>
<xsl:when test="$value = 'repair'">修理</xsl:when>
<xsl:when test="$value = 'modify'">改装</xsl:when>
<xsl:when test="$value = 'overhaul'">翻修</xsl:when>
<xsl:when test="$value = 'other'">其他</xsl:when>
<xsl:when test="$value = 'jc-rev-no'">版本号</xsl:when>
<xsl:when test="$value = 'jc-rev-date'">版本日期</xsl:when>
<xsl:when test="$value = 'smjc-rev-date'">版本日期</xsl:when>
<xsl:when test="$value = 'page-no'">页 号</xsl:when>
<xsl:when test="$value = 'jc-date'">日期</xsl:when>
<xsl:when test="$value = 'jc-ex-unit'">执行单位</xsl:when>
<xsl:when test="$value = 'jc-station'">维修地点</xsl:when>
<xsl:when test="$value = 'jc-air-reg-no'">机号</xsl:when>
<xsl:when test="$value = 'jc-ref-manhour'">工时</xsl:when>
<xsl:when test="$value = 'jc-ac-manhour'">实际总工时</xsl:when>
<xsl:when test="$value = 'jc-flight-no'">航班号</xsl:when>
<xsl:when test="$value = 'collapse'">收缩</xsl:when>
<xsl:when test="$value = 'risk_title'">风险信息</xsl:when>
<xsl:when test="$value ='risk_point'">风险点</xsl:when>
<xsl:when test="$value ='risk_result'">案例及可能后果</xsl:when>
<xsl:when test="$value ='risk_precaution'">风险防范措施</xsl:when>
<xsl:when test="$value = 'cmjc_title'">部件工作单</xsl:when>
<xsl:when test="$value = 'eotk_title'">工程指令工作单</xsl:when>
<xsl:when test="$value = 'totk_title'">技术指令工作单</xsl:when>
<xsl:when test="$value = 'version_title'">版本</xsl:when>
<xsl:when test="$value = 'version_remark'">版本备注</xsl:when>
<xsl:when test="$value = 'license_no'">序列号</xsl:when>
<xsl:when test="$value = 'department'">地点</xsl:when>
<xsl:when test="$value = 'jc-sup'">补充信息</xsl:when>
<xsl:when test="$value = 'issue-reason'">说明</xsl:when>
<xsl:when test="$value = 'check-level-title'">本工作单是否包含必检内容</xsl:when>
<xsl:when test="$value = 'work-date'">工作日期</xsl:when>
<xsl:when test="$value = 'executive-unit'">执行单位</xsl:when>
<xsl:when test="$value = 'station'">航站</xsl:when>
<xsl:when test="$value = 'aircraft-reg-no'">飞机注册号</xsl:when>
<xsl:when test="$value = 'ref-mh'">参考总工时</xsl:when>
<xsl:when test="$value = 'act-mh'">实际总工时</xsl:when>
<xsl:when test="$value = 'rev-no'">修改号</xsl:when>
<xsl:when test="$value = 'rev-date'">修改日期</xsl:when>
<xsl:when test="$value = 'signoff_mech_header'">工作者/日期</xsl:when>
<xsl:when test="$value = 'signoff_insp_header'">检查者/日期</xsl:when>
<xsl:when test="$value = 'signoff_verify_header'">必检/日期</xsl:when>
<xsl:when test="$value = 'signoff_ndt_header'">NDT/日期</xsl:when>
<xsl:when test="$value = 'signoff_mech_header_lmjc'">工作者</xsl:when>
<xsl:when test="$value = 'signoff_insp_header_lmjc'">检查者</xsl:when>
<xsl:when test="$value = 'signoff_verify_header_lmjc'">必检</xsl:when>
<xsl:when test="$value = 'signoff_mech_header_drjc'">特检工作者/日期</xsl:when>
<xsl:when test="$value = 'signoff_verify_header_drjc'">特检检查者/日期</xsl:when>
<xsl:when test="$value = 'memo_title'">附加说明</xsl:when>
<xsl:when test="$value = 'reset_title'">作废说明</xsl:when>
<xsl:when test="$value = 'memo_user_name'">添加者</xsl:when>
<xsl:when test="$value = 'memo_add_time'">时间</xsl:when>
<xsl:when test="$value = 'rest_user_name'">添加者</xsl:when>
<xsl:when test="$value = 'zonlist_title'">区域列表</xsl:when>
<xsl:when test="$value = 'einlist_title'">设备列表</xsl:when>
<xsl:when test="$value = 'genRangeTitle'">飞机适用范围</xsl:when>
<xsl:when test="$value = 'plane_eff'">飞机适用性</xsl:when>
<xsl:when test="$value = 'catalog'">类别</xsl:when>
<xsl:when test="$value = 'ref_ch'">参考</xsl:when>
<xsl:when test="$value = 'model_no'">型号</xsl:when>
<xsl:when test="$value = 'verify-pn-change'">改装后件号是否变化</xsl:when>
<xsl:when test="$value = 'pn-after-modify'">改装后件号/型号</xsl:when>
<xsl:when test="$value ='prepared_date'">编写</xsl:when>
<xsl:when test="$value ='audited_date'">审核</xsl:when>
<xsl:when test="$value ='approved_date'">批准</xsl:when>
<xsl:when test="$value ='eo-insp-level'">检验等级</xsl:when>
<xsl:when test="$value ='access'">接近盖板</xsl:when>
<xsl:when test="$value ='responsible-person'">负责人</xsl:when>
<xsl:when test="$value ='jc-seq'">工卡序号</xsl:when>
<xsl:when test="$value ='ac-fsn'">机队序列号</xsl:when>
<xsl:when test="$value ='accomplished-by'">完工签署</xsl:when>
<xsl:when test="$value ='jc-ver'">工卡版本</xsl:when>
<xsl:when test="$value ='ref-ver-revdate'">参考文件版本及修订日期</xsl:when>
<xsl:when test="$value ='achieve-data'">完成日期</xsl:when>
<xsl:when test="$value ='jc-rii'">必检</xsl:when>
<xsl:when test="$value ='ref_mh'">参考工时</xsl:when>
<xsl:when test="$value = 'jc-proof'">校对</xsl:when>
<xsl:when test="$value = 'jc-proof-date'">校对时间</xsl:when>
<xsl:when test="$value ='lm_ref_mh'">参考工时</xsl:when>
<xsl:when test="$value ='lm_pr'">放行人员</xsl:when>
<xsl:when test="$value='drjc-jcno'">工卡号 /</xsl:when>
<xsl:when test="$value='drjc-ref-drno'">参考损伤报告:</xsl:when>
<xsl:when test="$value='drjc-rii'">是否必检 /</xsl:when>
<xsl:when test="$value='drjc-repeat'">重复性 /</xsl:when>
<xsl:when test="$value='drjc-ver'">工卡版本号 /</xsl:when>
<xsl:when test="$value ='drjc-writer'">编写 /</xsl:when>
<xsl:when test="$value ='drjc-proof'">校对 /</xsl:when>
<xsl:when test="$value ='drjc-audit'">审核 /</xsl:when>
<xsl:when test="$value ='drjc-ratify'">批准 /</xsl:when>
<xsl:when test="$value ='drjc-refs'">依据文件/版次</xsl:when>
<xsl:when test="$value ='drjc-class'">检查/修理类别</xsl:when>
<xsl:when test="$value ='drjc-complate'">完工签署</xsl:when>
<xsl:when test="$value ='drjc-complate-date'">完工日期</xsl:when>
<xsl:when test="$value = 'eojc-author'">编写</xsl:when>
<xsl:when test="$value = 'eojc-refs'">依据文件及版本</xsl:when>
<xsl:when test="$value ='qc-visa'">必检批准</xsl:when>
<xsl:when test="$value ='eo-man-hour'">实际工时(MH)</xsl:when>
<xsl:when test="$value = 'eojc_ac_title'">件号</xsl:when>
<xsl:when test="$value = 'eojc_tail_title'">序号</xsl:when>
<xsl:when test="$value ='eojc-achieve-data'">完成时间</xsl:when>
<xsl:when test="$value = 'eojc-no'">工卡编号</xsl:when>
<xsl:when test="$value = 'eojc-ver'">版次</xsl:when>
<xsl:when test="$value = 'eojc-issued_date'">颁发日期</xsl:when>
<xsl:when test="$value ='tojc-pn'">件号</xsl:when>
<xsl:when test="$value ='tojc-sn'">序号</xsl:when>
<xsl:when test="$value ='qecjc-installation-location'">装机位置</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="en_value">
<xsl:choose>
<xsl:when test="$value = 'jc_major_title'">WORK CARD</xsl:when>
<xsl:when test="$value = 'subtask_title'">Subtask</xsl:when>
<xsl:when test="$value = 'effect_prefix'">** ON A/C: </xsl:when>
<xsl:when test="$value = 'task_title'">TASK</xsl:when>
<xsl:when test="$value = 'warning_title'">WARNING</xsl:when>
<xsl:when test="$value = 'note_title'">NOTE</xsl:when>
<xsl:when test="$value = 'caution_title'">CAUTION</xsl:when>
<xsl:when test="$value = 'cb_panel'">Panel</xsl:when>
<xsl:when test="$value = 'cb_designation'">Designation</xsl:when>
<xsl:when test="$value = 'cb_fin'">FIN</xsl:when>
<xsl:when test="$value = 'cb_loc'">Location</xsl:when>
<xsl:when test="$value = 'signBtnTitle'">Sign</xsl:when>
<xsl:when test="$value = 'inapplicable'">Inapplicable</xsl:when>
<xsl:when test="$value = 'mandatory_mess'">Please fill the required fields!</xsl:when>
<xsl:when test="$value = 'invalid_sign_off'">Invalid Sign Off</xsl:when>
<xsl:when test="$value = 'memo'">Memo</xsl:when>
<xsl:when test="$value = 'confirm_signoff'">Sign</xsl:when>
<xsl:when test="$value = 'confirm_invalid_signoff'">Invalid</xsl:when>
<xsl:when test="$value = 'cancel'">Cancel</xsl:when>
<xsl:when test="$value = 'signOff_status_label'">Sign Off Status</xsl:when>
<xsl:when test="$value = 'SIGN_STATUS_SIGNED'">Signed</xsl:when>
<xsl:when test="$value = 'SIGN_STATUS_UNSIGNED'">Unsigned</xsl:when>
<xsl:when test="$value = 'SIGN_STATUS_INVALID'">Invalid</xsl:when>
<xsl:when test="$value = 'SIGN_STATUS_INAPPLICABLE'">Inapplicable</xsl:when>
<xsl:when test="$value = 'jc_ac_title'">A/C TYPE</xsl:when>
<xsl:when test="$value = 'jc_tail_title'">A/C REG</xsl:when>
<xsl:when test="$value = 'jc-title'">Title</xsl:when>
<xsl:when test="$value = 'jc-no'">JOB CARD No.</xsl:when>
<xsl:when test="$value = 'jc-task'">TASK No.</xsl:when>
<xsl:when test="$value = 'jc-zone'">ZONE</xsl:when>
<xsl:when test="$value = 'jc-level'">Level</xsl:when>
<xsl:when test="$value = 'jc-insp-level'">CHECK LEVEL</xsl:when>
<xsl:when test="$value = 'jc-author'">WRITTEN BY</xsl:when>
<xsl:when test="$value = 'smjc-author'">WRITTEN BY</xsl:when>
<xsl:when test="$value = 'jc-author-date'">WRITE DATE</xsl:when>
<xsl:when test="$value ='jc-skill'">Skill</xsl:when>
<xsl:when test="$value ='mpd-mh'">MH</xsl:when>
<xsl:when test="$value ='jc-actualmh'">Actual Man-Hours</xsl:when>
<xsl:when test="$value ='jc-reviewer'">AUDITED BY</xsl:when>
<xsl:when test="$value ='jc-review-date'">REVIEW DATE</xsl:when>
<xsl:when test="$value ='jc-refs'">REFERENCE DOC</xsl:when>
<xsl:when test="$value ='cmjc-refs'">Technical Data/Rev</xsl:when>
<xsl:when test="$value ='manual-rev'">Revision</xsl:when>
<!-- <xsl:when test="$value ='cmjc-manual-rev'">Worksheet Rev.</xsl:when> -->
<xsl:when test="$value ='cmjc-manual-rev'">Job Card Rev.</xsl:when>
<xsl:when test="$value ='jc-approver'">APPROVED BY</xsl:when>
<xsl:when test="$value ='smjc-approver'">APPROVED BY</xsl:when>
<xsl:when test="$value ='jc-approve-date'">APPROVE DATE</xsl:when>
<xsl:when test="$value ='jc-effect'">Effect</xsl:when>
<xsl:when test="$value ='jc-tool'">TOOL</xsl:when>
<xsl:when test="$value ='jc-equipment'">EQUIPMENT</xsl:when>
<xsl:when test="$value ='jc-material'">MATERIAL</xsl:when>
<xsl:when test="$value ='jc-tool-info'">Tools Information</xsl:when>
<xsl:when test="$value ='jc-material-info'">Materials Information</xsl:when>
<xsl:when test="$value ='jc-hhhc-info'">Expendable Parts Information</xsl:when>
<xsl:when test="$value ='jc-part-no'">REFERENCE</xsl:when>
<xsl:when test="$value ='jc-serial-no'">SERIAL NO</xsl:when>
<xsl:when test="$value ='cmjc-part-no'">P/N</xsl:when>
<xsl:when test="$value ='jc-part-desc'">Part Desc</xsl:when>
<xsl:when test="$value ='jc-part-qn'">Quantity</xsl:when>
<xsl:when test="$value ='jc-remark'">Remark</xsl:when>
<xsl:when test="$value ='jc-caution'">Caution</xsl:when>
<xsl:when test="$value ='task-no'">Tracking NO.</xsl:when>
<!-- <xsl:when test="$value ='card-no'">Worksheet No.</xsl:when>-->
<xsl:when test="$value ='card-no'">Job Card No.</xsl:when>
<xsl:when test="$value ='cmp-name'">Part Name</xsl:when>
<xsl:when test="$value ='sn'">S/N</xsl:when>
<xsl:when test="$value ='mx-level'">Maintenance Category</xsl:when>
<xsl:when test="$value ='mx-info'">Modification NO.</xsl:when>
<xsl:when test="$value ='main-equip'">Main test Equipment</xsl:when>
<xsl:when test="$value ='applic-parts'">P/N Applicable</xsl:when>
<xsl:when test="$value ='effect-date'">Valid from</xsl:when>
<xsl:when test="$value ='inspection'">Testing</xsl:when>
<xsl:when test="$value ='repair'">Repair</xsl:when>
<xsl:when test="$value ='modify'">Modify</xsl:when>
<xsl:when test="$value ='overhaul'">Overhaul</xsl:when>
<xsl:when test="$value ='other'">Other</xsl:when>
<xsl:when test="$value ='jc-rev-no'">VERSION NO.</xsl:when>
<xsl:when test="$value ='jc-rev-date'">VERSION DATE</xsl:when>
<xsl:when test="$value ='smjc-rev-date'">VERSION DATE</xsl:when>
<xsl:when test="$value ='page-no'">Page No.</xsl:when>
<xsl:when test="$value ='jc-date'">DATE</xsl:when>
<xsl:when test="$value ='jc-ex-unit'">Executive Unit</xsl:when>
<xsl:when test="$value ='jc-station'">MAINT STATION</xsl:when>
<xsl:when test="$value ='jc-air-reg-no'">A/C REG</xsl:when>
<xsl:when test="$value ='jc-ref-manhour'">MAN-HOURS</xsl:when>
<xsl:when test="$value ='jc-ac-manhour'">Actual Manhours</xsl:when>
<xsl:when test="$value ='jc-flight-no'">FLIGHT NO.</xsl:when>
<xsl:when test="$value ='collapse'">Collapse</xsl:when>
<xsl:when test="$value ='risk_title'"/>
<xsl:when test="$value ='cmjc_title'">Component Work Card</xsl:when>
<xsl:when test="$value ='eotk_title'">Engineer Order Task Card</xsl:when>
<xsl:when test="$value ='totk_title'">Technical Order Task Card</xsl:when>
<xsl:when test="$value ='version_title'">VERSION</xsl:when>
<xsl:when test="$value ='version_remark'">VERSION REMARK</xsl:when>
<xsl:when test="$value ='license_no'">License No</xsl:when>
<xsl:when test="$value ='department'">Department</xsl:when>
<xsl:when test="$value ='jc-sup'">Suplement</xsl:when>
<xsl:when test="$value ='issue-reason'">Reason Of Issue</xsl:when>
<xsl:when test="$value ='check-level-title'">INSP. REQUIRE</xsl:when>
<xsl:when test="$value ='work-date'">Date</xsl:when>
<xsl:when test="$value ='executive-unit'">Executive Unit</xsl:when>
<xsl:when test="$value ='station'">STATION</xsl:when>
<xsl:when test="$value ='aircraft-reg-no'">Aircraft Reg No</xsl:when>
<xsl:when test="$value ='ref-mh'">Ref Manhours</xsl:when>
<xsl:when test="$value ='act-mh'">Actual Manhours</xsl:when>
<xsl:when test="$value ='signoff_mech_header'">Per.By</xsl:when>
<xsl:when test="$value ='signoff_insp_header'">Insp.By</xsl:when>
<xsl:when test="$value ='signoff_verify_header'">RII.By</xsl:when>
<xsl:when test="$value ='signoff_ndt_header'">Ndt.By</xsl:when>
<xsl:when test="$value ='signoff_mech_header_lmjc'">Per.By</xsl:when>
<xsl:when test="$value ='signoff_insp_header_lmjc'">Insp.By</xsl:when>
<xsl:when test="$value ='signoff_verify_header_lmjc'">RII.By</xsl:when>
<xsl:when test="$value = 'signoff_mech_header_drjc'">Per.By</xsl:when>
<xsl:when test="$value ='signoff_insp_header_drjc'">Insp.By</xsl:when>
<xsl:when test="$value = 'signoff_verify_header_drjc'">RII.By</xsl:when>
<xsl:when test="$value ='memo_user_name'">Name</xsl:when>
<xsl:when test="$value ='memo_add_time'">Date</xsl:when>
<xsl:when test="$value ='memo_title'">Comment</xsl:when>
<xsl:when test="$value ='reset_title'">Reset Comment</xsl:when>
<xsl:when test="$value ='memo_user_name'">Creator</xsl:when>
<xsl:when test="$value ='memo_add_time'">Time</xsl:when>
<xsl:when test="$value ='rest_user_name'">Creator</xsl:when>
<xsl:when test="$value ='zonlist_title'">Zone List</xsl:when>
<xsl:when test="$value ='einlist_title'">Equipment List</xsl:when>
<xsl:when test="$value ='genRangeTitle'">AC EFFECTIVITY</xsl:when>
<xsl:when test="$value = 'model_no'">Model NO.</xsl:when>
<xsl:when test="$value = 'verify-pn-change'">PN change After Modification</xsl:when>
<xsl:when test="$value = 'pn-after-modify'">Modified PN/MN</xsl:when>
<xsl:when test="$value ='prepared_date'">Prepared by</xsl:when>
<xsl:when test="$value ='audited_date'">Reviewed by</xsl:when>
<xsl:when test="$value ='approved_date'">Approved by</xsl:when>
<xsl:when test="$value ='access'">ACCESS PANEL</xsl:when>
<xsl:when test="$value ='responsible-person'">Responsible person</xsl:when>
<xsl:when test="$value ='jc-seq'">JC SEQ No.</xsl:when>
<xsl:when test="$value ='ac-fsn'">A/C FSN</xsl:when>
<xsl:when test="$value ='accomplished-by'">ACCOMPLISHED BY</xsl:when>
<xsl:when test="$value ='jc-ver'">JC VER</xsl:when>
<xsl:when test="$value ='ref-ver-revdate'">REF VER &amp; REF REV DATE</xsl:when>
<xsl:when test="$value ='achieve-data'">ACCOMPLISHED DATE</xsl:when>
<xsl:when test="$value ='jc-rii'">RII</xsl:when>
<xsl:when test="$value ='ref_mh'">REFERENCE MH</xsl:when>
<xsl:when test="$value = 'jc-proof'">PROOFED BY</xsl:when>
<xsl:when test="$value = 'jc-proof-date'">PROOF DATE</xsl:when>
<xsl:when test="$value ='lm_ref_mh'">Ref. Man-Hours</xsl:when>
<xsl:when test="$value ='lm_pr'">DISPATCH PERSONNEL</xsl:when>
<xsl:when test="$value='drjc-jcno'">JOBCARD No:</xsl:when>
<xsl:when test="$value='drjc-ref-drno'">REF DR No:</xsl:when>
<xsl:when test="$value='drjc-rii'">RII</xsl:when>
<xsl:when test="$value='drjc-repeat'">Periodical</xsl:when>
<xsl:when test="$value='drjc-ver'">Job Card Rev Issued</xsl:when>
<xsl:when test="$value ='drjc-writer'">Compiled By</xsl:when>
<xsl:when test="$value ='drjc-proof'">Checked By</xsl:when>
<xsl:when test="$value ='drjc-audit'">Reviewed By</xsl:when>
<xsl:when test="$value ='drjc-ratify'">Approved By</xsl:when>
<xsl:when test="$value ='drjc-refs'">REF Documents/Rev Date</xsl:when>
<xsl:when test="$value ='drjc-class'">Class</xsl:when>
<xsl:when test="$value ='drjc-complate'">Certified by</xsl:when>
<xsl:when test="$value ='drjc-complate-date'">Date</xsl:when>
<xsl:when test="$value = 'eojc-author'">Written By</xsl:when>
<xsl:when test="$value ='eojc-refs'">REF DOC &#38; VER</xsl:when>
<xsl:when test="$value ='qc-visa'">QC VISA</xsl:when>
<xsl:when test="$value ='eo-man-hour'">MAN HOURS(MH)</xsl:when>
<xsl:when test="$value = 'eojc_ac_title'">PN</xsl:when>
<xsl:when test="$value = 'eojc_tail_title'">SN</xsl:when>
<xsl:when test="$value ='eojc-achieve-data'">ACCOMPLISHED DATE</xsl:when>
<xsl:when test="$value = 'eojc-no'">JC No.</xsl:when>
<xsl:when test="$value = 'eojc-ver'">Rev No.</xsl:when>
<xsl:when test="$value = 'eojc-issued_date'">Issued Date</xsl:when>
<xsl:when test="$value ='tojc-pn'">PN</xsl:when>
<xsl:when test="$value ='tojc-sn'">SN</xsl:when>
<xsl:when test="$value ='qecjc-installation-location'">INSTALLATION LOCATION</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$spec_lang and $spec_lang='cn'">
<xsl:value-of select="$cn_value"/>
</xsl:when>
<xsl:when test="$spec_lang and $spec_lang='en'">
<xsl:value-of select="$en_value"/>
</xsl:when>
<xsl:otherwise>
<xsl:if test="$is_all or $is_cn">
<xsl:choose>
<xsl:when test="$wrapped">
<fo:block>
<xsl:value-of select="$cn_value"/>
</fo:block>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$cn_value"/>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
<xsl:if test="$is_all and not($wrapped)">
<xsl:text> </xsl:text>
</xsl:if>
<xsl:if test="$is_all or $is_en">
<xsl:choose>
<xsl:when test="$wrapped">
<fo:block>
<xsl:value-of select="$en_value"/>
</fo:block>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$en_value"/>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="generatorWaterMark">
<xsl:variable name="watermark">
<xsl:value-of select="//WATERMARK"/>
</xsl:variable>
<xsl:if test="normalize-space($watermark) !=''">
<xsl:attribute name="background-repeat">no-repeat</xsl:attribute>
<xsl:attribute name="background-position">center</xsl:attribute>
<xsl:attribute name="background-image">
<xsl:value-of select="concat('url(',$v_watermark,$watermark,'.png)')"/>
</xsl:attribute>
</xsl:if>
</xsl:template>
<xsl:template name="insertOperators">
<xsl:choose>
<xsl:when test="//JC-OPERATORS/JC-OPERATOR">
<xsl:for-each select="//JC-OPERATORS/JC-OPERATOR">
<xsl:variable name="operatorCode" select="translate(.,$upperCase,$lowerCase)"/>
<!-- Number of JC-OPERATOR nodes -->
<xsl:variable name="operatorsCount" select="count(preceding-sibling::JC-OPERATOR) + 1 + count(following-sibling::JC-OPERATOR)"/>
<!-- Position of this JC-OPERATOR node -->
<xsl:variable name="operatorPosition" select="count(preceding-sibling::JC-OPERATOR) + 1"/>
<xsl:call-template name="operatorCode2logo">
<xsl:with-param name="operatorCode" select="$operatorCode"/>
<xsl:with-param name="operatorPosition" select="$operatorPosition"/>
<xsl:with-param name="operatorsCount" select="$operatorsCount"/>
</xsl:call-template>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="logoPath">
<xsl:value-of select="concat($v_logo,'ss.png')"/>
</xsl:variable>
<fo:table border="0pt" display-align="center" table-layout="fixed"
vertical-align="middle" width="100%">
<fo:table-body>
<fo:table-row>
<fo:table-cell display-align="center" text-align="center"
vertical-align="middle">
<fo:block text-align="center" display-align="center">
<fo:external-graphic width="40mm" content-width="scale-to-fit" content-height="15mm">
<xsl:attribute name="src">url(<xsl:value-of select="$logoPath"/>)</xsl:attribute>
</fo:external-graphic>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="insertOperators-lmjc">
<xsl:variable name="logoPath">
<xsl:value-of select="concat($v_logo,'ss.png')"/>
</xsl:variable>
<fo:table border="0pt" display-align="center" table-layout="fixed"
vertical-align="middle" width="100%">
<fo:table-body>
<fo:table-row>
<fo:table-cell display-align="center" text-align="center"
vertical-align="middle">
<fo:block text-align="center" display-align="center">
<fo:external-graphic width="60mm" content-width="scale-to-fit" content-height="15mm">
<xsl:attribute name="src">url(<xsl:value-of select="$logoPath"/>)</xsl:attribute>
</fo:external-graphic>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</xsl:template>
<xsl:template name="insertOperators-drjc">
<xsl:variable name="logoPath">
<xsl:value-of select="concat($v_logo,'ss.png')"/>
</xsl:variable>
<fo:table border="0pt" display-align="center" table-layout="fixed"
vertical-align="middle" width="100%">
<fo:table-body>
<fo:table-row>
<fo:table-cell display-align="center" text-align="center"
vertical-align="middle">
<fo:block text-align="center" display-align="center">
<fo:external-graphic width="60mm" content-width="scale-to-fit" content-height="15mm">
<xsl:attribute name="src">url(<xsl:value-of select="$logoPath"/>)</xsl:attribute>
</fo:external-graphic>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</xsl:template>
<xsl:template name="operatorCode2logo">
<xsl:param name="operatorCode"/>
<xsl:param name="operatorPosition"/>
<xsl:param name="operatorsCount"/>
<xsl:variable name="operatorStr">
<xsl:choose>
<xsl:when test="$operatorCode='uea'">uea</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'uea'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$operatorsCount = 1">
<fo:external-graphic width="40mm" content-width="scale-to-fit" content-height="15mm">
<xsl:attribute name="src">
url(<xsl:value-of select="concat($v_logo,translate($operatorStr,$upperCase,$lowerCase),'.png')"
/>)</xsl:attribute>
</fo:external-graphic>
</xsl:when>
<xsl:when test="$operatorPosition mod 2 = 0">
<fo:external-graphic width="19.5mm" content-width="scale-to-fit" content-height="4mm">
<xsl:attribute name="src">url(<xsl:value-of select="concat($v_logo, translate($operatorStr,$upperCase,$lowerCase),'.png')"/>)</xsl:attribute>
</fo:external-graphic>
<fo:block>
<xsl:text> </xsl:text>
</fo:block>
</xsl:when>
<xsl:otherwise>
<fo:external-graphic width="19.5mm" content-width="scale-to-fit" content-height="4mm">
<xsl:attribute name="src">url(<xsl:value-of select="concat($v_logo,translate($operatorStr,$upperCase,$lowerCase),'.png')"
/>)</xsl:attribute>
</fo:external-graphic>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="pText"/>
<xsl:param name="delimiter"/>
<xsl:choose>
<xsl:when test="contains($pText, $delimiter)">
<fo:block>
<xsl:value-of select="substring-before($pText, $delimiter)"/>
</fo:block>
<xsl:call-template name="tokenize">
<xsl:with-param name="pText" select="substring-after($pText, $delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<fo:block>
<xsl:value-of select="$pText"/>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="createExSignoffTable">
<xsl:param name="userName"/>
<xsl:param name="time"/>
<xsl:variable name="pos" select="count(preceding::JC-EX-SIGNOFF)"/>
<fo:block-container start-indent="0pt">
<fo:table width="100%">
<fo:table-column column-number="1" column-width="56%"/>
<fo:table-column column-number="2" column-width="21%"/>
<fo:table-column column-number="3" column-width="21%"/>
<fo:table-column column-number="4" column-width="2%"/>
<fo:table-body>
<fo:table-row text-align="center" height="10mm" display-align="center">
<fo:table-cell number-rows-spanned="2">
<fo:block/>
</fo:table-cell>
<fo:table-cell border="0.5pt solid black" number-rows-spanned="2">
<fo:block font-weight="bold" text-align="center">
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="'signoff_mech_header'"/>
<xsl:with-param name="wrapped" select="true()"/>
</xsl:call-template>
</fo:block>
</fo:table-cell>
<fo:table-cell border="0.5pt solid black" text-align="center">
<fo:block color="white" font-size="1pt">
<!-- <xsl:value-of select="$userName"/> -->
<xsl:value-of select="concat('risk_ex_signoff_',$pos+1)"/>
</fo:block>
</fo:table-cell>
<fo:table-cell number-rows-spanned="2">
<fo:block/>
</fo:table-cell>
</fo:table-row>
<fo:table-row font-size="6.5pt" height="4mm" display-align="center" text-align="center">
<fo:table-cell border="0.5pt solid black">
<fo:block>
<!--<xsl:value-of select="$time"/>-->
<xsl:value-of select="substring($time,1,10)"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:block-container>
</xsl:template>
<xsl:template name="showRevMarker">
<!--<xsl:if
test="name((preceding::REVST|REVEND)[1])='REVST' and name((following::REVST|REVEND)[1])='REVEND'">
<xsl:attribute name="background-color">yellow</xsl:attribute>
</xsl:if>-->
</xsl:template>
<xsl:template name="getEffGrpStr">
<xsl:param name="eff"/>
<xsl:choose>
<xsl:when test="string-length($eff) > 5">
<xsl:variable name="effBefore" select="substring($eff,1,6)"/>
<xsl:variable name="effAfter" select="substring($eff,7)"/>
<xsl:value-of select="concat(substring($effBefore,1,3),'-',substring($effBefore,4))"/>
<xsl:if test="string-length($effAfter) > 5">
<xsl:value-of select="', '"/>
</xsl:if>
<xsl:call-template name="getEffGrpStr">
<xsl:with-param name="eff" select="$effAfter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$eff"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="generateTaskTitle">
<xsl:param name="title"/>
<xsl:param name="node" select="."/>
<xsl:choose>
<xsl:when test="not($node/@CHAPNBR) or ($node/@CHAPNBR='99' and $node/@SECTNBR='99')">
<fo:block>
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="$title"/>
</xsl:call-template>
<xsl:text> </xsl:text>
<xsl:variable name="num"/>
<xsl:value-of select="$num"/>
</fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block>
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="$title"/>
</xsl:call-template>
<xsl:text> </xsl:text>
<xsl:choose>
<xsl:when test="$node/@CONFLTR!='' and $node/@CONFLTR!=' '">
<xsl:choose>
<xsl:when test="$node/@VARNBR!='' and $node/@VARNBR!='0'">
<xsl:value-of
select="concat($node/@CHAPNBR,'-',$node/@SECTNBR,'-',$node/@SUBJNBR,'-',$node/@FUNC,'-',$node/@SEQ,'-',$node/@CONFLTR,$node/@VARNBR)"
/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of
select="concat($node/@CHAPNBR,'-',$node/@SECTNBR,'-',$node/@SUBJNBR,'-',$node/@FUNC,'-',$node/@SEQ,'-',$node/@CONFLTR)"
/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$node/@VARNBR!='' and $node/@VARNBR!='0'">
<xsl:value-of
select="concat($node/@CHAPNBR,'-',$node/@SECTNBR,'-',$node/@SUBJNBR,'-',$node/@FUNC,'-',$node/@SEQ,'-',$node/@VARNBR)"
/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of
select="concat($node/@CHAPNBR,'-',$node/@SECTNBR,'-',$node/@SUBJNBR,'-',$node/@FUNC,'-',$node/@SEQ)"
/>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="showTitle">
<xsl:choose>
<xsl:when test="parent::CEP or (parent::TASK and count(preceding::TASK)=0) or parent::GRAPHIC or parent::SHEET">
</xsl:when>
<xsl:otherwise>
<fo:block font-weight="bold">
<xsl:apply-templates/>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="isArchive">
<xsl:choose>
<xsl:when test="$action = 'archive'">
<xsl:value-of select="'true'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'false'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="makeStepNum">
<xsl:choose>
<xsl:when test="@NUM">
<xsl:value-of select="@NUM"/>
<xsl:if test="normalize-space(@NUM)">
<xsl:text>.</xsl:text>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="preceding::CMJC-HEADER">
<xsl:number count="STEP" format="1." from="TOPIC" level="any"/>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="preceding::LMJC-HEADER and (contains(/TASK/TITLE/text(),'PRIOR-SUMMER') or contains(/TASK/TITLE/text(),'PRIOR-WINTER') or contains(/TASK/TITLE/text(),'EXTREME COLD'))">
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="makeSeqNum">
<xsl:with-param name="node" select="parent::*"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
<xsl:number count="STEP[not(@IF_LR='Y')]" format="1."/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="insertStar">
<xsl:variable name="star_graphic" select="concat($v_icon,'/star.jpg')"/>
<fo:external-graphic width="2mm" content-height="60%"
content-width="60%">
<xsl:attribute name="src">url(<xsl:value-of select="$star_graphic"/>)</xsl:attribute>
</fo:external-graphic>
</xsl:template>
<xsl:template name="insertCheckbox">
<xsl:param name="checked" select="false()"/>
<xsl:variable name="checkedbox_graphic" select="concat($v_icon,'checkbox_full.png')"/>
<xsl:variable name="uncheckedbox_graphic" select="concat($v_icon,'checkbox_empty.png')"/>
<xsl:choose>
<xsl:when test="$checked">
<fo:external-graphic width="4mm" content-height="10%" keep-with-next="always"
content-width="10%" vertical-align="bottom">
<xsl:attribute name="src">url(<xsl:value-of select="$checkedbox_graphic"/>)</xsl:attribute>
</fo:external-graphic>
</xsl:when>
<xsl:when test="@CHECKED='Y'">
<fo:external-graphic width="4mm" content-height="10%" keep-with-next="always"
content-width="10%" vertical-align="bottom">
<xsl:attribute name="src">url(<xsl:value-of select="$checkedbox_graphic"/>)</xsl:attribute>
</fo:external-graphic>
</xsl:when>
<xsl:when test="@*[translate(name(.),$upperCase,$lowerCase)='checked' and translate(.,$upperCase,$lowerCase)='true']">
<fo:external-graphic width="4mm" content-height="10%" keep-with-next="always"
content-width="10%" vertical-align="bottom">
<xsl:attribute name="src">url(<xsl:value-of select="$checkedbox_graphic"/>)</xsl:attribute>
</fo:external-graphic>
</xsl:when>
<xsl:otherwise>
<fo:external-graphic width="4mm" content-height="10%" keep-with-next="always"
content-width="10%" vertical-align="bottom">
<xsl:attribute name="src">url(<xsl:value-of select="$uncheckedbox_graphic"/>)</xsl:attribute>
</fo:external-graphic>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="br">
<xsl:param name="string"/>
<xsl:choose>
<xsl:when test="contains($string,'\n')">
<fo:block>
<xsl:value-of select="substring-before($string,'\n')"/>
</fo:block>
<xsl:call-template name="br">
<xsl:with-param name="string" select="substring-after($string,'\n')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<fo:block>
<xsl:value-of select="$string"/>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="BRN">
<fo:block color="#FFFFFF">
<xsl:text>-</xsl:text>
</fo:block>
</xsl:template>
<xsl:template name="isStandAlone">
<xsl:choose>
<xsl:when test="$action = 'standalone'">
<xsl:value-of select="'true'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'false'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="createRefIntUrl">
<xsl:param name="key"/>
<xsl:variable name="cutoffKey"
select="translate(normalize-space(substring-after($key,'TASK')),' ','')"/>
<xsl:call-template name="createRefIntHrefAtt">
<xsl:with-param name="id" select="normalize-space($cutoffKey)"/>
</xsl:call-template>
</xsl:template>
<!-- 定义 createRefIntUrlnoTask 函数-->
<xsl:template name="createRefIntUrlnoTask">
<!-- 获取key 的值-->
<xsl:param name="key"/>
<xsl:call-template name="createRefIntHrefAtt">
<!-- normalize-space($key) 清楚前后空格字符串 -->
<xsl:with-param name="id" select="normalize-space($key)"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="createRefIntHrefAtt">
<!-- 获取key 值-->
<xsl:param name="id"/>
<xsl:variable name="manual" select="'AMM'"/>
<!-- <xsl:variable name="mapping_id"
select="preceding::EOTK-HEADER/JC-REF-TASKS//AMM-TASK[CODE=$id]/FILE | preceding::SMJC-HEADER/JC-REF-TASKS//AMM-TASK[CODE=$id]/FILE"/>
<xsl:variable name="viewerBaseUrl">
<xsl:call-template name="viewerUrl"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="preceding::SMJC-HEADER or preceding::EOTK-HEADER">
<!-\-<xsl:value-of
select="concat($viewerBaseUrl,'/MRO/OpenDocument.do?model=',$model,'&amp;manual=',$manual,'&amp;id=',$mapping_id)"/>-\->
<xsl:value-of
select="concat($viewerBaseUrl,'/MRO/OpenDocument.do?model=',$model,'&amp;manual=',$manual,'&amp;id=',$mapping_id)"/>
</xsl:when>
</xsl:choose>-->
<xsl:variable name="manualUrl">
<xsl:call-template name="getManualUrl"/>
</xsl:variable>
<xsl:value-of select="concat('url(',$manualUrl,'&amp;id=',$id,')')"/>
</xsl:template>
<!-- 获取链接手册的URL地址 -->
<xsl:template name="getManualUrl">
<xsl:variable name="jcCustomerCode">
<xsl:value-of select="$customerCode"/>
</xsl:variable>
<xsl:variable name="jcAc">
<xsl:choose>
<xsl:when test="//SMJC-HEADER/JC-AC">
<xsl:value-of select="//SMJC-HEADER/JC-AC/text()"/>
</xsl:when>
<xsl:otherwise>A330</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="concat($viewer_url,'/tdms/pub/EnigmaPreview?customerCode=',$jcCustomerCode,'&amp;cmpFleet=',$jcAc)"/>
</xsl:template>
<xsl:template name="showCustomAtt">
<xsl:param name="show_att"/>
<xsl:param name="att_name"/>
<xsl:param name="att_suffix"/>
<xsl:param name="att_prefix"/>
<xsl:if test="translate($show_att,$upperCase,$lowerCase) = 'y'">
<fo:block>
<xsl:choose>
<xsl:when test="$att_name !=''">
<xsl:value-of select="concat($att_prefix,@*[name()=$att_name],$att_suffix)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($att_prefix,@SR-ID,$att_suffix)"/>
</xsl:otherwise>
</xsl:choose>
<xsl:text> </xsl:text>
</fo:block>
</xsl:if>
</xsl:template>
<xsl:template name="showEffect">
<xsl:param name="showEFF" select="false()"/>
<fo:block font-style="italic" font-family="sans-serif" font-weight="bold">
<xsl:if test="@MERGED='TRUE'">
<xsl:attribute name="background">yellow</xsl:attribute>
</xsl:if>
<xsl:choose>
<xsl:when test="@EFFTEXT and string-length(@EFFTEXT)&gt;0">
<xsl:text>** ON A/C :</xsl:text>
<xsl:value-of select="@EFFTEXT"/>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="parent::REFINT">
<fo:inline padding="0.5mm" color="red">
<xsl:variable name="eff_prefix">** ON A/C:</xsl:variable>
<xsl:variable name="effGrp">
<xsl:value-of select="translate(@EFFRG,' ','')"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$effGrp = '001999'">
<xsl:if test="$showEFF">
<xsl:value-of select="concat($eff_prefix,'ALL')"/>
</xsl:if>
<xsl:apply-templates select="SBEFFC"/>
<xsl:apply-templates select="SBEFF"/>
</xsl:when>
<xsl:otherwise>
<xsl:if test="$showEFF">
<xsl:value-of select="$eff_prefix"/>
<xsl:call-template name="getEffGrpStr">
<xsl:with-param name="eff" select="$effGrp"/>
</xsl:call-template>
</xsl:if>
<xsl:apply-templates select="SBEFFC"/>
<xsl:apply-templates select="SBEFF"/>
</xsl:otherwise>
</xsl:choose>
</fo:inline>
</xsl:when>
<xsl:when test="parent::ROW|parent::TABLE">
<fo:inline padding="0.5mm" color="red">
<xsl:variable name="eff_prefix">** ON A/C:</xsl:variable>
<xsl:variable name="effGrp">
<xsl:value-of select="translate(@EFFRG,' ','')"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$effGrp = '001999'">
<xsl:if test="$showEFF">
<xsl:value-of select="concat($eff_prefix,'ALL')"/>
</xsl:if>
<xsl:apply-templates select="SBEFFC"/>
<xsl:apply-templates select="SBEFF"/>
</xsl:when>
<xsl:when test="$effGrp != ''">
<xsl:if test="$showEFF">
<xsl:value-of select="$eff_prefix"/>
<xsl:call-template name="getEffGrpStr">
<xsl:with-param name="eff" select="$effGrp"/>
</xsl:call-template>
</xsl:if>
<xsl:apply-templates select="SBEFFC"/>
<xsl:apply-templates select="SBEFF"/>
</xsl:when>
</xsl:choose>
</fo:inline>
</xsl:when>
<xsl:otherwise>
<fo:block padding="0.5mm" color="red">
<xsl:variable name="eff_prefix">** ON A/C:</xsl:variable>
<xsl:variable name="effGrp">
<xsl:value-of select="translate(@EFFRG,' ','')"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$effGrp = '001999'">
<xsl:if test="$showEFF">
<xsl:value-of select="concat($eff_prefix,'ALL')"/>
</xsl:if>
<xsl:apply-templates select="SBEFFC"/>
<xsl:apply-templates select="SBEFF"/>
</xsl:when>
<xsl:otherwise>
<xsl:if test="$showEFF and $effGrp != ''">
<xsl:value-of select="$eff_prefix"/>
<xsl:call-template name="getEffGrpStr">
<xsl:with-param name="eff" select="$effGrp"/>
</xsl:call-template>
</xsl:if>
<xsl:apply-templates select="SBEFFC"/>
<xsl:apply-templates select="SBEFF"/>
</xsl:otherwise>
</xsl:choose>
</fo:block>
<xsl:if test="parent::TASK">
<xsl:if test="count(/TASK/ASSODATA/EINLST) > 0">
<fo:block margin-top="1mm">
<fo:inline font-weight="normal">
<xsl:text>FIN:</xsl:text>
</fo:inline>
<xsl:for-each select="/TASK/ASSODATA/EINLST/EINDATA">
<xsl:if test="EFFECT/@EFFRG = /TASK/EFFECT/@EFFRG">
<fo:inline font-weight="normal" color="blue" text-decoration="underline">
<xsl:value-of select="replace(EIN/text(),'-','')"/>
<xsl:text> </xsl:text>
</fo:inline>
</xsl:if>
</xsl:for-each>
</fo:block>
<xsl:for-each select="/TASK/ASSODATA/EINLST/EINDATA">
<xsl:if test="EFFECT/@EFFRG != /TASK/EFFECT/@EFFRG">
<xsl:variable name="firsteff">
<xsl:value-of select="EFFECT/@EFFRG"/>
</xsl:variable>
<xsl:variable name="lasteff">
<xsl:value-of select="preceding-sibling::EINDATA[1]/EFFECT/@EFFRG"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$firsteff != $lasteff">
<fo:block margin-top="2mm" color="red">
<xsl:text>** ON A/C </xsl:text>
<xsl:call-template name="getEffGrpStr">
<xsl:with-param name="eff">
<xsl:value-of select=" translate(./EFFECT/@EFFRG,' ','')"/>
</xsl:with-param>
</xsl:call-template>
</fo:block>
<fo:inline margin-top="2mm" font-weight="normal" color="blue" text-decoration="underline">
<xsl:value-of select="replace(EIN/text(),'-','')"/>
</fo:inline>
</xsl:when>
<xsl:otherwise>
<fo:inline margin-top="2mm" font-weight="normal" color="blue" text-decoration="underline">
<xsl:value-of select="replace(EIN/text(),'-','')"/>
</fo:inline>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:for-each>
<fo:block margin-top="2mm" color="red">
<xsl:variable name="eff_prefix">** ON A/C:</xsl:variable>
<xsl:variable name="effGrp">
<xsl:value-of select="translate(@EFFRG,' ','')"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$effGrp = '001999'">
<xsl:if test="$showEFF">
<xsl:value-of select="concat($eff_prefix,'ALL')"/>
</xsl:if>
<xsl:apply-templates select="SBEFFC"/>
<xsl:apply-templates select="SBEFF"/>
</xsl:when>
<xsl:otherwise>
<xsl:if test="$showEFF and $effGrp != ''">
<xsl:value-of select="$eff_prefix"/>
<xsl:call-template name="getEffGrpStr">
<xsl:with-param name="eff" select="$effGrp"/>
</xsl:call-template>
</xsl:if>
<xsl:apply-templates select="SBEFFC"/>
<xsl:apply-templates select="SBEFF"/>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</xsl:if>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</xsl:template>
<xsl:template name="showAncestorEFFECT">
<xsl:choose>
<!-- TOEO工卡,JAVA中CONEFFECT转为EFFECT,造成某节点中有两个EFFECT节点,默认取第一个 -->
<xsl:when
test="not(child::EFFECT) and preceding-sibling::*[1]/EFFECT and not(contains(preceding-sibling::*[1]/EFFECT[1]/@EFFTEXT,'CEA ALL') or contains(preceding-sibling::*[1]/EFFECT[1]/@EFFRG,'001999'))"
>true</xsl:when>
<xsl:otherwise>false</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="showConEffect">
<fo:block font-style="italic" font-weight="bold" color="red" padding="0.5mm">
<xsl:variable name="eff_prefix">** CONF: </xsl:variable>
<xsl:value-of select="concat($eff_prefix,@EFFRG)"/>
</fo:block>
</xsl:template>
<xsl:template name="makeSeqNum">
<xsl:param name="node" select="."/>
<xsl:choose>
<xsl:when test="$node/preceding::CMJC-HEADER and self::TOPIC">
<xsl:value-of select="''"/>
</xsl:when>
<xsl:when test="$node/preceding::LMJC-HEADER and self::TOPIC and (contains(/TASK/TITLE/text(),'PRIOR-SUMMER') or contains(/TASK/TITLE/text(),'PRIOR-WINTER') or contains(/TASK/TITLE/text(),'EXTREME COLD'))">
<xsl:value-of select="''"/>
</xsl:when>
<xsl:when test="$node/parent::CEP or $node/parent::TASK or $node/parent::TFMATR/parent::CEP or $node/parent::TFMATR/parent::TASK">
<xsl:number count="PRETOPIC[parent::TFMATR/parent::CEP or parent::TFMATR/parent::TASK]|TOPIC[parent::CEP or parent::TASK]" format="1." level="any"/>
</xsl:when>
<xsl:when test="$node/parent::TOPIC and $node/self::TOPIC and (not($node/preceding::JC-TASK) or not($node/preceding::TASK)) and (not($node/following::JC-TASK) or not($node/following::TASK))">
<xsl:number count="PRETOPIC[parent::TFMATR/parent::TASK]/LIST1/L1ITEM | TOPIC[parent::TOPIC]" format="A." level="any"/>
</xsl:when>
<xsl:when test="$node/self::PRETOPIC and ($node/parent::TFMATR/parent::JC-TASK or $node/parent::TFMATR/parent::TASK )">
<xsl:number count="PRETOPIC[parent::TFMATR/parent::JC-TASK]|TOPIC[parent::JC-TASK]" format="A." level="any"/>
</xsl:when>
<xsl:when test="$node/self::TOPIC and ($node/parent::JC-TASK or $node/parent::TASK)">
<xsl:number count="PRETOPIC[parent::TFMATR/parent::JC-TASK]|TOPIC[parent::JC-TASK]" format="A." level="any"/>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="createBodyHeader">
<xsl:param name="seqNum"/>
<xsl:param name="name"/>
<xsl:param name="textType"></xsl:param>
<xsl:choose>
<xsl:when test="preceding::LMJC-HEADER or preceding::SMJC-HEADER or preceding::NRCJC-HEADER or preceding::QECJC-HEADER">
<fo:block font-size="10pt" font-weight="bold">
<fo:list-block provisional-label-separation="0.2em" provisional-distance-between-starts="2em">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>
<xsl:if test="$seqNum!=''">
<xsl:value-of select="$seqNum"/>
</xsl:if>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:if test="@MAV">
<fo:inline font-weight="bold">
<xsl:choose>
<xsl:when test="@MAV='MAV'">
<xsl:text>M AV </xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@MAV"/>
</xsl:otherwise>
</xsl:choose>
<xsl:text> </xsl:text>
</fo:inline>
</xsl:if>
<fo:inline>
<xsl:if test="@DM='Y'">
<xsl:text>&#160;&#160;</xsl:text>
<fo:external-graphic content-height="5mm">
<xsl:attribute name="src">
url(<xsl:value-of select="concat($v_icon,'dm.png')"/>)
</xsl:attribute>
</fo:external-graphic>
</xsl:if>
<xsl:if test="@RII='Y'">
<xsl:text>&#160;&#160;</xsl:text>
<fo:external-graphic content-height="5mm">
<xsl:attribute name="src">
url(<xsl:value-of select="concat($v_icon,'rii.png')"/>)
</xsl:attribute>
</fo:external-graphic>
</xsl:if>
</fo:inline>
<fo:inline text-decoration="underline">
<xsl:value-of select="$name"/>
</fo:inline>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block>
</xsl:when>
<xsl:when test="preceding::EOTK-HEADER or preceding::DRJC-HEADER or preceding::QECJC-HEADER">
<fo:block font-size="10pt">
<xsl:if test="$textType='title'">
<xsl:attribute name="font-weight">bold</xsl:attribute>
</xsl:if>
<fo:list-block provisional-label-separation="0.2em" provisional-distance-between-starts="2em">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>
<xsl:if test="$seqNum!=''">
<xsl:value-of select="$seqNum"/>
</xsl:if>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:value-of select="$name"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block font-size="9pt" font-weight="bold">
<fo:list-block provisional-label-separation="5pt" provisional-distance-between-starts="10pt">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>
<xsl:if test="$seqNum!=''">
<xsl:value-of select="$seqNum"/>
</xsl:if>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block text-decoration="underline">
<xsl:value-of select="$name"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="selectLangEle">
<xsl:param name="elementName"/>
<xsl:variable name="elementName2">
<xsl:choose>
<xsl:when test="$is_cn or $is_all">
<xsl:value-of select="concat($elementName, 'C')"/>
</xsl:when>
<xsl:when test="$is_en">
<xsl:value-of select="$elementName"/>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$is_all">
<xsl:value-of select="./*[name(.)=$elementName2]/node()"/>
<xsl:value-of select="./*[name(.)=$elementName]/node()"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="./*[name(.)=$elementName2]/node()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="split-string">
<xsl:param name="len"/>
<xsl:param name="value"/>
<xsl:variable name="reminding" select="substring($value, number($len) + 1)"/>
<xsl:variable name="newstring" select="substring($value, 1, number($len))"/>
<xsl:value-of select="concat(' ', $newstring)"/>
<xsl:choose>
<xsl:when test="string-length($reminding) > number($len)">
<xsl:call-template name="split-string">
<xsl:with-param name="len" select="$len"/>
<xsl:with-param name="value" select="$reminding"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(' ', $reminding)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name = "getRefsInfo">
<xsl:param name="refs"/>
<xsl:choose>
<xsl:when test="contains($refs,'|')">
<fo:block>
<xsl:value-of select="substring-before($refs,'|')"></xsl:value-of>
</fo:block>
<xsl:variable name="refsafter">
<xsl:value-of select=" substring-after ($refs,'|')"/>
</xsl:variable>
<xsl:call-template name="getRefsInfo">
<xsl:with-param name="refs">
<xsl:value-of select="$refsafter"></xsl:value-of>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<fo:block>
<xsl:value-of select="$refs"></xsl:value-of>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="utf-8"?>
<!-- ========================================================== -->
<!-- Graphic module. -->
<!-- Author: Bireturn -->
<!-- Version: 1.0 -->
<!-- ========================================================== -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format" version="2.0">
<xsl:param name="GRAPHICS_DIR" select="''"/>
<!-- variable GRAPHICS_DIR -->
<xsl:template match="GRAPHIC">
<xsl:if test="not (@CHG) or @CHG !='D'">
<xsl:choose>
<xsl:when test="ancestor::APPEND">
<xsl:call-template name="graphicDisplay"/>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="//SMJC-HEADER">
<xsl:call-template name="smjcGraphicPageSet"/>
</xsl:when>
<xsl:when test="//NRCJC-HEADER">
<xsl:call-template name="nrcjcGraphicPageSet"/>
</xsl:when>
<xsl:when test="//CMJC-HEADER">
<xsl:call-template name="cmjcGraphicPageSet"/>
</xsl:when>
<xsl:when test="//LMJC-HEADER">
<xsl:call-template name="lmjcGraphicPageSet"/>
</xsl:when>
<xsl:when test="//EOTK-HEADER or //TOTK-HEADER">
<xsl:call-template name="eotkGraphicPageSet"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="noheaderGraphicPageSet"/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template name="smjcGraphicPageSet">
<fo:page-sequence master-reference="smjc-otherpages" break-before="page"
id="{generate-id()}" font-size="{$g_font_size}" font-family="{$g_font_family}">
<fo:static-content flow-name="xsl-region-before">
<fo:block xsl:use-attribute-sets="blockWrap">
<xsl:call-template name="smjcCommonPageHeader"/>
</fo:block>
</fo:static-content>
<fo:static-content flow-name="xsl-region-after">
<fo:block xsl:use-attribute-sets="blockWrap">
<xsl:call-template name="smjcCommonPageFooter"/>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<xsl:call-template name="graphicDisplay"/>
</fo:flow>
</fo:page-sequence>
</xsl:template>
<xsl:template name="nrcjcGraphicPageSet">
<fo:page-sequence master-reference="nrcjc-otherpages" break-before="page"
id="{generate-id()}" font-size="{$g_font_size}" font-family="{$g_font_family}">
<fo:static-content flow-name="xsl-region-before">
<fo:block xsl:use-attribute-sets="blockWrap">
<xsl:call-template name="nrcjcCommonPageHeader"/>
</fo:block>
</fo:static-content>
<fo:static-content flow-name="xsl-region-after">
<fo:block xsl:use-attribute-sets="blockWrap">
<xsl:call-template name="nrcjcCommonPageFooter"/>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<xsl:call-template name="graphicDisplay"/>
</fo:flow>
</fo:page-sequence>
</xsl:template>
<xsl:template name="cmjcGraphicPageSet">
<fo:page-sequence master-reference="cmjc-otherpages" break-before="page"
id="{generate-id()}" font-size="{$g_font_size}" font-family="{$g_font_family}">
<fo:static-content flow-name="xsl-region-before" font-size="{$g_font_size}">
<fo:block xsl:use-attribute-sets="blockWrap">
<xsl:call-template name="cmjcCommonPageHeader"/>
</fo:block>
</fo:static-content>
<fo:static-content flow-name="xsl-region-after" font-size="{$g_font_size}">
<fo:block xsl:use-attribute-sets="blockWrap">
<xsl:call-template name="cmjcCommonPageFooter"/>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body" font-size="{$g_font_size}">
<xsl:call-template name="graphicDisplay"/>
</fo:flow>
</fo:page-sequence>
</xsl:template>
<xsl:template name="eotkGraphicPageSet">
<fo:page-sequence master-reference="otk-otherpages" break-before="page" id="{generate-id()}"
font-size="{$g_font_size}" font-family="{$g_font_family}">
<fo:static-content flow-name="xsl-region-before">
<fo:block xsl:use-attribute-sets="blockWrap">
<xsl:call-template name="otkFirstPageHeader"/>
</fo:block>
</fo:static-content>
<fo:static-content flow-name="xsl-region-after">
<fo:block xsl:use-attribute-sets="blockWrap">
<xsl:call-template name="otkCommonPageFooter"/>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<xsl:call-template name="graphicDisplay"/>
</fo:flow>
</fo:page-sequence>
</xsl:template>
<xsl:template name="lmjcGraphicPageSet">
<fo:page-sequence master-reference="lmjc-otherpages" break-before="page"
id="{generate-id()}" font-size="{$g_font_size}" font-family="{$g_font_family}">
<fo:static-content flow-name="xsl-region-before" font-size="{$g_font_size}">
<fo:block xsl:use-attribute-sets="blockWrap">
<xsl:call-template name="lmjcCommonHeaderTable"/>
</fo:block>
</fo:static-content>
<fo:static-content flow-name="xsl-region-after" font-size="{$g_font_size}">
<fo:block xsl:use-attribute-sets="blockWrap">
<xsl:call-template name="lmjcCommonPageFooter"/>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body" font-size="{$g_font_size}">
<xsl:call-template name="graphicDisplay"/>
</fo:flow>
</fo:page-sequence>
</xsl:template>
<xsl:template name="noheaderGraphicPageSet">
<fo:page-sequence master-reference="noheader-otherpages" break-before="page"
id="{generate-id()}" font-size="{$g_font_size}" font-family="{$g_font_family}">
<fo:static-content flow-name="xsl-region-before" font-size="{$g_font_size}">
<fo:block xsl:use-attribute-sets="blockWrap">
<xsl:call-template name="noheaderCommonPageHeader"/>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<xsl:call-template name="graphicDisplay"/>
</fo:flow>
</fo:page-sequence>
</xsl:template>
<xsl:template name="graphicDisplay">
<fo:block xsl:use-attribute-sets="blockWrap" margin-top="4pt">
<xsl:call-template name="generateID"/>
<xsl:call-template name="showRevMarker"/>
<xsl:apply-templates select="SHEET"/>
<xsl:apply-templates select="TITLE"/>
</fo:block>
<fo:block id="lastpage"/>
</xsl:template>
<xsl:template match="SHEET">
<!-- Start Variable for URL -->
<fo:block-container start-indent="0pt" space-before="2pt">
<fo:block space-before="1pt" text-align="center">
<xsl:variable name="url">
<xsl:value-of select="$graphics_dir"/>
</xsl:variable>
<xsl:variable name="gnbr">
<xsl:value-of select="@GNBR"/>
</xsl:variable>
<xsl:variable name="imageArea">
<xsl:value-of select="@IMGAREA"/>
</xsl:variable>
<xsl:variable name="fullPageWidth">
<xsl:choose>
<xsl:when test="/*[1]/*[1]/ROTATE = 'Y' or /*[1]/descendant::*[@ROTATE = '1']">
<xsl:value-of> 215mm </xsl:value-of>
</xsl:when>
<xsl:otherwise>
<xsl:value-of> 175mm </xsl:value-of>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="fullPageHeight">
<xsl:choose>
<xsl:when test="/*[1]/*[1]/ROTATE = 'Y' or /*[1]/descendant::*[@ROTATE = '1']">
<xsl:choose>
<xsl:when test="//SMJC-HEADER or //NRCJC-HEADER or //QECJC-HEADER or //TCJC-HEADER">
<xsl:value-of> 135mm </xsl:value-of>
</xsl:when>
<xsl:when test="//LMJC-HEADER">
<xsl:value-of> 130mm </xsl:value-of>
</xsl:when>
<xsl:when test="//EOTK-HEADER or //TOTK-HEADER">
<xsl:value-of> 120mm </xsl:value-of>
</xsl:when>
<xsl:otherwise>
<xsl:value-of> 135mm </xsl:value-of>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="//SMJC-HEADER">
<xsl:value-of> 220mm </xsl:value-of>
</xsl:when>
<xsl:when test="//QECJC-HEADER">
<xsl:value-of> 210mm </xsl:value-of>
</xsl:when>
<xsl:when test="//NRCJC-HEADER">
<xsl:value-of> 220mm </xsl:value-of>
</xsl:when>
<xsl:when test="//TCJC-HEADER">
<xsl:value-of> 220mm </xsl:value-of>
</xsl:when>
<xsl:when test="//CMJC-HEADER">
<xsl:value-of> 215mm </xsl:value-of>
</xsl:when>
<xsl:when test="//LMJC-HEADER">
<xsl:value-of> 210mm </xsl:value-of>
</xsl:when>
<xsl:when test="//EOTK-HEADER or //TOTK-HEADER">
<xsl:value-of> 200mm </xsl:value-of>
</xsl:when>
<xsl:otherwise>
<xsl:value-of> 230mm </xsl:value-of>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!--<xsl:attribute name="id"> <!-\- ancestor 选取当前结点的父辈节点 -\->
<xsl:value-of select="concat(ancestor::GRAPHIC/@KEY,'_',./@SHEETNBR)"></xsl:value-of>
</xsl:attribute>-->
<xsl:call-template name="generateID"/>
<fo:external-graphic text-align="center">
<!--
<xsl:choose>
<xsl:when test="IMGSIZE/@IMGWID and IMGSIZE/@IMGHGT">
<xsl:attribute name="scaling">non-uniform</xsl:attribute>
<xsl:attribute name="content-width">
<xsl:value-of select="concat(IMGSIZE/@IMGWID, IMGSIZE/@UNIT)"/>
</xsl:attribute>
<xsl:attribute name="width">
<xsl:value-of select="concat(IMGSIZE/@IMGWID, IMGSIZE/@UNIT)"/>
</xsl:attribute>
<xsl:attribute name="content-height">
<xsl:value-of select="concat(IMGSIZE/@IMGHGT, IMGSIZE/@UNIT)"/>
</xsl:attribute>
<xsl:attribute name="height">
<xsl:value-of select="concat(IMGSIZE/@IMGHGT, IMGSIZE/@UNIT)"/>
</xsl:attribute>
<xsl:attribute name="keep-with-next.within-page">always</xsl:attribute>
</xsl:when>
<xsl:when test="IMGSIZE/@IMGWIDh">
<xsl:attribute name="scaling">uniform</xsl:attribute>
<xsl:attribute name="content-width">
<xsl:value-of select="concat(IMGSIZE/@IMGWID, IMGSIZE/@UNIT)"/>
</xsl:attribute>
<xsl:attribute name="width">
<xsl:value-of select="concat(IMGSIZE/@IMGWID, IMGSIZE/@UNIT)"/>
</xsl:attribute>
<xsl:attribute name="content-height">
<xsl:value-of select="'scale-to-fit'"/>
</xsl:attribute>
<xsl:attribute name="height">
<xsl:value-of select="$fullPageHeight"/>
</xsl:attribute>
<xsl:attribute name="keep-with-next.within-page">always</xsl:attribute>
</xsl:when>
<xsl:when test="IMGSIZE/@IMGHGT">
<xsl:attribute name="scaling">uniform</xsl:attribute>
<xsl:attribute name="content-width">
<xsl:value-of select="'scale-to-fit'"/>
</xsl:attribute>
<xsl:attribute name="width">
<xsl:value-of select="$fullPageWidth"/>
</xsl:attribute>
<xsl:attribute name="content-height">
<xsl:value-of select="concat(IMGSIZE/@IMGHGT, IMGSIZE/@UNIT)"/>
</xsl:attribute>
<xsl:attribute name="height">
<xsl:value-of select="concat(IMGSIZE/@IMGHGT, IMGSIZE/@UNIT)"/>
</xsl:attribute>
<xsl:attribute name="keep-with-next.within-page">always</xsl:attribute>
</xsl:when>
<xsl:otherwise>
-->
<!-- 图片宽度和高度设置为默认值 -->
<xsl:attribute name="scaling">uniform</xsl:attribute>
<xsl:attribute name="content-height">
<xsl:value-of select="'scale-to-fit'"/>
</xsl:attribute>
<xsl:attribute name="content-width">
<xsl:value-of select="'scale-to-fit'"/>
</xsl:attribute>
<xsl:attribute name="width">
<xsl:value-of select="$fullPageWidth"/>
</xsl:attribute>
<xsl:attribute name="height">
<xsl:value-of select="$fullPageHeight"/>
</xsl:attribute>
<xsl:attribute name="keep-with-next.within-page">always</xsl:attribute>
<!--
</xsl:otherwise>
</xsl:choose>
-->
<xsl:attribute name="src">url(<xsl:value-of select="concat($url, $gnbr)"/>)</xsl:attribute>
</fo:external-graphic>
<!-- titlec title -->
<fo:block text-align="center">
<!-- 统一规则定的TITLE -->
<!--
<xsl:choose>
<xsl:when test="child::TITLEC or child::TITLE">
<xsl:value-of select="TITLEC"/>
<fo:block>
<xsl:value-of select="TITLE"/>
</fo:block>
</xsl:when>
<xsl:when test="parent::GRAPHIC/TITLEC or parent::GRAPHIC/TITLE">
<xsl:choose>
<xsl:when test="not(preceding-sibling::SHEET or following-sibling::SHEET)">
<xsl:value-of select="parent::GRAPHIC/TITLEC"/>
<fo:block>
<xsl:value-of select="parent::GRAPHIC/TITLE"/>
</fo:block>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(parent::GRAPHIC/TITLEC, ' - Sheet ', @SHEETNBR)"/>
<fo:block>
<xsl:value-of select="concat(parent::GRAPHIC/TITLE, ' - Sheet ', @SHEETNBR)"/>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="grp" select="parent::GRAPHIC"/>
<xsl:variable name="graphic_pos"
select="count($grp/preceding-sibling::GRAPHIC) + 1"/>
<xsl:choose>
<xsl:when
test="not(preceding-sibling::SHEET or following-sibling::SHEET)">
<xsl:value-of select="concat('Figure', ' ', $graphic_pos)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of
select="concat('Figure', ' ', $graphic_pos, ' - Sheet ', @SHEETNBR)"
/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
-->
<!-- 参照旧版样式TITLE展示 -->
<xsl:choose>
<xsl:when test="//SMJC-HEADER or //NRCJC-HEADER or //TCJC-HEADER or //QECJC-HEADER or //LMJC-HEADER">
<xsl:choose>
<xsl:when test="TITLE">
<xsl:value-of select="TITLE"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(parent::GRAPHIC/TITLE,' - Sheet ',@SHEETNBR)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="//EOTK-HEADER or //TOTK-HEADER">
<xsl:variable name="sheetCount">
<xsl:value-of select="count(parent::GRAPHIC/SHEET)"/>
</xsl:variable>
<fo:block>
<xsl:choose>
<xsl:when test="@SHEETNBR">
<xsl:value-of select="concat(TITLE,' - Sheet ',@SHEETNBR)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(TITLE,' - Sheet ',count(preceding-sibling::SHEET)+1, '/', $sheetCount)"/>
</xsl:otherwise>
</xsl:choose>
</fo:block>
<fo:block>
<xsl:value-of select="parent::GRAPHIC/TITLEC"></xsl:value-of>
</fo:block>
<fo:block>
<xsl:value-of select="parent::GRAPHIC/TITLE"></xsl:value-of>
</fo:block>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="grp" select="parent::GRAPHIC"/>
<xsl:variable name="graphic_pos" select="count($grp/preceding-sibling::GRAPHIC)+1"/>
<xsl:value-of select="concat('Figure',' ',$graphic_pos,' - Sheet ',@SHEETNBR)"/>
</xsl:otherwise>
</xsl:choose>
</fo:block>
<!-- effect -->
<fo:block text-align="left" start-indent="3pt">
<!-- 适用性展示 -->
<xsl:variable name="needToShow">
<xsl:call-template name="showAncestorEFFECT"/>
</xsl:variable>
<xsl:if test="$needToShow = 'true'">
<fo:block>
<xsl:apply-templates select="ancestor::*[child::EFFECT][1]/EFFECT"/>
</fo:block>
</xsl:if>
<xsl:if test="child::EFFECT">
<xsl:apply-templates select="EFFECT"/>
</xsl:if>
</fo:block>
</fo:block>
<xsl:apply-templates select="GDESC"/>
</fo:block-container>
</xsl:template>
<xsl:template match="GDESC">
<fo:block margin-left="14pt" margin-right="6pt" space-before="5pt">
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="FTNOTE">
<fo:block space-before="2pt">
<fo:list-block provisional-label-separation="5pt" provisional-distance-between-starts="50pt">
<xsl:call-template name="generateID"/>
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>
<xsl:value-of>*[</xsl:value-of>
<xsl:number count="FTNOTE" from="TABLE" format="1"/>
<xsl:value-of>]</xsl:value-of>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block>
</xsl:template>
</xsl:stylesheet>
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format" version="2.0">
<xsl:template match="REFBLOCK">
<xsl:apply-templates select="EFFECT"/>
<fo:inline>
<xsl:choose>
<xsl:when test="child::REFINT|REFEXT|GRPHCREF">
<xsl:choose>
<xsl:when test="//SMJC-HEADER or //NRCJC-HEADER or //TCJC-HEADER or //QECJC-HEADER">
<xsl:choose>
<xsl:when test="(ancestor::PARA or ancestor::PARAC) and ancestor::ENTRY">
<xsl:variable name="eff">
<xsl:value-of select="REFINT[1]/EFFECT/@EFFRG"/>
</xsl:variable>
<xsl:variable name="effsbCount">
<xsl:value-of select="count(REFINT[1]/EFFECT/SBEFF) + count(REFINT[1]/EFFECT/SBEFFC)"/>
</xsl:variable>
<xsl:variable name="parenteff">
<xsl:value-of select="ancestor::SUBTASK/EFFECT/@EFFRG"/>
</xsl:variable>
<xsl:variable name="parenteffsbCount">
<xsl:value-of select="count(ancestor::SUBTASK/EFFECT/SBEFF) + count(ancestor::SUBTASK/EFFECT/SBEFFC)"/>
</xsl:variable>
<xsl:variable name="prerefblockeff">
<xsl:value-of select="preceding-sibling::REFBLOCK/REFINT[1]/EFFECT/@EFFRG"/>
</xsl:variable>
<xsl:variable name="prerefblockeffsbCount">
<xsl:value-of select="count(preceding-sibling::REFBLOCK/REFINT[1]/EFFECT/SBEFF) + count(preceding-sibling::REFBLOCK/REFINT[1]/EFFECT/SBEFFC)"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="not(preceding-sibling::REFBLOCK)">
<!--如果两个有效性不相同,则显示REFINT下面的有效性-->
<xsl:if test="$eff != $parenteff or $effsbCount != $parenteffsbCount">
<xsl:apply-templates select="REFINT[1]/EFFECT"/>
</xsl:if>
</xsl:when>
<xsl:when test="preceding-sibling::REFBLOCK">
<!--如果和前一个兄弟节点有效性不相同,则显示REFINT下面的有效性-->
<xsl:if test="$eff != $prerefblockeff or $effsbCount != $prerefblockeffsbCount">
<xsl:apply-templates select="REFINT[1]/EFFECT"/>
</xsl:if>
</xsl:when>
</xsl:choose>
<xsl:if test="text()[normalize-space(.) != '']">
<xsl:choose>
<xsl:when test="ancestor::PARAC">
<xsl:variable name="ref_ch">
<xsl:call-template name="translate-value">
<xsl:with-param name="spec_lang" select="ch"/>
<xsl:with-param name="value" select="'ref_ch'"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat('(', $ref_ch, ': AMM TASK ')"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>(Ref: AMM TASK </xsl:text>
</xsl:otherwise>
</xsl:choose>
<fo:inline>
<xsl:if test="@IS-DOUBL = 'Y'">
<xsl:attribute name="text-decoration">
<xsl:text>underline</xsl:text>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="text()[normalize-space(.) != '']"/>
</fo:inline>
<xsl:text>)</xsl:text>
<xsl:if test="($eff != $parenteff or $effsbCount != $parenteffsbCount) and ancestor::PARA[following-sibling::*]">
<xsl:apply-templates select=" ancestor::SUBTASK/EFFECT"/>
</xsl:if>
</xsl:if>
<xsl:for-each select="REFINT[preceding-sibling::REFINT]">
<xsl:variable name="eff">
<xsl:value-of select="EFFECT/@EFFRG"/>
</xsl:variable>
<xsl:variable name="parenteff">
<xsl:value-of select="ancestor::SUBTASK/EFFECT/@EFFRG"/>
</xsl:variable>
<xsl:if test="$eff != $parenteff">
<xsl:apply-templates select="EFFECT"/>
</xsl:if>
</xsl:for-each>
</xsl:when>
<!--<xsl:when test="ancestor::PARAC and ancestor::ENTRY">-->
<!--parac 不做处理-->
<!--</xsl:when>-->
<xsl:otherwise>
<xsl:if test="text()[normalize-space(.) != '']">
<xsl:choose>
<xsl:when test="ancestor::PARAC">
<xsl:variable name="ref_ch">
<xsl:call-template name="translate-value">
<xsl:with-param name="spec_lang" select="ch"/>
<xsl:with-param name="value" select="'ref_ch'"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat('(', $ref_ch, ': AMM TASK ')"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>(Ref: AMM TASK </xsl:text>
</xsl:otherwise>
</xsl:choose>
<fo:inline>
<xsl:if test="@IS-DOUBL = 'Y'">
<xsl:attribute name="text-decoration">
<xsl:text>underline</xsl:text>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="text()[normalize-space(.) != '']"/>
</fo:inline>
<xsl:text>)</xsl:text>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="REFINT|REFEXT|GRPHCREF"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:if test="text()[normalize-space(.) != '']">
<xsl:choose>
<xsl:when test="ancestor::PARAC">
<xsl:variable name="ref_ch">
<xsl:call-template name="translate-value">
<xsl:with-param name="spec_lang" select="ch"/>
<xsl:with-param name="value" select="'ref_ch'"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat('(', $ref_ch, ': ')"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>(Ref: </xsl:text>
</xsl:otherwise>
</xsl:choose>
<fo:inline>
<xsl:if test="@IS-DOUBL = 'Y'">
<xsl:attribute name="text-decoration">
<xsl:text>underline</xsl:text>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="text()[normalize-space(.) != '']"/>
</fo:inline>
<xsl:text>)</xsl:text>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</fo:inline>
</xsl:template>
<xsl:template match="REFINT | REFEXT | GRPHCREF">
<fo:inline>
<xsl:if test="not(parent::REFBLOCK or ancestor::ENTRY)">
<xsl:variable name="needToShow">
<xsl:call-template name="showAncestorEFFECT"/>
</xsl:variable>
<xsl:if test="$needToShow='true'">
<xsl:apply-templates select="ancestor::*[child::EFFECT][1]/EFFECT"/>
</xsl:if>
<xsl:apply-templates select="EFFECT"/>
</xsl:if>
<xsl:choose>
<xsl:when test="self::GRPHCREF and (//EOTK-HEADER or //TOTK-HEADER)">
<xsl:choose>
<xsl:when test="ancestor::PARAC">
<xsl:value-of select="text()[normalize-space(.) != '']"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="ID">
<xsl:value-of select="@REFID"/>
</xsl:variable>
<xsl:choose>
<xsl:when
test="@REFID and //*[@ID = $ID or @KEY = $ID or @FTNOTEID = $ID]">
<fo:basic-link color="blue" text-decoration="underline"
internal-destination="{@REFID}">
<xsl:value-of select="text()[normalize-space(.) != '']"/>
</fo:basic-link>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="text()[normalize-space(.) != '']"/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="ancestor::PARAC">
<xsl:variable name="ref_ch">
<xsl:call-template name="translate-value">
<xsl:with-param name="spec_lang" select="ch"/>
<xsl:with-param name="value" select="'ref_ch'"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains(text(),'参考')">
<xsl:text>(</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('(', $ref_ch, ': ')"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="self::REFINT and (contains($model,'33') or contains($model,'32')) and (preceding::SMJC-HEADER or preceding::NRCJC-HEADER or preceding::TCJC-HEADER or preceding::QECJC-HEADER)">
<xsl:text>AMM </xsl:text>
</xsl:if>
<xsl:value-of select="text()[normalize-space(.) != '']"/>
<xsl:variable name="REFSPL">
<xsl:value-of select="concat('(', @REFSPL, ')')" />
</xsl:variable>
<xsl:choose>
<xsl:when test="self::REFEXT and @REFSPL and not(contains(text(), @REFSPL))">
<xsl:value-of select="$REFSPL" />
</xsl:when>
</xsl:choose>
<xsl:text>)</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="contains(text(),'Refer')">
<xsl:text> (</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text> (Ref: </xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="self::REFINT and (contains($model,'33') or contains($model,'32')) and (preceding::SMJC-HEADER or preceding::NRCJC-HEADER or preceding::TCJC-HEADER or preceding::QECJC-HEADER)">
<xsl:text>AMM </xsl:text>
</xsl:if>
<xsl:variable name="ID">
<xsl:value-of select="@REFID"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="@REFID and //*[@ID = $ID or @KEY = $ID or @FTNOTEID = $ID]">
<fo:basic-link color="blue" text-decoration="underline"
internal-destination="{@REFID}">
<xsl:value-of select="text()[normalize-space(.) != '']"/>
</fo:basic-link>
</xsl:when>
<xsl:when test="self::REFINT and not(@REFID and //*[@ID = $ID or @KEY = $ID or @FTNOTEID = $ID])">
<xsl:variable name="ref_url">
<xsl:choose>
<xsl:when test="contains(text(),'TASK')">
<xsl:call-template name="createRefIntUrl">
<xsl:with-param name="key"
select="text()[normalize-space(.) != '']"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="createRefIntUrlnoTask">
<xsl:with-param name="key"
select="text()[normalize-space(.) != '']"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fo:basic-link color="blue" text-decoration="underline">
<xsl:attribute name="external-destination">
<xsl:value-of select="$ref_url"/>
</xsl:attribute>
<xsl:value-of select="text()[normalize-space(.) != '']"/>
</fo:basic-link>
</xsl:when>
<xsl:when test="self::REFEXT and translate(@REFMAN, $upperCase, $lowerCase) = 'amm'">
<xsl:variable name="ref_url">
<xsl:call-template name="createRefIntUrlnoTask">
<xsl:with-param name="key" select="@REFLOC"/>
</xsl:call-template>
</xsl:variable>
<fo:basic-link color="blue" text-decoration="underline">
<xsl:attribute name="external-destination">
<xsl:value-of select="$ref_url"/>
</xsl:attribute>
<xsl:value-of select="text()[normalize-space(.) != '']"/>
</fo:basic-link>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="text()[normalize-space(.) != '']"/>
</xsl:otherwise>
</xsl:choose>
<xsl:variable name="REFSPL">
<xsl:value-of select="concat('(', @REFSPL, ')')" />
</xsl:variable>
<xsl:choose>
<xsl:when test="self::REFEXT and @REFSPL and not(contains(text(), @REFSPL))">
<xsl:value-of select="$REFSPL" />
</xsl:when>
</xsl:choose>
<xsl:text>) </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</fo:inline>
</xsl:template>
<xsl:template match="PAN">
<fo:inline>
<xsl:call-template name="generateID"/>
<xsl:call-template name="showRevMarker"/>
<xsl:text> </xsl:text>
<xsl:value-of select="text()"/>
<xsl:text> </xsl:text>
</fo:inline>
</xsl:template>
<xsl:template match="EIN">
<fo:inline>
<xsl:call-template name="generateID"/>
<xsl:call-template name="showRevMarker"/>
<xsl:text> FIN </xsl:text>
<xsl:value-of select="replace(text(),'-','')"/>
</fo:inline>
</xsl:template>
<xsl:template match="EXTERNAL-LINKS">
<xsl:if test="count(child::EXTERNAL-LINK) > 0">
<fo:block margin="1mm" margin-left="2mm">
<xsl:text>参考文件:</xsl:text>
</fo:block>
</xsl:if>
<xsl:for-each select="EXTERNAL-LINK">
<fo:block margin="1mm" margin-left="2mm">
<xsl:choose>
<xsl:when test="@FILELINK and @FILELINK != ''">
<fo:basic-link color="blue" text-decoration="underline">
<xsl:attribute name="external-destination"> url(<xsl:value-of
select="@FILELINK"/>) </xsl:attribute>
<xsl:value-of select="text()"/>
</fo:basic-link>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="text()"/>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</xsl:for-each>
</xsl:template>
<xsl:template match="EQUNAME">
<fo:inline>
<xsl:text>(</xsl:text>
<xsl:apply-templates/>
<xsl:text>)</xsl:text>
</fo:inline>
</xsl:template>
<xsl:template name="showRevMarker">
<!--<xsl:if
test="name((preceding::REVST|REVEND)[1])='REVST' and name((following::REVST|REVEND)[1])='REVEND'">
<xsl:attribute name="background-color">yellow</xsl:attribute>
</xsl:if>-->
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="utf-8"?>
<!-- ========================================================== -->
<!-- list module. -->
<!-- This module is used to handle UNLIST and NUMLIST. -->
<!-- Version: 1.0 -->
<!-- ========================================================== -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format" version="2.0">
<!-- ========================================================== -->
<!--Match UNLIST by zdp -->
<!-- ========================================================== -->
<xsl:template match="UNLIST">
<!-- UNLIST 标签中 除 UNLITEM 外,还可以包含 WARNING CAUTION 和 NOTE。 modify by:ZJX -->
<xsl:if test="child::*[not(name()='UNLITEM')]">
<xsl:apply-templates select="*[not(name()='UNLITEM')]"/>
</xsl:if>
<fo:list-block provisional-label-separation="1.2em" provisional-distance-between-starts="2.5em" space-before="5pt">
<xsl:apply-templates select="UNLITEM"/>
</fo:list-block>
</xsl:template>
<!-- ========================================================== -->
<!--Match UNLITEM by zdp -->
<!-- ========================================================== -->
<xsl:template match="UNLITEM">
<xsl:variable name="BULLTYPE">
<xsl:value-of select="parent::UNLIST/@BULLTYPE"/>
</xsl:variable>
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>
<xsl:if test="$BULLTYPE ">
<xsl:choose>
<xsl:when test="preceding::LMJC-HEADER and count(ancestor::UNLIST) = 1">
<xsl:number format="(1)"/>
</xsl:when>
<xsl:when test="preceding::LMJC-HEADER and count(ancestor::UNLIST) = 2">
<xsl:number format="(a)"/>
</xsl:when>
<xsl:when test="preceding::LMJC-HEADER and count(ancestor::UNLIST) = 3">
<xsl:number format="(i)"/>
</xsl:when>
<xsl:when test="$BULLTYPE = 'BULLET'">
<xsl:text>&#x2022;</xsl:text>
</xsl:when>
<xsl:when test="$BULLTYPE = 'NDASH'">
<xsl:text>&#8211;</xsl:text>
</xsl:when>
<xsl:when test="$BULLTYPE = 'MDASH'">
<xsl:text>&#8212;</xsl:text>
</xsl:when>
<xsl:when test="$BULLTYPE = 'DIAMOND'">
<fo:inline font-family="Arial">
<xsl:text>&#x2666;</xsl:text>
</fo:inline>
</xsl:when>
<xsl:when test="$BULLTYPE = 'ASTERISK'">
<fo:inline font-family="Arial">
<xsl:text>*</xsl:text>
</fo:inline>
</xsl:when>
<xsl:when test="$BULLTYPE = 'DELTA'">
<xsl:text>&#916;</xsl:text>
</xsl:when>
<xsl:when test="$BULLTYPE = 'SQUARE'">
<xsl:text>&#9830;</xsl:text>
</xsl:when>
<xsl:when test="$BULLTYPE = 'NONE'">
<xsl:text/>
</xsl:when>
<xsl:otherwise>
<xsl:text>&#x2022;</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:if test="@MAV or @RII">
<fo:block font-weight="bold">
<xsl:choose>
<xsl:when test="@MAV='MAV'">
<xsl:text>M AV</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@MAV"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="@RII='Y'">
<xsl:text>&#160;&#160;</xsl:text>
<fo:external-graphic content-height="5mm">
<xsl:attribute name="src">
url(<xsl:value-of select="concat($v_icon,'rii.png')"/>)
</xsl:attribute>
</fo:external-graphic>
</xsl:if>
<xsl:if test="@DM='Y'">
<xsl:text>&#160;&#160;</xsl:text>
<fo:external-graphic content-height="5mm">
<xsl:attribute name="src">
url(<xsl:value-of select="concat($v_icon,'dm.png')"/>)
</xsl:attribute>
</fo:external-graphic>
</xsl:if>
</fo:block>
</xsl:if>
<xsl:apply-templates/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>
<!-- ========================================================== -->
<!--Match NUMLIST -->
<!-- ========================================================== -->
<xsl:template match="NUMLIST">
<!-- NUMLIST 标签中 只包含 UNLITEM。 -->
<fo:list-block provisional-label-separation="0.2em" provisional-distance-between-starts="2em" space-before="5pt">
<xsl:apply-templates/>
</fo:list-block>
</xsl:template>
<!-- ========================================================== -->
<!--Match NUMLITEM by zdp -->
<!-- ========================================================== -->
<xsl:template match="NUMLITEM">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<xsl:variable name="NUMTYPE">
<xsl:value-of select="parent::NUMLIST/@NUMTYPE"/>
</xsl:variable>
<fo:block>
<xsl:if test="$NUMTYPE">
<xsl:choose>
<xsl:when test=" $NUMTYPE = 'NNP'">
<xsl:number format="1"/>
<xsl:text>.</xsl:text>
</xsl:when>
<xsl:when test="$NUMTYPE = 'AUP'">
<xsl:number format="A"/>
<xsl:text>.</xsl:text>
</xsl:when>
<xsl:when test="$NUMTYPE = 'NNB'">
<xsl:number format="A"/>
<xsl:text>.</xsl:text>
</xsl:when>
<xsl:when test="$NUMTYPE = 'ALB'">
<xsl:number format="A"/>
<xsl:text>.</xsl:text>
</xsl:when>
<xsl:when test="$NUMTYPE = 'NNS'">
<xsl:number format="A"/>
<xsl:text>.</xsl:text>
</xsl:when>
<xsl:when test="$NUMTYPE = 'RUP'">
<xsl:number format="A"/>
<xsl:text>.</xsl:text>
</xsl:when>
<xsl:when test="$NUMTYPE = 'RLP'">
<xsl:number format="A"/>
<xsl:text>.</xsl:text>
</xsl:when>
<xsl:when test="$NUMTYPE = 'NNR'">
<xsl:number format="1"/>
<xsl:text>&#41;</xsl:text>
</xsl:when>
<xsl:when test="$NUMTYPE = 'AUR'">
<xsl:number format="A"/>
<xsl:text>.</xsl:text>
</xsl:when>
<xsl:when test="$NUMTYPE = 'ALR'">
<xsl:number format="a"/>
<xsl:text>.</xsl:text>
</xsl:when>
<xsl:when test="$NUMTYPE= 'AUR'">
<xsl:number format="A"/>
<xsl:text>.</xsl:text>
</xsl:when>
<xsl:when test="$NUMTYPE= 'ALR'">
<xsl:number format="a"/>
<xsl:text>&#41;</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:number format="1"/>
<xsl:text>.</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>
<xsl:template match="LIST1 | LIST2 | LIST3 | LIST4 | LIST5 | LIST6 | LIST7">
<fo:block space-before="6pt" space-after="6pt" keep-with-previous="always">
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<!-- <xsl:template match="STEP">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>
<xsl:number format="1"/><xsl:text>.</xsl:text>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template> -->
<!-- ========================================================== -->
<!-- Match LITEM -->
<!-- ========================================================== -->
<xsl:template match="L1ITEM | L2ITEM | L3ITEM | L4ITEM | L5ITEM | L6ITEM | L7ITEM">
<xsl:if test="not(contains(child::PARA[1], 'Referenced Information'))">
<fo:block space-before="2pt">
<xsl:call-template name="generateID"/>
<!-- 所有 warning 和 caution 放到 item 最开始 -->
<!--
<xsl:if test="child::WARNING or child::CAUTION">
<xsl:apply-templates select="WARNING|CAUTION"/>
</xsl:if>
-->
<fo:list-block provisional-label-separation="0.2em" provisional-distance-between-starts="2em">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block padding="0.1mm" width="120mm">
<xsl:choose>
<xsl:when test="@NUM">
<xsl:value-of select="@NUM"/>
<xsl:if test="normalize-space(@NUM)">
<xsl:text>.</xsl:text>
</xsl:if>
</xsl:when>
<!-- TOPIC 下嵌套有 JC-TASK 或 TOPIC -->
<xsl:when test="self::L1ITEM">
<xsl:choose>
<xsl:when test="not(preceding::SMJC-HEADER) and not(preceding::TCJC-HEADER) and not(preceding::EOTK-HEADER) and not(preceding::NRCJC-HEADER) and not(preceding::QECJC-HEADER)">
<xsl:number count="L1ITEM" format="(1)." from="STEP" level="any"/>
</xsl:when>
<xsl:when test="ancestor::SUBTASK and count(preceding::EOTK-HEADER) &gt; 0">
<xsl:number count="L1ITEM" format="A." from="SUBTASK" level="any"/>
</xsl:when>
<xsl:when test="ancestor::STEP and count(preceding::EOTK-HEADER) &gt; 0">
<xsl:number count="L1ITEM" format="A." from="STEP" level="any"/>
</xsl:when>
<xsl:when test="ancestor::STEP and count(preceding::SMJC-HEADER) &gt; 0">
<xsl:number count="L1ITEM" format="A." from="STEP" level="any"/>
</xsl:when>
<xsl:when test="ancestor::STEP and count(preceding::TCJC-HEADER) &gt; 0">
<xsl:number count="L1ITEM" format="A." from="STEP" level="any"/>
</xsl:when>
<xsl:when test="ancestor::STEP and count(preceding::NRCJC-HEADER) &gt; 0">
<xsl:number count="L1ITEM" format="A." from="STEP" level="any"/>
</xsl:when>
<xsl:when test="ancestor::STEP and count(preceding::QECJC-HEADER) &gt; 0">
<xsl:number count="L1ITEM" format="A." from="STEP" level="any"/>
</xsl:when>
<xsl:when test="ancestor::STEP and count(preceding::LMJC-HEADER) &gt; 0">
<xsl:number count="L1ITEM" format="(1)." from="STEP" level="any"/>
</xsl:when>
<!-- <xsl:when test="ancestor::PRETOPIC">
<xsl:number count="L1ITEM" format="(1)." from="PRETOPIC" level="any"/>
</xsl:when> -->
<xsl:when test="count(ancestor::TOPIC) = 2 or (ancestor::JC-TASK)">
<xsl:number count="L1ITEM" format="(1)." from="TOPIC" level="any"/>
</xsl:when>
<xsl:when test="ancestor::TOPIC and (//SMJC-HEADER or //NRCJC-HEADER or //TCJC-HEADER or //QECJC-HEADER)">
<xsl:variable name="itemcount">
<xsl:value-of>
<xsl:number count="L1ITEM" format="1" from="TOPIC" level="any"/>
</xsl:value-of>
</xsl:variable>
<xsl:variable name="itemposition">
<!--<xsl:choose>
<xsl:when test="$itemcount &gt; 8 and $itemcount &lt; 14">
<xsl:value-of select="$itemcount + 1"/>
</xsl:when>
<xsl:when test="$itemcount &gt; 13 and $itemcount &lt; 24">
<xsl:value-of select="$itemcount + 2"/>
</xsl:when>
<xsl:when test="$itemcount &gt; 23">
<xsl:value-of select="$itemcount + 3"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$itemcount"/>
</xsl:otherwise>
</xsl:choose>-->
<xsl:value-of select="$itemcount"/>
</xsl:variable>
<xsl:number value="$itemposition" format="A."/>
</xsl:when>
<xsl:when test="ancestor::TOPIC">
<xsl:number count="L1ITEM" format="A." from="TOPIC" level="any"/>
</xsl:when>
<xsl:when test="ancestor::PRETOPIC">
<xsl:number count="L1ITEM" format="A." from="PRETOPIC" level="any"/>
</xsl:when>
</xsl:choose>
</xsl:when>
<xsl:when test="self::L2ITEM">
<xsl:choose>
<xsl:when test="count(ancestor::TOPIC) = 2 or (ancestor::JC-TASK)">
<xsl:number count="L2ITEM" format="(a)." level="multiple"/>
</xsl:when>
<xsl:when test="ancestor::STEP and count(preceding::LMJC-HEADER) &gt; 0">
<xsl:number count="L2ITEM" format="(a)." from="STEP" level="any"/>
</xsl:when>
<xsl:when test="(ancestor::STEP)">
<xsl:number count="L2ITEM" format="(1)." from="L1ITEM" level="any"/>
</xsl:when>
<xsl:otherwise>
<xsl:number count="L2ITEM" format="(1)." level="multiple"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="self::L3ITEM">
<xsl:choose>
<xsl:when test="count(ancestor::TOPIC) = 2 or (ancestor::JC-TASK)">
<xsl:number count="L3ITEM" format="1)." level="multiple"/>
</xsl:when>
<xsl:when test="ancestor::STEP and count(preceding::LMJC-HEADER) &gt; 0">
<xsl:number count="L3ITEM" format="(i)." from="STEP" level="any"/>
</xsl:when>
<xsl:otherwise>
<xsl:number count="L3ITEM" format="(a)." level="multiple"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="self::L4ITEM">
<xsl:choose>
<xsl:when test="ancestor::STEP and count(preceding::EOTK-HEADER) &gt; 0 or count(preceding::LMJC-HEADER) &gt; 0">
<xsl:number count="L4ITEM" format="1)." from="LIST4" level="any"/>
</xsl:when>
<xsl:when test="count(ancestor::TOPIC) = 2 or (ancestor::JC-TASK)">
<xsl:number count="L4ITEM" format="a)." level="multiple"/>
</xsl:when>
<xsl:otherwise>
<xsl:number count="L4ITEM" format="1." level="multiple"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="self::L5ITEM">
<xsl:choose>
<xsl:when test="ancestor::STEP and count(preceding::EOTK-HEADER) &gt; 0 or count(preceding::LMJC-HEADER) &gt; 0">
<xsl:number count="L5ITEM" format="a)." from="LIST5" level="any"/>
</xsl:when>
<xsl:when test="count(ancestor::TOPIC) = 2 or (ancestor::JC-TASK)">
<xsl:number count="L5ITEM" format="(1)." level="multiple"/>
</xsl:when>
<xsl:otherwise>
<xsl:number count="L5ITEM" format="a" level="multiple"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="self::L6ITEM">
<xsl:choose>
<xsl:when test="count(ancestor::TOPIC) = 2 or (ancestor::JC-TASK)">
<xsl:number count="L6ITEM" format="(a)." level="multiple"/>
</xsl:when>
<xsl:otherwise>
<xsl:number count="L6ITEM" format="1." level="multiple"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="self::L7ITEM">
<xsl:choose>
<xsl:when test="count(ancestor::TOPIC) = 2 or (ancestor::JC-TASK)">
<xsl:number count="L7ITEM" format="1)." level="multiple"/>
</xsl:when>
<xsl:otherwise>
<xsl:number count="L7ITEM" format="i" level="multiple"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<!--
<xsl:apply-templates select="*[not(self::WARNING or self::CAUTION)]"/>
-->
<xsl:apply-templates select="*"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="wpDescriptor"/>
<xsl:param name="jcId"/>
<xsl:param name="logoBaseURL"/>
<xsl:param name="RouteId"/>
<xsl:param name="printScriptPath"/>
<!--
<xsl:param name="workPackageID">
<xsl:choose>
<xsl:when test="">
<xsl:value-of select=" "/>
</xsl:when>
<xsl:otherwise>
<xsl:text>N/A</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:param>
-->
<xsl:param name="jobCardID">
<xsl:value-of select="$jcId"/>
</xsl:param>
<!--
<xsl:param name="revDate">
<xsl:for-each select="$metaFileDoc/workpackage/job_card[@id=$jcId]">
<xsl:choose>
<xsl:when test="@revdate">
<xsl:value-of select="@revdate"/>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="parent::workpackage/@revdate">
<xsl:value-of select="parent::workpackage/@revdate"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>N/A</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:param>
<xsl:param name="issueDate">
<xsl:for-each select="$metaFileDoc/workpackage/job_card[@id=$jcId]">
<xsl:choose>
<xsl:when test="@issuedate">
<xsl:value-of select="@issuedate"/>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="parent::workpackage/@issuedate">
<xsl:value-of select="parent::workpackage/@issuedate"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>N/A</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:param>
<xsl:param name="layout">
<xsl:for-each select="$metaFileDoc/workpackage/job_card[@id=$jcId]">
<xsl:choose>
<xsl:when test="@layout">
<xsl:value-of select="@layout"/>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$metaFileDoc/workpackage/@layout">
<xsl:value-of select="$metaFileDoc/workpackage/@layout"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>N/A</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:param>
<xsl:param name="tailNumber">
<xsl:for-each select="$metaFileDoc/workpackage/job_card[@id=$jcId]">
<xsl:choose>
<xsl:when test="@tailnumber">
<xsl:value-of select="@tailnumber"/>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="parent::workpackage/@tailnumber">
<xsl:value-of select="parent::workpackage/@tailnumber"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>N/A</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:param>
<xsl:param name="model">
<xsl:for-each select="$metaFileDoc/workpackage/job_card[@id=$jcId]">
<xsl:choose>
<xsl:when test="@model">
<xsl:value-of select="/@model"/>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="parent::workpackage/@model">
<xsl:value-of select="parent::workpackage/@model"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>N/A</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:param>
-->
<!-- 调用样式传入参数 -->
<xsl:param name="dataImagesURL"></xsl:param>
<xsl:param name="action">preview</xsl:param>
<!--'standalone'、'archive'、'template'、'preview' 根据这个显示不同的样式或数据 -->
<xsl:param name="show_eff">Y</xsl:param>
<!-- 是否显示适用性,对于单机卡可能不显示适用性 -->
<xsl:param name="viewer_url"></xsl:param>
<!-- 查看手册或图片的链接,默认为生产环境地址 -->
<xsl:param name="show_ex_st">Y</xsl:param>
<!-- 是否显示附加签字点 -->
<xsl:param name="att_name"/>
<xsl:param name="att_prefix">S</xsl:param>
<!--部件卡为P,定检卡为S-->
<xsl:param name="att_suffix"/>
<xsl:param name="showNaBlow">N</xsl:param>
<xsl:param name="naTitle">N/A</xsl:param>
<xsl:param name="showNaAbove">Y</xsl:param>
<xsl:param name="tot_title"/>
<!-- 为空则EOTK,不为空则TOTK -->
<xsl:param name="tot_titlec"/>
<xsl:param name="barcode"/>
<!-- 条形码 -->
<xsl:param name="inspLevel"/>
<!-- <xsl:param name="watermark"/> -->
<!-- 水印 -->
<!--添加获取customer code 参数-->
<xsl:param name="customerCode">UEA</xsl:param>
<xsl:param name="model">
<xsl:if test="//SMJC-HEADER or //TCJC-HEADER or //NRCJC-HEADER or //QECJC-HEADER or //EOTK-HEADER or //LMJC-HEADER">
<xsl:value-of select="//JC-AC"/>
</xsl:if>
</xsl:param>
</xsl:stylesheet>
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:stbl="http://nwalsh.com/xslt/ext/com.nwalsh.saxon.Table"
xmlns:xtbl="com.nwalsh.xalan.Table"
xmlns:lxslt="http://xml.apache.org/xslt"
exclude-result-prefixes="stbl xtbl lxslt"
version='1.0'>
<!-- **********************************************************
tbl.xsl - transforms CALS tables into FOs
********************************************************** -->
<xsl:import href="tblparamsfo.xsl"/>
<xsl:attribute-set name="table.cell.padding">
<xsl:attribute name="margin-left">0pt</xsl:attribute>
<xsl:attribute name="margin-right">0pt</xsl:attribute>
<xsl:attribute name="padding">
<xsl:value-of select="$table.cell.padding.amount"/>
</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="table-reset-indents">
<xsl:attribute name="start-indent">0pt</xsl:attribute>
<xsl:attribute name="end-indent">0pt</xsl:attribute>
</xsl:attribute-set>
<xsl:param name="tablecolumns.extension" select="'1'"/>
<xsl:param name="use.extensions" select="'0'"/>
<xsl:template name="copy-string">
<!-- returns 'count' copies of 'string' -->
<xsl:param name="string"/>
<xsl:param name="count" select="0"/>
<xsl:param name="result"/>
<xsl:choose>
<xsl:when test="$count&gt;0">
<xsl:call-template name="copy-string">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="result">
<xsl:value-of select="$result"/>
<xsl:value-of select="$string"/>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="blank.spans">
<xsl:param name="cols" select="1"/>
<xsl:if test="$cols &gt; 0">
<xsl:text>0:</xsl:text>
<xsl:call-template name="blank.spans">
<xsl:with-param name="cols" select="$cols - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="calculate.following.spans">
<xsl:param name="colspan" select="1"/>
<xsl:param name="spans" select="''"/>
<xsl:choose>
<xsl:when test="$colspan &gt; 0">
<xsl:call-template name="calculate.following.spans">
<xsl:with-param name="colspan" select="$colspan - 1"/>
<xsl:with-param name="spans" select="substring-after($spans,':')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$spans"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="finaltd">
<xsl:param name="spans"/>
<xsl:param name="col" select="0"/>
<xsl:if test="$spans != ''">
<xsl:choose>
<xsl:when test="starts-with($spans,'0:')">
<xsl:call-template name="empty.table.cell">
<xsl:with-param name="colnum" select="$col"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
<xsl:call-template name="finaltd">
<xsl:with-param name="spans" select="substring-after($spans,':')"/>
<xsl:with-param name="col" select="$col+1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="sfinaltd">
<xsl:param name="spans"/>
<xsl:if test="$spans != ''">
<xsl:choose>
<xsl:when test="starts-with($spans,'0:')">0:</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-before($spans,':')-1"/>
<xsl:text>:</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:call-template name="sfinaltd">
<xsl:with-param name="spans" select="substring-after($spans,':')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="entry.colnum">
<xsl:param name="entry" select="."/>
<xsl:choose>
<xsl:when test="$entry/@spanname">
<xsl:variable name="spanname" select="$entry/@spanname"/>
<xsl:variable name="spanspec"
select="$entry/ancestor::tgroup/spanspec[@spanname=$spanname]"/>
<xsl:variable name="colspec"
select="$entry/ancestor::tgroup/colspec[@colname=$spanspec/@namest]"/>
<xsl:call-template name="colspec.colnum">
<xsl:with-param name="colspec" select="$colspec"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$entry/@colname">
<xsl:variable name="colname" select="$entry/@colname"/>
<xsl:variable name="colspec"
select="$entry/ancestor::tgroup/colspec[@colname=$colname]"/>
<xsl:call-template name="colspec.colnum">
<xsl:with-param name="colspec" select="$colspec"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$entry/@namest">
<xsl:variable name="namest" select="$entry/@namest"/>
<xsl:variable name="colspec"
select="$entry/ancestor::tgroup/colspec[@colname=$namest]"/>
<xsl:call-template name="colspec.colnum">
<xsl:with-param name="colspec" select="$colspec"/>
</xsl:call-template>
</xsl:when>
<!-- no idea, return 0 -->
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="colspec.colnum">
<xsl:param name="colspec" select="."/>
<xsl:choose>
<xsl:when test="$colspec/@colnum">
<xsl:value-of select="$colspec/@colnum"/>
</xsl:when>
<xsl:when test="$colspec/preceding-sibling::colspec">
<xsl:variable name="prec.colspec.colnum">
<xsl:call-template name="colspec.colnum">
<xsl:with-param name="colspec"
select="$colspec/preceding-sibling::colspec[1]"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$prec.colspec.colnum + 1"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="calculate.colspan">
<xsl:param name="entry" select="."/>
<xsl:variable name="spanname" select="$entry/@spanname"/>
<xsl:variable name="spanspec"
select="$entry/ancestor::tgroup/spanspec[@spanname=$spanname]"/>
<xsl:variable name="namest">
<xsl:choose>
<xsl:when test="@spanname">
<xsl:value-of select="$spanspec/@namest"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$entry/@namest"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="nameend">
<xsl:choose>
<xsl:when test="@spanname">
<xsl:value-of select="$spanspec/@nameend"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$entry/@nameend"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="scol">
<xsl:call-template name="colspec.colnum">
<xsl:with-param name="colspec"
select="$entry/ancestor::tgroup/colspec[@colname=$namest]"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="ecol">
<xsl:call-template name="colspec.colnum">
<xsl:with-param name="colspec"
select="$entry/ancestor::tgroup/colspec[@colname=$nameend]"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$namest != '' and $nameend != ''">
<xsl:choose>
<xsl:when test="$ecol &gt;= $scol">
<xsl:value-of select="$ecol - $scol + 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$scol - $ecol + 1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="calculate.rowsep">
<xsl:param name="entry" select="."/>
<xsl:param name="colnum" select="0"/>
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="$entry"/>
<xsl:with-param name="colnum" select="$colnum"/>
<xsl:with-param name="attribute" select="'rowsep'"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="calculate.colsep">
<xsl:param name="entry" select="."/>
<xsl:param name="colnum" select="0"/>
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="$entry"/>
<xsl:with-param name="colnum" select="$colnum"/>
<xsl:with-param name="attribute" select="'colsep'"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="inherited.table.attribute">
<xsl:param name="entry" select="."/>
<xsl:param name="row" select="$entry/ancestor-or-self::row[1]"/>
<xsl:param name="colnum" select="0"/>
<xsl:param name="attribute" select="'colsep'"/>
<xsl:param name="lastrow" select="0"/>
<xsl:param name="lastcol" select="0"/>
<xsl:variable name="tgroup" select="$row/ancestor::tgroup[1]"/>
<xsl:variable name="entry.value">
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$entry"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="row.value">
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$row"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="span.value">
<xsl:if test="$entry/@spanname">
<xsl:variable name="spanname" select="$entry/@spanname"/>
<xsl:variable name="spanspec"
select="$tgroup/spanspec[@spanname=$spanname]"/>
<xsl:variable name="span.colspec"
select="$tgroup/colspec[@colname=$spanspec/@namest]"/>
<xsl:variable name="spanspec.value">
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$spanspec"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="scolspec.value">
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$span.colspec"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$spanspec.value != ''">
<xsl:value-of select="$spanspec.value"/>
</xsl:when>
<xsl:when test="$scolspec.value != ''">
<xsl:value-of select="$scolspec.value"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:if>
</xsl:variable>
<xsl:variable name="namest.value">
<xsl:if test="$entry/@namest">
<xsl:variable name="namest" select="$entry/@namest"/>
<xsl:variable name="colspec"
select="$tgroup/colspec[@colname=$namest]"/>
<xsl:variable name="namest.value">
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$colspec"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$namest.value">
<xsl:value-of select="$namest.value"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:if>
</xsl:variable>
<xsl:variable name="tgroup.value">
<xsl:choose>
<!-- Special case to handle thead valign -->
<xsl:when test="$attribute='valign' and ancestor::thead/@valign">
<xsl:value-of select="ancestor::thead/@valign"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$tgroup"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="default.value">
<!-- rowsep and colsep can have defaults on the "table" wrapper and
ultimately on the frame setting for outside rules. Non-outside
rules are unaffected by the frame setting. Both rowsep and colsep
default to 1 on the table wrapper if otherwise unspecified. -->
<!-- handle those here, for everything else, the default is the tgroup value -->
<xsl:choose>
<xsl:when test="$tgroup.value != ''">
<xsl:value-of select="$tgroup.value"/>
</xsl:when>
<xsl:when test="$attribute = 'rowsep'">
<xsl:choose>
<xsl:when test="$tgroup/parent::*/@rowsep">
<xsl:value-of select="$tgroup/parent::*/@rowsep"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$attribute = 'colsep'">
<xsl:choose>
<xsl:when test="$tgroup/parent::*/@colsep">
<xsl:value-of select="$tgroup/parent::*/@colsep"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<!-- empty -->
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="frame.value">
<xsl:variable name="frame">
<xsl:choose>
<xsl:when test="$tgroup/parent::*/@frame">
<xsl:value-of select="$tgroup/parent::*/@frame"/>
</xsl:when>
<xsl:otherwise>all</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$attribute='rowsep'">
<xsl:choose>
<xsl:when test="$frame='all' or $frame='topbot' or $frame='bot' or $frame='ALL' or $frame='TOPBOT' or $frame='BOT'">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$attribute='colsep'">
<xsl:choose>
<xsl:when test="$frame='all' or $frame='sides' or $frame='ALL' or $frame='SIDES'">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$lastrow='1' and $attribute='rowsep'">
<xsl:value-of select="$frame.value"/>
</xsl:when>
<xsl:when test="$lastcol='1' and $attribute='colsep'">
<xsl:value-of select="$frame.value"/>
</xsl:when>
<xsl:when test="$entry.value != ''">
<xsl:value-of select="$entry.value"/>
</xsl:when>
<xsl:when test="$row.value != ''">
<xsl:value-of select="$row.value"/>
</xsl:when>
<xsl:when test="$span.value != ''">
<xsl:value-of select="$span.value"/>
</xsl:when>
<xsl:when test="$namest.value != ''">
<xsl:value-of select="$namest.value"/>
</xsl:when>
<xsl:when test="$colnum &gt; 0">
<xsl:variable name="calc.colvalue">
<xsl:call-template name="colnum.colspec">
<xsl:with-param name="colnum" select="$colnum"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$calc.colvalue != ''">
<xsl:value-of select="$calc.colvalue"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$default.value"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$default.value"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="colnum.colspec">
<xsl:param name="colnum" select="0"/>
<xsl:param name="attribute" select="'colname'"/>
<xsl:param name="colspecs" select="ancestor::tgroup/colspec"/>
<xsl:param name="count" select="1"/>
<xsl:choose>
<xsl:when test="not($colspecs) or $count &gt; $colnum">
<!-- nop -->
</xsl:when>
<xsl:when test="$colspecs[1]/@colnum">
<xsl:choose>
<xsl:when test="$colspecs[1]/@colnum = $colnum">
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$colspecs[1]"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="colnum.colspec">
<xsl:with-param name="colnum" select="$colnum"/>
<xsl:with-param name="attribute" select="$attribute"/>
<xsl:with-param name="colspecs"
select="$colspecs[position()&gt;1]"/>
<xsl:with-param name="count"
select="$colspecs[1]/@colnum+1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$count = $colnum">
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$colspecs[1]"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="colnum.colspec">
<xsl:with-param name="colnum" select="$colnum"/>
<xsl:with-param name="attribute" select="$attribute"/>
<xsl:with-param name="colspecs"
select="$colspecs[position()&gt;1]"/>
<xsl:with-param name="count" select="$count+1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="get-attribute">
<xsl:param name="element" select="."/>
<xsl:param name="attribute" select="''"/>
<xsl:for-each select="$element/@*">
<xsl:if test="local-name(.) = $attribute">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
<!-- ==================================================================== -->
<lxslt:component prefix="xtbl" xmlns:lxslt="http://xml.apache.org/xslt"
functions="adjustColumnWidths"/>
<!-- ==================================================================== -->
<xsl:template name="empty.table.cell">
<xsl:param name="colnum" select="0"/>
<xsl:variable name="lastrow">
<xsl:variable name="rows-spanned">
<xsl:choose>
<xsl:when test="@morerows">
<xsl:value-of select="@morerows+1"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="ancestor::thead">0</xsl:when>
<xsl:when test="ancestor::tfoot
and not(ancestor::row/following-sibling::row)">1</xsl:when>
<xsl:when test="not(ancestor::tfoot)
and ancestor::tgroup/tfoot">0</xsl:when>
<xsl:when test="not(ancestor::tfoot)
and not(ancestor::tgroup/tfoot)
and count(ancestor::row/following-sibling::row) &lt; $rows-spanned">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="lastcol">
<xsl:variable name="spanname" select="@spanname"/>
<xsl:variable name="spanspec"
select="ancestor::tgroup/spanspec[@spanname=$spanname]"/>
<xsl:variable name="nameend">
<xsl:choose>
<xsl:when test="@spanname">
<xsl:value-of select="$spanspec/@nameend"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@nameend"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="ecol">
<xsl:choose>
<xsl:when test="$nameend!=''">
<xsl:call-template name="colspec.colnum">
<xsl:with-param name="colspec"
select="ancestor::tgroup/colspec[@colname=$nameend]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$colnum"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$ecol &lt; ancestor::tgroup/@cols">0</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="rowsep">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="NOT-AN-ELEMENT-NAME"/>
<xsl:with-param name="row" select="ancestor-or-self::row[1]"/>
<xsl:with-param name="colnum" select="$colnum"/>
<xsl:with-param name="attribute" select="'rowsep'"/>
<xsl:with-param name="lastrow" select="$lastrow"/>
<xsl:with-param name="lastcol" select="$lastcol"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="colsep">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="NOT-AN-ELEMENT-NAME"/>
<xsl:with-param name="row" select="ancestor-or-self::row[1]"/>
<xsl:with-param name="colnum" select="$colnum"/>
<xsl:with-param name="attribute" select="'colsep'"/>
<xsl:with-param name="lastrow" select="$lastrow"/>
<xsl:with-param name="lastcol" select="$lastcol"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="context">
<xsl:choose>
<xsl:when test="../row/../thead">thead</xsl:when>
<xsl:when test="../row/../tfoot">tfoot</xsl:when>
<xsl:otherwise>tbody</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fo:table-cell text-align="center"
display-align="center"
xsl:use-attribute-sets="table.cell.padding" border="medium solid black">
<xsl:call-template name="entry">
<xsl:with-param name="context" select="$context"/>
</xsl:call-template>
<xsl:call-template name="maybe-emit-cell-padding-attrs"/>
<xsl:call-template name="maybe-emit-rtf-direct-attrs"/>
<xsl:if test="$rowsep &gt; 0 and $lastrow = 0">
<xsl:call-template name="border">
<xsl:with-param name="side" select="'bottom'"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="$colsep &gt; 0 and $lastcol = 0">
<xsl:call-template name="border">
<xsl:with-param name="side" select="'right'"/>
</xsl:call-template>
</xsl:if>
<!-- ***** first added call to handle _cellfont ***** -->
<xsl:call-template name="just-after-table-cell-stag"/>
<!-- ***** end added line ***** -->
<!-- fo:table-cell should not be empty -->
<fo:block/>
<!-- ***** second added call to handle _cellfont ***** -->
<xsl:call-template name="just-before-table-cell-etag"/>
<!-- ***** end added line ***** -->
</fo:table-cell>
</xsl:template>
<!-- ==================================================================== -->
<xsl:template name="border">
<xsl:param name="side" select="'left'"/>
<!-- Maybe set border thickness from PubTbl PI -->
<xsl:variable name="border-thickness">
<xsl:choose>
<xsl:when test="ancestor-or-self::tgroup[1]/processing-instruction('PubTbl')[starts-with(.,'tgroup') and contains(.,' rth=')]">
<xsl:variable name="rth-pi"
select="ancestor-or-self::tgroup[1]/processing-instruction('PubTbl')[starts-with(.,'tgroup') and contains(.,' rth=')]"/>
<xsl:variable name="rth-pi2" select='substring-after($rth-pi," rth=")'/>
<xsl:value-of select="substring-before(substring($rth-pi2,2),'&quot;')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$table.border.thickness"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:attribute name="border-{$side}-width">
<xsl:value-of select="$border-thickness"/>
</xsl:attribute>
<xsl:attribute name="border-{$side}-style">
<xsl:value-of select="$table.border.style"/>
</xsl:attribute>
<xsl:attribute name="border-{$side}-color">
<xsl:value-of select="$table.border.color"/>
</xsl:attribute>
</xsl:template>
<!-- ==================================================================== -->
<!-- This next template is for Epic 4.3 TurboStyler compatibility
and should be deletable when Styler replaces TurboStyler
HOWEVER, it is still used by Styler for tables within headers/footers! -->
<xsl:template match="tgroup">
<fo:table table-layout="fixed" border-after-width.conditionality="retain" border-before-width.conditionality="retain" >
<xsl:if test="name(preceding-sibling::*[1])='title'">
<xsl:attribute name="keep-with-previous.within-page">always</xsl:attribute>
</xsl:if>
<xsl:choose>
<xsl:when test="count(preceding-sibling::tgroup)=0">
<xsl:call-template name="tgroup.first"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="tgroup.notfirst"/>
</xsl:otherwise>
</xsl:choose>
<!-- default the value of frame to all -->
<xsl:variable name="frame">
<xsl:choose>
<xsl:when test="../@frame">
<xsl:value-of select="../@frame"/>
</xsl:when>
<xsl:otherwise>all</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$frame='ALL' or $frame='all'">
<xsl:call-template name="border">
<xsl:with-param name="side" select="'left'"/>
</xsl:call-template>
<xsl:call-template name="border">
<xsl:with-param name="side" select="'right'"/>
</xsl:call-template>
<xsl:call-template name="border">
<xsl:with-param name="side" select="'top'"/>
</xsl:call-template>
<xsl:call-template name="border">
<xsl:with-param name="side" select="'bottom'"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$frame='BOTTOM' or $frame='bottom'">
<xsl:call-template name="border">
<xsl:with-param name="side" select="'bottom'"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$frame='SIDES' or $frame='sides'">
<xsl:call-template name="border">
<xsl:with-param name="side" select="'left'"/>
</xsl:call-template>
<xsl:call-template name="border">
<xsl:with-param name="side" select="'right'"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$frame='TOP' or $frame='top'">
<xsl:call-template name="border">
<xsl:with-param name="side" select="'top'"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$frame='TOPBOT' or $frame='topbot'">
<xsl:call-template name="border">
<xsl:with-param name="side" select="'top'"/>
</xsl:call-template>
<xsl:call-template name="border">
<xsl:with-param name="side" select="'bottom'"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$frame='NONE' or $frame='none'">
<xsl:attribute name="border-left-style">none</xsl:attribute>
<xsl:attribute name="border-right-style">none</xsl:attribute>
<xsl:attribute name="border-top-style">none</xsl:attribute>
<xsl:attribute name="border-bottom-style">none</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="border-left-style">none</xsl:attribute>
<xsl:attribute name="border-right-style">none</xsl:attribute>
<xsl:attribute name="border-top-style">none</xsl:attribute>
<xsl:attribute name="border-bottom-style">none</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<xsl:call-template name="tgroup-after-table-fo"/>
</fo:table>
</xsl:template>
<xsl:template match="tgroup" name="tgroup-after-table-fo" mode="already-emitted-table-fo">
<xsl:variable name="colspecs">
<xsl:choose>
<xsl:when test="$use.extensions != 0
and $tablecolumns.extension != 0">
<xsl:call-template name="generate.colgroup.raw">
<xsl:with-param name="cols" select="@cols"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="generate.colgroup">
<xsl:with-param name="cols" select="@cols"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$use.extensions != 0
and $tablecolumns.extension != 0">
<xsl:choose>
<xsl:when test="function-available('stbl:adjustColumnWidths')"
xmlns:stbl="http://nwalsh.com/xslt/ext/com.nwalsh.saxon.Table">
<xsl:copy-of select="stbl:adjustColumnWidths($colspecs)"/>
</xsl:when>
<xsl:when test="function-available('xtbl:adjustColumnWidths')"
xmlns:xtbl="com.nwalsh.xalan.Table">
<xsl:copy-of select="xtbl:adjustColumnWidths($colspecs)"/>
</xsl:when>
<xsl:otherwise>
<xsl:message terminate="yes">
<xsl:text>No adjustColumnWidths function available.</xsl:text>
</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$colspecs"/>
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates select="thead"/>
<xsl:apply-templates select="tfoot"/>
<xsl:apply-templates select="tbody"/>
</xsl:template>
<xsl:template match="colspec"/>
<xsl:template match="spanspec"/>
<xsl:template match="thead">
<xsl:variable name="tgroup" select="parent::*"/>
<fo:table-header xsl:use-attribute-sets="table-reset-indents">
<xsl:call-template name="thead"/>
<xsl:apply-templates select="row[1]">
<xsl:with-param name="spans">
<xsl:call-template name="blank.spans">
<xsl:with-param name="cols" select="../@cols"/>
</xsl:call-template>
</xsl:with-param>
</xsl:apply-templates>
<xsl:apply-templates select="isatrow[1]">
<xsl:with-param name="spans">
<xsl:call-template name="blank.spans">
<xsl:with-param name="cols" select="../@cols"/>
</xsl:call-template>
</xsl:with-param>
</xsl:apply-templates>
</fo:table-header>
</xsl:template>
<xsl:template match="tfoot">
<xsl:variable name="tgroup" select="parent::*"/>
<fo:table-footer xsl:use-attribute-sets="table-reset-indents">
<xsl:call-template name="tfoot"/>
<xsl:apply-templates select="row[1]">
<xsl:with-param name="spans">
<xsl:call-template name="blank.spans">
<xsl:with-param name="cols" select="../@cols"/>
</xsl:call-template>
</xsl:with-param>
</xsl:apply-templates>
<xsl:apply-templates select="isatrow[1]">
<xsl:with-param name="spans">
<xsl:call-template name="blank.spans">
<xsl:with-param name="cols" select="../@cols"/>
</xsl:call-template>
</xsl:with-param>
</xsl:apply-templates>
</fo:table-footer>
</xsl:template>
<xsl:template match="tbody">
<xsl:variable name="tgroup" select="parent::*"/>
<fo:table-body xsl:use-attribute-sets="table-reset-indents">
<xsl:call-template name="tbody"/>
<xsl:apply-templates select="row[1]">
<xsl:with-param name="spans">
<xsl:call-template name="blank.spans">
<xsl:with-param name="cols" select="../@cols"/>
</xsl:call-template>
</xsl:with-param>
</xsl:apply-templates>
</fo:table-body>
</xsl:template>
<xsl:template match="row">
<xsl:param name="spans"/>
<xsl:choose>
<xsl:when test="contains($spans, '0')">
<xsl:call-template name="normal-row">
<xsl:with-param name="spans" select="$spans"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!--
<xsl:if test="normalize-space(.//text()) != ''">
<xsl:message>Warning: overlapped row contains content!</xsl:message>
</xsl:if>
-->
<fo:table-row>
<xsl:comment> This row intentionally left blank </xsl:comment>
<fo:table-cell>
<fo:block/>
</fo:table-cell>
</fo:table-row>
<xsl:apply-templates select="following-sibling::row[1]">
<xsl:with-param name="spans">
<xsl:call-template name="consume-row">
<xsl:with-param name="spans" select="$spans"/>
</xsl:call-template>
</xsl:with-param>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="consume-row">
<xsl:param name="spans"/>
<xsl:if test="contains($spans,':')">
<xsl:value-of select="substring-before($spans,':') - 1"/>
<xsl:text>:</xsl:text>
<xsl:call-template name="consume-row">
<xsl:with-param name="spans" select="substring-after($spans,':')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="normal-row">
<xsl:param name="spans"/>
<fo:table-row>
<xsl:call-template name="row"/>
<!-- maybe set the height attribute from a PubTbl row rht value -->
<xsl:if test="ancestor-or-self::row[1]/processing-instruction('PubTbl')
[starts-with(.,'row') and contains(.,' rht=')]">
<xsl:attribute name="height">
<xsl:variable name="rht-pi"
select="ancestor-or-self::row[1]/processing-instruction('PubTbl')
[starts-with(.,'row') and contains(.,' rht=')]"/>
<xsl:variable name="rht-pi2" select='substring-after($rht-pi," rht=")'/>
<xsl:value-of select="substring-before(substring($rht-pi2,2),'&quot;')"/>
</xsl:attribute>
</xsl:if>
<!-- maybe set the break-before or keep-with-previous attribute
from a PubTbl row breakpenalty value -->
<xsl:if test="ancestor-or-self::row[1]/processing-instruction('PubTbl')
[starts-with(.,'row') and contains(.,' breakpenalty=')]">
<xsl:variable name="breakpenalty-pi"
select="ancestor-or-self::row[1]/processing-instruction('PubTbl')
[starts-with(.,'row') and contains(.,' breakpenalty=')]"/>
<xsl:variable name="breakpenalty-pi2" select='substring-after($breakpenalty-pi," breakpenalty=")'/>
<xsl:variable name="breakpenalty" select="substring-before(substring($breakpenalty-pi2,2),'&quot;')"/>
<xsl:choose>
<xsl:when test="$breakpenalty='10000'">
<xsl:attribute name="keep-with-previous">always</xsl:attribute>
</xsl:when>
<xsl:when test="$breakpenalty='-10000'">
<xsl:attribute name="break-before">column</xsl:attribute>
</xsl:when>
</xsl:choose>
</xsl:if>
<xsl:apply-templates select="entry[1]">
<xsl:with-param name="spans" select="$spans"/>
</xsl:apply-templates>
</fo:table-row>
<xsl:if test="following-sibling::row">
<xsl:variable name="nextspans">
<xsl:apply-templates select="entry[1]" mode="span">
<xsl:with-param name="spans" select="$spans"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:apply-templates select="following-sibling::row[1]">
<xsl:with-param name="spans" select="$nextspans"/>
</xsl:apply-templates>
</xsl:if>
</xsl:template>
<xsl:template match="entry" name="entry-template">
<xsl:param name="col" select="1"/>
<xsl:param name="spans"/>
<xsl:variable name="row" select="parent::row"/>
<xsl:variable name="group" select="$row/parent::*[1]"/>
<xsl:variable name="empty.cell" select="count(node()) = 0"/>
<xsl:variable name="named.colnum">
<xsl:call-template name="entry.colnum"/>
</xsl:variable>
<xsl:variable name="entry.colnum">
<xsl:choose>
<xsl:when test="$named.colnum &gt; 0">
<xsl:value-of select="$named.colnum"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$col"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="entry.colspan">
<xsl:choose>
<xsl:when test="@spanname or @namest">
<xsl:call-template name="calculate.colspan"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="following.spans">
<xsl:call-template name="calculate.following.spans">
<xsl:with-param name="colspan" select="$entry.colspan"/>
<xsl:with-param name="spans" select="$spans"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="lastrow">
<xsl:variable name="rows-spanned">
<xsl:choose>
<xsl:when test="@morerows">
<xsl:value-of select="@morerows+1"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="ancestor::thead">0</xsl:when>
<xsl:when test="ancestor::tfoot
and not(ancestor::row/following-sibling::row)">1</xsl:when>
<xsl:when test="not(ancestor::tfoot)
and ancestor::tgroup/tfoot">0</xsl:when>
<xsl:when test="not(ancestor::tfoot)
and not(ancestor::tgroup/tfoot)
and count(ancestor::row/following-sibling::row) &lt; $rows-spanned">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="lastcol">
<xsl:variable name="spanname" select="@spanname"/>
<xsl:variable name="spanspec"
select="ancestor::tgroup/spanspec[@spanname=$spanname]"/>
<xsl:variable name="nameend">
<xsl:choose>
<xsl:when test="@spanname">
<xsl:value-of select="$spanspec/@nameend"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@nameend"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="ecol">
<xsl:choose>
<xsl:when test="$nameend!=''">
<xsl:call-template name="colspec.colnum">
<xsl:with-param name="colspec"
select="ancestor::tgroup/colspec[@colname=$nameend]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$col"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$ecol &lt; ancestor::tgroup/@cols">0</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="rowsep">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="."/>
<xsl:with-param name="colnum" select="$entry.colnum"/>
<xsl:with-param name="attribute" select="'rowsep'"/>
<xsl:with-param name="lastrow" select="$lastrow"/>
<xsl:with-param name="lastcol" select="$lastcol"/>
</xsl:call-template>
</xsl:variable>
<!--
<xsl:message><xsl:value-of select="."/>: <xsl:value-of select="$rowsep"/></xsl:message>
-->
<xsl:variable name="colsep">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="."/>
<xsl:with-param name="colnum" select="$entry.colnum"/>
<xsl:with-param name="attribute" select="'colsep'"/>
<xsl:with-param name="lastrow" select="$lastrow"/>
<xsl:with-param name="lastcol" select="$lastcol"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="valign">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="."/>
<xsl:with-param name="colnum" select="$entry.colnum"/>
<xsl:with-param name="attribute" select="'valign'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="align">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="."/>
<xsl:with-param name="colnum" select="$entry.colnum"/>
<xsl:with-param name="attribute" select="'align'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="char">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="."/>
<xsl:with-param name="colnum" select="$entry.colnum"/>
<xsl:with-param name="attribute" select="'char'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="charoff">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="."/>
<xsl:with-param name="colnum" select="$entry.colnum"/>
<xsl:with-param name="attribute" select="'charoff'"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$spans != '' and not(starts-with($spans,'0:'))">
<xsl:call-template name="entry-template">
<xsl:with-param name="col" select="$col+1"/>
<xsl:with-param name="spans" select="substring-after($spans,':')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$entry.colnum &gt; $col">
<xsl:call-template name="empty.table.cell">
<xsl:with-param name="colnum" select="$col"/>
</xsl:call-template>
<xsl:call-template name="entry-template">
<xsl:with-param name="col" select="$col+1"/>
<xsl:with-param name="spans" select="substring-after($spans,':')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="context">
<xsl:choose>
<xsl:when test="parent::row/parent::thead">thead</xsl:when>
<xsl:when test="parent::row/parent::tfoot">tfoot</xsl:when>
<xsl:otherwise>tbody</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="cell.content">
<fo:block>
<!-- highlight this entry? -->
<xsl:if test="ancestor::thead">
<xsl:attribute name="font-weight">bold</xsl:attribute>
</xsl:if>
<!-- are we missing any indexterms? -->
<xsl:if test="not(preceding-sibling::entry)
and not(parent::row/preceding-sibling::row)">
<!-- this is the first entry of the first row -->
<xsl:if test="ancestor::thead or
(ancestor::tbody
and not(ancestor::tbody/preceding-sibling::thead
or ancestor::tbody/preceding-sibling::tbody))">
<!-- of the thead or the first tbody -->
<xsl:apply-templates select="ancestor::tgroup/preceding-sibling::indexterm"/>
</xsl:if>
</xsl:if>
<!--
<xsl:text>(</xsl:text>
<xsl:value-of select="$rowsep"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="$colsep"/>
<xsl:text>)</xsl:text>
-->
<xsl:choose>
<xsl:when test="$empty.cell">
<xsl:text>&#160;</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</xsl:variable>
<fo:table-cell xsl:use-attribute-sets="table.cell.padding">
<xsl:call-template name="entry">
<xsl:with-param name="context" select="$context"/>
</xsl:call-template>
<xsl:call-template name="maybe-emit-cell-padding-attrs"/>
<xsl:call-template name="maybe-emit-rtf-direct-attrs"/>
<xsl:if test="$rowsep &gt; 0 and $lastrow = 0">
<xsl:call-template name="border">
<xsl:with-param name="side" select="'bottom'"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="$colsep &gt; 0 and $lastcol = 0">
<xsl:call-template name="border">
<xsl:with-param name="side" select="'right'"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="@morerows != ' '">
<xsl:attribute name="number-rows-spanned">
<xsl:value-of select="number(@morerows+1)"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="$entry.colspan &gt; 1">
<xsl:attribute name="number-columns-spanned">
<xsl:value-of select="$entry.colspan"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="$valign != ''">
<xsl:attribute name="display-align">
<xsl:choose>
<xsl:when test="$valign='top' or $valign='TOP'">before</xsl:when>
<xsl:when test="$valign='middle' or $valign='MIDDLE'">center</xsl:when>
<xsl:when test="$valign='bottom' or $valign='BOTTOM'">after</xsl:when>
<xsl:otherwise>
<xsl:message>
<xsl:text>Unexpected valign value: </xsl:text>
<xsl:value-of select="$valign"/>
<xsl:text>, center used.</xsl:text>
</xsl:message>
<xsl:text>center</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:if>
<xsl:if test="$align != ''">
<xsl:attribute name="text-align">
<xsl:choose>
<xsl:when test="$align='left' or $align='LEFT'">left</xsl:when>
<xsl:when test="$align='right' or $align='RIGHT'">right</xsl:when>
<xsl:when test="$align='center' or $align='CENTER'">center</xsl:when>
<xsl:when test="$align='justify' or $align='JUSTIFY'">justify</xsl:when>
<xsl:otherwise>
<xsl:message>
<xsl:text>Unexpected valign value: </xsl:text>
<xsl:value-of select="$align"/>
<xsl:text>, center used.</xsl:text>
</xsl:message>
<xsl:text>center</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:if>
<!--
<xsl:if test="@charoff">
<xsl:attribute name="charoff">
<xsl:value-of select="@charoff"/>
</xsl:attribute>
</xsl:if>
-->
<!-- ***** first added call to handle _cellfont ***** -->
<xsl:call-template name="just-after-table-cell-stag"/>
<!-- ***** end added line ***** -->
<xsl:copy-of select="$cell.content"/>
<!-- ***** second added call to handle _cellfont ***** -->
<xsl:call-template name="just-before-table-cell-etag"/>
<!-- ***** end added line ***** -->
</fo:table-cell>
<xsl:choose>
<xsl:when test="following-sibling::entry">
<xsl:apply-templates select="following-sibling::entry[1]">
<xsl:with-param name="col" select="$col+$entry.colspan"/>
<xsl:with-param name="spans" select="$following.spans"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="finaltd">
<xsl:with-param name="spans" select="$following.spans"/>
<xsl:with-param name="col" select="$col+$entry.colspan"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="maybe-emit-cell-padding-attrs">
<xsl:if test="ancestor-or-self::tgroup[1]/processing-instruction('PubTbl')
[starts-with(.,'tgroup') and contains(.,'marg=')]">
<!-- Use PubTbl tgroup values for c{r,l,t,b}marg to set overriding padding-* values -->
<xsl:variable name="cmarg-pi"
select="ancestor-or-self::tgroup[1]/processing-instruction('PubTbl')
[starts-with(.,'tgroup') and contains(.,'marg=')]"/>
<xsl:if test="contains($cmarg-pi,'crmarg=')">
<xsl:variable name="marg-pi2" select='substring-after($cmarg-pi,"crmarg=")'/>
<xsl:attribute name="padding-right">
<xsl:value-of select="substring-before(substring($marg-pi2,2),'&quot;')"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="contains($cmarg-pi,'clmarg=')">
<xsl:variable name="marg-pi2" select='substring-after($cmarg-pi,"clmarg=")'/>
<xsl:attribute name="padding-left">
<xsl:value-of select="substring-before(substring($marg-pi2,2),'&quot;')"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="contains($cmarg-pi,'ctmarg=')">
<xsl:variable name="marg-pi2" select='substring-after($cmarg-pi,"ctmarg=")'/>
<xsl:attribute name="padding-top">
<xsl:value-of select="substring-before(substring($marg-pi2,2),'&quot;')"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="contains($cmarg-pi,'cbmarg=')">
<xsl:variable name="marg-pi2" select='substring-after($cmarg-pi,"cbmarg=")'/>
<xsl:attribute name="padding-bottom">
<xsl:value-of select="substring-before(substring($marg-pi2,2),'&quot;')"/>
</xsl:attribute>
</xsl:if>
</xsl:if>
</xsl:template>
<xsl:template match="entry" name="sentry" mode="span">
<xsl:param name="col" select="1"/>
<xsl:param name="spans"/>
<xsl:variable name="entry.colnum">
<xsl:call-template name="entry.colnum"/>
</xsl:variable>
<xsl:variable name="entry.colspan">
<xsl:choose>
<xsl:when test="@spanname or @namest">
<xsl:call-template name="calculate.colspan"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="following.spans">
<xsl:call-template name="calculate.following.spans">
<xsl:with-param name="colspan" select="$entry.colspan"/>
<xsl:with-param name="spans" select="$spans"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$spans != '' and not(starts-with($spans,'0:'))">
<xsl:value-of select="substring-before($spans,':')-1"/>
<xsl:text>:</xsl:text>
<xsl:call-template name="sentry">
<xsl:with-param name="col" select="$col+1"/>
<xsl:with-param name="spans" select="substring-after($spans,':')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$entry.colnum &gt; $col">
<xsl:text>0:</xsl:text>
<xsl:call-template name="sentry">
<xsl:with-param name="col" select="$col+$entry.colspan"/>
<xsl:with-param name="spans" select="$following.spans"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="copy-string">
<xsl:with-param name="count" select="$entry.colspan"/>
<xsl:with-param name="string">
<xsl:choose>
<xsl:when test="@morerows">
<xsl:value-of select="@morerows"/>
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
<xsl:text>:</xsl:text>
</xsl:with-param>
</xsl:call-template>
<xsl:choose>
<xsl:when test="following-sibling::entry">
<xsl:apply-templates select="following-sibling::entry[1]"
mode="span">
<xsl:with-param name="col" select="$col+$entry.colspan"/>
<xsl:with-param name="spans" select="$following.spans"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="sfinaltd">
<xsl:with-param name="spans" select="$following.spans"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="generate.colgroup.raw">
<xsl:param name="cols" select="1"/>
<xsl:param name="count" select="1"/>
<xsl:choose>
<xsl:when test="$count>$cols"></xsl:when>
<xsl:otherwise>
<xsl:call-template name="generate.col.raw">
<xsl:with-param name="countcol" select="$count"/>
</xsl:call-template>
<xsl:call-template name="generate.colgroup.raw">
<xsl:with-param name="cols" select="$cols"/>
<xsl:with-param name="count" select="$count+1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="generate.colgroup">
<xsl:param name="cols" select="1"/>
<xsl:param name="count" select="1"/>
<xsl:choose>
<xsl:when test="$count>$cols"></xsl:when>
<xsl:otherwise>
<xsl:call-template name="generate.col">
<xsl:with-param name="countcol" select="$count"/>
</xsl:call-template>
<xsl:call-template name="generate.colgroup">
<xsl:with-param name="cols" select="$cols"/>
<xsl:with-param name="count" select="$count+1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="generate.col.raw">
<!-- generate the table-column for column countcol -->
<xsl:param name="countcol">1</xsl:param>
<xsl:param name="colspecs" select="./colspec"/>
<xsl:param name="count">1</xsl:param>
<xsl:param name="colnum">1</xsl:param>
<xsl:choose>
<xsl:when test="$count>count($colspecs)">
<fo:table-column column-number="{$countcol}"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="colspec" select="$colspecs[$count=position()]"/>
<xsl:variable name="colspec.colnum">
<xsl:choose>
<xsl:when test="$colspec/@colnum">
<xsl:value-of select="$colspec/@colnum"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$colnum"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="colspec.colwidth">
<xsl:choose>
<xsl:when test="$colspec/@colwidth">
<xsl:value-of select="$colspec/@colwidth"/>
</xsl:when>
<xsl:otherwise>1*</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$colspec.colnum=$countcol">
<fo:table-column column-number="{$countcol}">
<xsl:attribute name="column-width">
<xsl:value-of select="$colspec.colwidth"/>
</xsl:attribute>
</fo:table-column>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="generate.col.raw">
<xsl:with-param name="countcol" select="$countcol"/>
<xsl:with-param name="colspecs" select="$colspecs"/>
<xsl:with-param name="count" select="$count+1"/>
<xsl:with-param name="colnum">
<xsl:choose>
<xsl:when test="$colspec/@colnum">
<xsl:value-of select="$colspec/@colnum + 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$colnum + 1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="generate.col">
<!-- generate the table-column for column countcol -->
<xsl:param name="countcol">1</xsl:param>
<xsl:param name="colspecs" select="./colspec"/>
<xsl:param name="count">1</xsl:param>
<xsl:param name="colnum">1</xsl:param>
<xsl:choose>
<xsl:when test="$count>count($colspecs)">
<fo:table-column column-number="{$countcol}">
<xsl:variable name="colwidth">
<xsl:call-template name="calc.column.width"/>
</xsl:variable>
<xsl:if test="$colwidth != 'proportional-column-width(1)' or
$inhibit-default-colwidth-emission='0'">
<xsl:attribute name="column-width">
<xsl:value-of select="$colwidth"/>
</xsl:attribute>
</xsl:if>
</fo:table-column>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="colspec" select="$colspecs[$count=position()]"/>
<xsl:variable name="colspec.colnum">
<xsl:choose>
<xsl:when test="$colspec/@colnum">
<xsl:value-of select="$colspec/@colnum"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$colnum"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="colspec.colwidth">
<xsl:choose>
<xsl:when test="$colspec/@colwidth='*'">1*</xsl:when>
<xsl:when test="$colspec/@colwidth">
<xsl:value-of select="$colspec/@colwidth"/>
</xsl:when>
<xsl:otherwise>1*</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$colspec.colnum=$countcol">
<fo:table-column column-number="{$countcol}">
<xsl:variable name="colwidth">
<xsl:call-template name="calc.column.width">
<xsl:with-param name="colwidth">
<xsl:value-of select="$colspec.colwidth"/>
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:if test="$colwidth != 'proportional-column-width(1)' or
$inhibit-default-colwidth-emission='0'">
<xsl:attribute name="column-width">
<xsl:value-of select="$colwidth"/>
</xsl:attribute>
</xsl:if>
</fo:table-column>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="generate.col">
<xsl:with-param name="countcol" select="$countcol"/>
<xsl:with-param name="colspecs" select="$colspecs"/>
<xsl:with-param name="count" select="$count+1"/>
<xsl:with-param name="colnum">
<xsl:choose>
<xsl:when test="$colspec/@colnum">
<xsl:value-of select="$colspec/@colnum + 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$colnum + 1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="calc.column.width">
<xsl:param name="colwidth">1*</xsl:param>
<!-- Ok, the colwidth could have any one of the following forms: -->
<!-- 1* = proportional width -->
<!-- 1unit = 1.0 units wide -->
<!-- 1 = 1pt wide -->
<!-- 1*+1unit = proportional width + some fixed width -->
<!-- 1*+1 = proportional width + some fixed width -->
<!-- If it has a proportional width, translate it to XSL -->
<xsl:if test="contains($colwidth, '*')">
<xsl:text>proportional-column-width(</xsl:text>
<xsl:value-of select="substring-before($colwidth, '*')"/>
<xsl:text>)</xsl:text>
</xsl:if>
<xsl:if test="contains($colwidth, 'in')">
<xsl:text>proportional-column-width(</xsl:text>
<xsl:value-of select="substring-before($colwidth, 'in')"/>
<xsl:text>)</xsl:text>
</xsl:if>
<!-- Now grab the non-proportional part of the specification -->
<xsl:variable name="width-units">
<xsl:choose>
<xsl:when test="contains($colwidth, '*')">
<xsl:value-of
select="normalize-space(substring-after($colwidth, '*'))"/>
</xsl:when>
<xsl:when test="contains($colwidth, 'in')">
<xsl:value-of
select="normalize-space(substring-after($colwidth, 'in'))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="normalize-space($colwidth)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- Ok, now the width-units could have any one of the following forms: -->
<!-- = <empty string> -->
<!-- 1unit = 1.0 units wide -->
<!-- 1 = 1pt wide -->
<!-- with an optional leading sign -->
<!-- Grab the width part by blanking out the units part and discarding -->
<!-- whitespace. It's not pretty, but it works. -->
<xsl:variable name="width"
select="normalize-space(translate($width-units,
'+-0123456789.abcdefghijklmnopqrstuvwxyz',
'+-0123456789.'))"/>
<!-- Grab the units part by blanking out the width part and discarding -->
<!-- whitespace. It's not pretty, but it works. -->
<xsl:variable name="units"
select="normalize-space(translate($width-units,
'abcdefghijklmnopqrstuvwxyz+-0123456789.',
'abcdefghijklmnopqrstuvwxyz'))"/>
<!-- Output the width -->
<xsl:value-of select="$width"/>
<!-- Output the units, translated appropriately -->
<xsl:choose>
<xsl:when test="$units = 'pi'">pc</xsl:when>
<xsl:when test="$units = '' and $width != ''">pt</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$units"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="maybe-emit-rtf-direct-attrs"/>
<xsl:template name="tgroup.first">
<!--<xsl:attribute name="font-size">9pt</xsl:attribute>-->
<xsl:attribute name="font-family">
<xsl:value-of select="$g_font_family"/>
</xsl:attribute>
<xsl:attribute name="margin-left">0pt</xsl:attribute>
<xsl:attribute name="text-indent">0pt</xsl:attribute>
</xsl:template>
<xsl:template name="tgroup.notfirst">
<xsl:attribute name="margin-left">0pt</xsl:attribute>
<xsl:attribute name="text-indent">0pt</xsl:attribute>
</xsl:template>
<xsl:template name="thead"/>
<xsl:template name="tfoot"/>
<xsl:template name="tbody"/>
<xsl:template name="row">
<xsl:attribute name="text-align">left</xsl:attribute>
<xsl:attribute name="keep-together.within-page">always</xsl:attribute>
<xsl:attribute name="page-break-inside">avoid</xsl:attribute>
<xsl:attribute name="keep-together">always</xsl:attribute>
</xsl:template>
<xsl:template name="entry">
<xsl:attribute name="margin-left">0pt - inherited-property-value(start-indent) + 0pt</xsl:attribute>
<xsl:attribute name="margin-right">0pt - inherited-property-value(end-indent) + 0pt</xsl:attribute>
<!--<xsl:attribute name="text-align">left</xsl:attribute>-->
<!--<xsl:attribute name="text-indent">0pt</xsl:attribute>-->
<!--<xsl:attribute name="font-size">9pt</xsl:attribute>-->
<xsl:attribute name="padding-left">3pt</xsl:attribute>
<xsl:attribute name="space-before.optimum">0pt</xsl:attribute>
<xsl:attribute name="space-before.minimum">0pt</xsl:attribute>
<xsl:attribute name="space-before.maximum">0pt</xsl:attribute>
<xsl:attribute name="space-before.precedence">force</xsl:attribute>
</xsl:template>
<xsl:template name="just-after-table-cell-stag"/>
<xsl:template name="just-after-table-cell-etag"/>
<xsl:template name="just-before-table-cell-stag"/>
<xsl:template name="just-before-table-cell-etag"/>
</xsl:stylesheet>
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
version="2.0">
<!-- **********************************************************
tbl.xsl - transforms CALS tables into FOs
********************************************************** -->
<!--<xsl:import href="tbl-params.xsl"/>-->
<xsl:param name="default.table.width" select="''"/>
<xsl:param name="table.border.color" select="'black'"/>
<xsl:param name="table.border.style" select="'solid'"/>
<xsl:param name="table.border.thickness" select="'0.5pt'"/>
<xsl:param name="table.cell.padding.amount" select="'1mm'"/>
<xsl:param name="tablecolumns.extension" select="'1'"/>
<xsl:param name="use.extensions" select="'0'"/>
<!-- tbl-params.xsl sets the following parameters:
<xsl:param name="default.table.width" select="''"/>
<xsl:param name="TABLE.border.color" select="'black'"/>
<xsl:param name="TABLE.border.style" select="'solid'"/>
<xsl:param name="TABLE.border.thickness" select="'0.5pt'"/>
<xsl:param name="TABLE.cell.padding.amount" select="'2pt'"/>
-->
<xsl:variable name="rule-appearance">black solid 0.5pt</xsl:variable>
<xsl:variable name="no-rule">white solid 0pt</xsl:variable>
<xsl:attribute-set name="table.cell.padding">
<xsl:attribute name="margin-left">0pt</xsl:attribute>
<xsl:attribute name="margin-right">0pt</xsl:attribute>
<xsl:attribute name="padding">
<xsl:value-of select="$table.cell.padding.amount"/>
</xsl:attribute>
<xsl:attribute name="text-align">center</xsl:attribute>
<xsl:attribute name="display-align">center</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="table-reset-indents">
<xsl:attribute name="start-indent">0pt</xsl:attribute>
<xsl:attribute name="end-indent">0pt</xsl:attribute>
</xsl:attribute-set>
<xsl:template match="EFFECT[parent::TABLE]" priority="10"/>
<xsl:template name="table-attributes">
<xsl:variable name="frame-val" select="translate(@FRAME,'topbmalsiden','TOPBMALSIDEN')"/>
<xsl:choose>
<xsl:when test="not($frame-val)">
<!-- Default -->
<xsl:attribute name="border-top">
<xsl:value-of select="$rule-appearance"/>
</xsl:attribute>
<xsl:attribute name="border-bottom">
<xsl:value-of select="$rule-appearance"/>
</xsl:attribute>
<xsl:attribute name="border-left">
<xsl:value-of select="$rule-appearance"/>
</xsl:attribute>
<xsl:attribute name="border-right">
<xsl:value-of select="$rule-appearance"/>
</xsl:attribute>
</xsl:when>
<xsl:when test="$frame-val = 'NONE'">
<xsl:attribute name="border-top">
<xsl:value-of select="$no-rule"/>
</xsl:attribute>
<xsl:attribute name="border-bottom">
<xsl:value-of select="$no-rule"/>
</xsl:attribute>
<xsl:attribute name="border-left">
<xsl:value-of select="$no-rule"/>
</xsl:attribute>
<xsl:attribute name="border-right">
<xsl:value-of select="$no-rule"/>
</xsl:attribute>
</xsl:when>
<xsl:when test="$frame-val = 'TOP'">
<xsl:attribute name="border-top">
<xsl:value-of select="$rule-appearance"/>
</xsl:attribute>
</xsl:when>
<xsl:when test="$frame-val = 'BOTTOM'">
<xsl:attribute name="border-bottom">
<xsl:value-of select="$rule-appearance"/>
</xsl:attribute>
</xsl:when>
<xsl:when test="$frame-val = 'TOPBOT'">
<xsl:attribute name="border-top">
<xsl:value-of select="$rule-appearance"/>
</xsl:attribute>
<xsl:attribute name="border-bottom">
<xsl:value-of select="$rule-appearance"/>
</xsl:attribute>
<xsl:attribute name="border-left">
<xsl:value-of select="$no-rule"/>
</xsl:attribute>
<xsl:attribute name="border-right">
<xsl:value-of select="$no-rule"/>
</xsl:attribute>
</xsl:when>
<xsl:when test="$frame-val = 'ALL'">
<xsl:attribute name="border-top">
<xsl:value-of select="$rule-appearance"/>
</xsl:attribute>
<xsl:attribute name="border-bottom">
<xsl:value-of select="$rule-appearance"/>
</xsl:attribute>
<xsl:attribute name="border-left">
<xsl:value-of select="$rule-appearance"/>
</xsl:attribute>
<xsl:attribute name="border-right">
<xsl:value-of select="$rule-appearance"/>
</xsl:attribute>
</xsl:when>
<xsl:when test="$frame-val = 'SIDES'">
<xsl:attribute name="border-left">
<xsl:value-of select="$rule-appearance"/>
</xsl:attribute>
<xsl:attribute name="border-right">
<xsl:value-of select="$rule-appearance"/>
</xsl:attribute>
<xsl:attribute name="border-top">
<xsl:value-of select="$no-rule"/>
</xsl:attribute>
<xsl:attribute name="border-bottom">
<xsl:value-of select="$no-rule"/>
</xsl:attribute>
</xsl:when>
</xsl:choose>
<xsl:if test="@PGWIDE = 1">
<xsl:attribute name="width">100%</xsl:attribute>
</xsl:if>
</xsl:template>
<xsl:template name="table-processing">
<xsl:variable name="table-colsep">
<xsl:choose>
<xsl:when test="string(@COLSEP)">
<xsl:value-of select="@COLSEP"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="table-rowsep">
<xsl:choose>
<xsl:when test="string(@ROWSEP)">
<xsl:value-of select="@ROWSEP"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="TITLE|TITLEC">
<!-- Table with caption -->
<fo:table-and-caption>
<xsl:call-template name="showRevMarker"/>
<fo:table-caption>
<fo:block>
<xsl:apply-templates select="TITLE|TITLEC"/>
</fo:block>
</fo:table-caption>
<fo:table width="100%" text-align="center" display-align="center">
<xsl:call-template name="table-attributes"/>
<xsl:apply-templates select="*[not(self::TITLE) and not(self::TITLEC)]">
<xsl:with-param name="rowsep" select="$table-rowsep"/>
<xsl:with-param name="colsep" select="$table-colsep"/>
</xsl:apply-templates>
</fo:table>
</fo:table-and-caption>
</xsl:when>
<!-- Table without caption -->
<xsl:otherwise>
<fo:table width="100%">
<xsl:call-template name="showRevMarker"/>
<xsl:call-template name="table-attributes"/>
<xsl:apply-templates select="*[not(self::TITLE) and not(self::TITLEC)]">
<xsl:with-param name="rowsep" select="$table-rowsep"/>
<xsl:with-param name="colsep" select="$table-colsep"/>
</xsl:apply-templates>
</fo:table>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="copy-string">
<!-- returns 'count' copies of 'string' -->
<xsl:param name="string"/>
<xsl:param name="count" select="0"/>
<xsl:param name="result"/>
<xsl:choose>
<xsl:when test="$count&gt;0">
<xsl:call-template name="copy-string">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="result">
<xsl:value-of select="$result"/>
<xsl:value-of select="$string"/>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="blank.spans">
<xsl:param name="cols" select="1"/>
<xsl:if test="$cols &gt; 0">
<xsl:text>0:</xsl:text>
<xsl:call-template name="blank.spans">
<xsl:with-param name="cols" select="$cols - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="calculate.following.spans">
<xsl:param name="colspan" select="1"/>
<xsl:param name="spans" select="''"/>
<xsl:choose>
<xsl:when test="$colspan &gt; 0">
<xsl:call-template name="calculate.following.spans">
<xsl:with-param name="colspan" select="$colspan - 1"/>
<xsl:with-param name="spans" select="substring-after($spans,':')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$spans"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="finaltd">
<xsl:param name="spans"/>
<xsl:param name="col" select="0"/>
<xsl:if test="$spans != ''">
<xsl:choose>
<xsl:when test="starts-with($spans,'0:')">
<xsl:call-template name="empty.table.cell">
<xsl:with-param name="colnum" select="$col"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
<xsl:call-template name="finaltd">
<xsl:with-param name="spans" select="substring-after($spans,':')"/>
<xsl:with-param name="col" select="$col+1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="sfinaltd">
<xsl:param name="spans"/>
<xsl:if test="$spans != ''">
<xsl:choose>
<xsl:when test="starts-with($spans, '0:')">0:</xsl:when>
<xsl:otherwise>
<xsl:value-of select="number(substring-before($spans, ':')) - 1"/>
<xsl:text>:</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:call-template name="sfinaltd">
<xsl:with-param name="spans" select="substring-after($spans, ':')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="entry.colnum">
<xsl:param name="entry" select="."/>
<xsl:choose>
<xsl:when test="$entry/@SPANNAME">
<xsl:variable name="spanname" select="$entry/@SPANNAME"/>
<xsl:variable name="spanspec"
select="$entry/ancestor::TGROUP/SPANSPEC[@SPANNAME=$spanname]"/>
<xsl:variable name="colspec"
select="$entry/ancestor::TGROUP/COLSPEC[translate(@COLNAME, $upperCase, $lowerCase) = translate($spanspec/@NAMEST, $upperCase, $lowerCase)]"/>
<xsl:call-template name="colspec.colnum">
<xsl:with-param name="colspec" select="$colspec"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$entry/@COLNAME">
<xsl:variable name="colname" select="$entry/@COLNAME"/>
<xsl:variable name="colspec"
select="$entry/ancestor::TGROUP/COLSPEC[@COLNAME=$colname]"/>
<xsl:call-template name="colspec.colnum">
<xsl:with-param name="colspec" select="$colspec"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$entry/@NAMEST">
<xsl:variable name="namest" select="translate($entry/@NAMEST, $upperCase, $lowerCase)"/>
<xsl:variable name="colspec"
select="$entry/ancestor::TGROUP/COLSPEC[translate(@COLNAME, $upperCase, $lowerCase) = $namest]"/>
<xsl:call-template name="colspec.colnum">
<xsl:with-param name="colspec" select="$colspec"/>
</xsl:call-template>
</xsl:when>
<!-- no idea, return 0 -->
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="colspec.colnum">
<xsl:param name="colspec" select="."/>
<xsl:choose>
<xsl:when test="$colspec/@COLNUM">
<xsl:value-of select="$colspec/@COLNUM"/>
</xsl:when>
<xsl:when test="$colspec/preceding-sibling::COLSPEC">
<xsl:variable name="prec.colspec.colnum">
<xsl:call-template name="colspec.colnum">
<xsl:with-param name="colspec"
select="$colspec/preceding-sibling::COLSPEC[1]"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$prec.colspec.colnum + 1"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="calculate.colspan">
<xsl:param name="entry" select="."/>
<xsl:variable name="spanname" select="$entry/@SPANNAME"/>
<xsl:variable name="spanspec"
select="$entry/ancestor::TGROUP/SPANSPEC[@SPANNAME=$spanname]"/>
<xsl:variable name="namest">
<xsl:choose>
<xsl:when test="@SPANNAME">
<xsl:value-of select="translate($spanspec/@NAMEST, $upperCase, $lowerCase)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate($entry/@NAMEST, $upperCase, $lowerCase)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="nameend">
<xsl:choose>
<xsl:when test="@SPANNAME">
<xsl:value-of select="translate($spanspec/@NAMEEND, $upperCase, $lowerCase)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate($entry/@NAMEEND, $upperCase, $lowerCase)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="scol">
<xsl:call-template name="colspec.colnum">
<xsl:with-param name="colspec"
select="$entry/ancestor::TGROUP/COLSPEC[translate(@COLNAME, $upperCase, $lowerCase) = $namest]"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="ecol">
<xsl:call-template name="colspec.colnum">
<xsl:with-param name="colspec"
select="$entry/ancestor::TGROUP/COLSPEC[translate(@COLNAME, $upperCase, $lowerCase) = $nameend]"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$namest != '' and $nameend != ''">
<xsl:choose>
<xsl:when test="number($ecol) &gt;= number($scol)">
<xsl:value-of select="$ecol - $scol + 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$scol - $ecol + 1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="calculate.rowsep">
<xsl:param name="entry" select="."/>
<xsl:param name="colnum" select="0"/>
</xsl:template>
<xsl:template name="inherited.table.attribute">
<xsl:param name="entry" select="."/>
<xsl:param name="row" select="$entry/ancestor-or-self::ROW[1]"/>
<xsl:param name="colnum" select="0"/>
<xsl:param name="attribute" select="'colsep'"/>
<xsl:param name="lastrow" select="0"/>
<xsl:param name="lastcol" select="0"/>
<xsl:variable name="tgroup" select="$row/ancestor::TGROUP[1]"/>
<xsl:variable name="entry.value">
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$entry"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="row.value">
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$row"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="span.value">
<xsl:if test="$entry/@SPANNAME">
<xsl:variable name="spanname" select="$entry/@SPANNAME"/>
<xsl:variable name="spanspec"
select="$tgroup/SPANSPEC[@SPANNAME = $spanname]"/>
<xsl:variable name="span.colspec"
select="$tgroup/COLSPEC[translate(@COLNAME, $upperCase, $lowerCase) = translate($spanspec/@NAMEST, $upperCase, $lowerCase)]"/>
<xsl:variable name="spanspec.value">
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$spanspec"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="scolspec.value">
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$span.colspec"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$spanspec.value != ''">
<xsl:value-of select="$spanspec.value"/>
</xsl:when>
<xsl:when test="$scolspec.value != ''">
<xsl:value-of select="$scolspec.value"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:if>
</xsl:variable>
<xsl:variable name="namest.value">
<xsl:if test="$entry/@NAMEST">
<xsl:variable name="namest" select="translate($entry/@NAMEST, $upperCase, $lowerCase)"/>
<xsl:variable name="colspec"
select="$tgroup/COLSPEC[translate(@COLNAME, $upperCase, $lowerCase) = $namest]"/>
<xsl:variable name="inner.namest.value">
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$colspec"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$inner.namest.value">
<xsl:value-of select="$inner.namest.value"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:if>
</xsl:variable>
<xsl:variable name="tgroup.value">
<xsl:choose>
<!-- Special case to handle thead valign -->
<!--<xsl:when test="$attribute='valign' and ancestor::THEAD/@valign">
<xsl:value-of select="ancestor::THEAD/@valign"/>
</xsl:when>-->
<xsl:when test="$attribute='valign'">
<xsl:choose>
<xsl:when test="@VALIGN">
<xsl:value-of select="translate(@VALIGN,$upperCase,$lowerCase)"/>
</xsl:when>
<xsl:when test="ancestor::THEAD/@VALIGN">
<xsl:value-of select="translate(ancestor::THEAD/@VALIGN,$upperCase,$lowerCase)"/>
</xsl:when>
<xsl:otherwise>center</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$attribute='align'">
<xsl:choose>
<xsl:when test="@ALIGN">
<xsl:value-of select="translate(@ALIGN,$upperCase,$lowerCase)"/>
</xsl:when>
<xsl:when test="ancestor::TGROUP/@ALIGN">
<xsl:value-of select="translate(ancestor::TGROUP/@ALIGN,$upperCase,$lowerCase)"/>
</xsl:when>
<xsl:otherwise>center</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$tgroup"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="default.value">
<!-- rowsep and colsep can have defaults on the "table" wrapper and
ultimately on the frame setting for outside rules. Non-outside
rules are unaffected by the frame setting. Both rowsep and colsep
default to 1 on the table wrapper if otherwise unspecified. -->
<!-- handle those here, for everything else, the default is the tgroup value -->
<xsl:choose>
<xsl:when test="$tgroup.value != ''">
<xsl:value-of select="$tgroup.value"/>
</xsl:when>
<xsl:when test="$attribute = 'rowsep'">
<xsl:choose>
<xsl:when test="$tgroup/parent::*/@ROWSEP">
<xsl:value-of select="$tgroup/parent::*/@ROWSEP"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$attribute = 'colsep'">
<xsl:choose>
<xsl:when test="$tgroup/parent::*/@COLSEP">
<xsl:value-of select="$tgroup/parent::*/@ROWSEP"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<!-- empty -->
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="frame.value">
<xsl:variable name="frame">
<xsl:choose>
<xsl:when test="$tgroup/parent::*/@FRAME">
<xsl:value-of select="$tgroup/parent::*/@FRAME"/>
</xsl:when>
<xsl:otherwise>all</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$attribute='rowsep'">
<xsl:choose>
<xsl:when
test="$frame='all' or $frame='topbot' or $frame='bot' or $frame='ALL' or $frame='TOPBOT' or $frame='BOT'">
1
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$attribute='colsep'">
<xsl:choose>
<xsl:when test="$frame='all' or $frame='sides' or $frame='ALL' or $frame='SIDES'">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$lastrow=1 and $attribute='rowsep'">
<xsl:value-of select="$frame.value"/>
</xsl:when>
<xsl:when test="$lastcol=1 and $attribute='colsep'">
<xsl:value-of select="$frame.value"/>
</xsl:when>
<xsl:when test="$entry.value != ''">
<xsl:value-of select="$entry.value"/>
</xsl:when>
<xsl:when test="$row.value != ''">
<xsl:value-of select="$row.value"/>
</xsl:when>
<xsl:when test="$span.value != ''">
<xsl:value-of select="$span.value"/>
</xsl:when>
<xsl:when test="$namest.value != ''">
<xsl:value-of select="$namest.value"/>
</xsl:when>
<xsl:when test="$colnum &gt; 0">
<xsl:variable name="calc.colvalue">
<xsl:call-template name="colnum.colspec">
<xsl:with-param name="colnum" select="$colnum"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$calc.colvalue != ''">
<xsl:value-of select="$calc.colvalue"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$default.value"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$default.value"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="calculate.colsep">
<xsl:param name="entry" select="."/>
<xsl:param name="colnum" select="0"/>
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="$entry"/>
<xsl:with-param name="colnum" select="$colnum"/>
<xsl:with-param name="attribute" select="'colsep'"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="colnum.colspec">
<xsl:param name="colnum" select="0"/>
<xsl:param name="attribute" select="'colname'"/>
<xsl:param name="colspecs" select="ancestor::TGROUP/COLSPEC"/>
<xsl:param name="count" select="1"/>
<xsl:choose>
<xsl:when test="not($colspecs) or $count &gt; $colnum">
<!-- nop -->
</xsl:when>
<xsl:when test="$colspecs[1]/@COLNUM">
<xsl:choose>
<xsl:when test="$colspecs[1]/@COLNUM = $colnum">
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$colspecs[1]"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="colnum.colspec">
<xsl:with-param name="colnum" select="$colnum"/>
<xsl:with-param name="attribute" select="$attribute"/>
<xsl:with-param name="colspecs"
select="$colspecs[position()&gt;1]"/>
<xsl:with-param name="count"
select="$colspecs[1]/@COLNUM+1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$count = $colnum">
<xsl:call-template name="get-attribute">
<xsl:with-param name="element" select="$colspecs[1]"/>
<xsl:with-param name="attribute" select="$attribute"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="colnum.colspec">
<xsl:with-param name="colnum" select="$colnum"/>
<xsl:with-param name="attribute" select="$attribute"/>
<xsl:with-param name="colspecs"
select="$colspecs[position()&gt;1]"/>
<xsl:with-param name="count" select="$count+1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="get-attribute">
<xsl:param name="element" select="."/>
<xsl:param name="attribute" select="''"/>
<xsl:for-each select="$element/@*">
<xsl:if test="local-name(.) = $attribute">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
<!-- ==================================================================== -->
<lxslt:component prefix="xtbl" xmlns:lxslt="http://xml.apache.org/xslt"
functions="adjustColumnWidths"/>
<!-- ==================================================================== -->
<xsl:template name="empty.table.cell">
<xsl:param name="colnum" select="1"/>
<xsl:variable name="lastrow">
<xsl:choose>
<xsl:when test="ancestor::THEAD">0</xsl:when>
<xsl:when test="ancestor::TFOOT
and not(ancestor::ROW/following-sibling::ROW)">1
</xsl:when>
<xsl:when test="not(ancestor::TFOOT)
and ancestor::TGROUP/TFOOT">0
</xsl:when>
<xsl:when test="not(ancestor::TFOOT)
and not(ancestor::TGROUP/TFOOT)
and not(ancestor::ROW/following-sibling::ROW)">1
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="lastcol">
<xsl:choose>
<xsl:when test="$colnum &lt; ancestor::TGROUP/@COLS">0</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="rowsep">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="NOT-AN-ELEMENT-NAME"/>
<xsl:with-param name="row" select="ancestor-or-self::ROW[1]"/>
<xsl:with-param name="colnum" select="$colnum"/>
<xsl:with-param name="attribute" select="'rowsep'"/>
<xsl:with-param name="lastrow" select="$lastrow"/>
<xsl:with-param name="lastcol" select="$lastcol"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="colsep">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="NOT-AN-ELEMENT-NAME"/>
<xsl:with-param name="row" select="ancestor-or-self::ROW[1]"/>
<xsl:with-param name="colnum" select="$colnum"/>
<xsl:with-param name="attribute" select="'colsep'"/>
<xsl:with-param name="lastrow" select="$lastrow"/>
<xsl:with-param name="lastcol" select="$lastcol"/>
</xsl:call-template>
</xsl:variable>
<fo:table-cell text-align="center"
display-align="center"
xsl:use-attribute-sets="table.cell.padding">
<xsl:call-template name="ENTRY"/>
<xsl:if test="$rowsep &gt; 0">
<xsl:call-template name="border">
<xsl:with-param name="side" select="'bottom'"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="$colsep &gt; 0">
<xsl:call-template name="border">
<xsl:with-param name="side" select="'right'"/>
</xsl:call-template>
</xsl:if>
<!-- ***** first added call to handle _cellfont ***** -->
<xsl:call-template name="just-after-table-cell-stag"/>
<!-- ***** end added line ***** -->
<!-- fo:table-cell should not be empty -->
<fo:block/>
<!-- ***** second added call to handle _cellfont ***** -->
<xsl:call-template name="just-before-table-cell-etag"/>
<!-- ***** end added line ***** -->
</fo:table-cell>
</xsl:template>
<!-- ==================================================================== -->
<xsl:template name="border">
<xsl:param name="side" select="'left'"/>
<!-- Maybe set border thickness from PubTbl PI -->
<xsl:variable name="border-thickness">
<xsl:choose>
<xsl:when
test="ancestor-or-self::TGROUP[1]/processing-instruction('PubTbl')[starts-with(.,'TGROUP') and contains(.,' rth=')]">
<xsl:variable name="rth-pi"
select="ancestor-or-self::TGROUP[1]/processing-instruction('PubTbl')[starts-with(.,'TGROUP') and contains(.,' rth=')]"/>
<xsl:variable name="rth-pi2" select='substring-after($rth-pi," rth=")'/>
<xsl:value-of select="substring-before(substring($rth-pi2,2),'&quot;')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$table.border.thickness"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:attribute name="border-{$side}-width">
<xsl:value-of select="$border-thickness"/>
</xsl:attribute>
<xsl:attribute name="border-{$side}-style">
<xsl:value-of select="$table.border.style"/>
</xsl:attribute>
<xsl:attribute name="border-{$side}-color">
<xsl:value-of select="$table.border.color"/>
</xsl:attribute>
</xsl:template>
<!-- ==================================================================== -->
<!-- This next template is for Epic 4.3 TurboStyler compatibility
and should be deletable when Styler replaces TurboStyler -->
<!-- <xsl:template match="TABLE">-->
<!-- <fo:block keep-with-previous.within-page="always" />-->
<!-- </xsl:template>-->
<xsl:template match="TGROUP">
<xsl:if test="descendant::TBODY/ROW">
<!-- 单机过滤导致 TBODY 为空时,表格不显示 -->
<fo:block-container start-indent="2pt">
<xsl:choose>
<xsl:when
test="ancestor::TABLE/parent::*[self::NOTE or self::WARNING or self::CAUTION] and not(ancestor::TABLE/preceding-sibling::*)">
<xsl:attribute name="margin-top">20pt</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="margin-top">3pt</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<fo:table border-collapse="collapse"
border-after-width.conditionality="retain"
border-before-width.conditionality="retain"
width="100%">
<xsl:choose>
<xsl:when test="count(preceding-sibling::TGROUP)=0">
<xsl:call-template name="TGROUP.first"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="TGROUP.notfirst"/>
</xsl:otherwise>
</xsl:choose>
<!-- default the value of frame to all -->
<xsl:variable name="frame">
<xsl:choose>
<xsl:when test="../@FRAME">
<xsl:value-of select="../@FRAME"/>
</xsl:when>
<xsl:otherwise>all</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- unless frame='none', for now, act as if it were 'all' -->
<xsl:if test="$frame!='none'">
<xsl:call-template name="border">
<xsl:with-param name="side" select="'left'"/>
</xsl:call-template>
<xsl:call-template name="border">
<xsl:with-param name="side" select="'right'"/>
</xsl:call-template>
<xsl:call-template name="border">
<xsl:with-param name="side" select="'top'"/>
</xsl:call-template>
<xsl:call-template name="border">
<xsl:with-param name="side" select="'bottom'"/>
</xsl:call-template>
</xsl:if>
<!-- end of TEMP code to approximate table rule support -->
<xsl:call-template name="tgroup-after-table-fo"/>
</fo:table>
</fo:block-container>
</xsl:if>
</xsl:template>
<xsl:template match="TGROUP" name="tgroup-after-table-fo" mode="already-emitted-table-fo">
<xsl:variable name="cols">
<xsl:variable name="ncols" select="number(@COLS)"/>
<xsl:choose>
<xsl:when test="string($ncols)='NaN' or (floor($ncols) - $ncols != 0)
or $ncols &lt; 1 or $ncols &gt; 100">
<xsl:text>1</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$ncols"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:if test="position() = 1">
<xsl:attribute name="width">
<xsl:choose>
<xsl:when test="$default.table.width = ''">
<xsl:text>100%</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$default.table.width"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:if>
<xsl:if test="ancestor::APPEND">
<xsl:attribute name="width">
<xsl:choose>
<xsl:when test="$default.table.width = ''">
<xsl:text>99%</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$default.table.width"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:if>
<xsl:call-template name="generate.colgroup">
<xsl:with-param name="cols" select="$cols"/>
</xsl:call-template>
<xsl:apply-templates select="THEAD"/>
<xsl:apply-templates select="TFOOT"/>
<xsl:apply-templates select="TBODY"/>
</xsl:template>
<xsl:template match="COLSPEC"/>
<xsl:template match="SPANSPEC"/>
<xsl:template match="THEAD">
<xsl:variable name="tgroup" select="parent::*"/>
<fo:table-header>
<xsl:call-template name="THEAD"/>
<xsl:apply-templates select="ROW[1]">
<xsl:with-param name="spans">
<xsl:call-template name="blank.spans">
<xsl:with-param name="cols" select="../@COLS"/>
</xsl:call-template>
</xsl:with-param>
</xsl:apply-templates>
</fo:table-header>
</xsl:template>
<xsl:template match="TFOOT">
<xsl:variable name="tgroup" select="parent::*"/>
<fo:table-footer>
<xsl:call-template name="TFOOT"/>
<xsl:apply-templates select="ROW[1]">
<xsl:with-param name="spans">
<xsl:call-template name="blank.spans">
<xsl:with-param name="cols" select="../@COLS"/>
</xsl:call-template>
</xsl:with-param>
</xsl:apply-templates>
</fo:table-footer>
</xsl:template>
<xsl:template match="TBODY">
<xsl:variable name="tgroup" select="parent::*"/>
<fo:table-body>
<xsl:call-template name="TBODY"/>
<xsl:apply-templates select="ROW[1]">
<xsl:with-param name="spans">
<xsl:call-template name="blank.spans">
<xsl:with-param name="cols" select="../@COLS"/>
</xsl:call-template>
</xsl:with-param>
</xsl:apply-templates>
</fo:table-body>
</xsl:template>
<xsl:template match="ROW">
<xsl:param name="spans"/>
<!-- 川航 PRETOPIC内,不显示适用性 -->
<!--<xsl:if test="EFFECT and not(ancestor::PRETOPIC)">-->
<xsl:if test="EFFECT or preceding-sibling::ROW[1]/EFFECT">
<xsl:variable name="entryCount">
<!--<xsl:value-of select="count(child::ENTRY)"/>-->
<xsl:value-of select="count(ancestor::TGROUP/THEAD/ROW[1]/ENTRY)"/>
</xsl:variable>
<xsl:variable name="getTotalColNumber">
<xsl:choose>
<xsl:when test="child::ENTRY[@NAMEST and @NAMEEND and @NAMEST != @NAMEEND]">
<xsl:variable name="getTotalColString">
<xsl:for-each select="child::ENTRY[@NAMEST and @NAMEEND and @NAMEST != @NAMEEND]">
<xsl:variable name="namest">
<xsl:value-of select="translate(@NAMEST, $upperCase, $lowerCase)"/>
</xsl:variable>
<xsl:variable name="nameend">
<xsl:value-of select="translate(@NAMEEND, $upperCase, $lowerCase)"/>
</xsl:variable>
<xsl:variable name="colCount">
<xsl:value-of
select="count(ancestor::TGROUP/child::COLSPEC[translate(@COLNAME, $upperCase, $lowerCase) = $nameend]/preceding-sibling::COLSPEC) - count(ancestor::TGROUP/child::COLSPEC[translate(@COLNAME, $upperCase, $lowerCase) = $namest]/preceding-sibling::COLSPEC)"/>
</xsl:variable>
<xsl:value-of select="concat($colCount, '+')"/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="getTotalCol">
<!-- 计算列加总 -->
<xsl:call-template name="getSum">
<xsl:with-param name="list">
<xsl:value-of select="$getTotalColString"/>
</xsl:with-param>
<xsl:with-param name="separator">+</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$entryCount + number($getTotalCol)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$entryCount"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fo:table-row>
<fo:table-cell number-columns-spanned="{$getTotalColNumber}" text-align="left"
border="0.5pt solid black">
<xsl:choose>
<xsl:when test="not(child::EFFECT)">
<fo:block font-style="italic" font-family="sans-serif" font-weight="bold" margin-left="0pt"
padding="4pt" color="red">
<xsl:text>**ON A/C ALL</xsl:text>
</fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block margin-left="0pt" padding="4pt" color="red">
<xsl:for-each select="child::EFFECT">
<xsl:call-template name="showEffect">
<xsl:with-param name="showEFF" select="true()"/>
</xsl:call-template>
</xsl:for-each>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</fo:table-cell>
</fo:table-row>
</xsl:if>
<!--
<xsl:if test="preceding-sibling::ROW[1]/EFFECT and not(child::EFFECT)">
<xsl:variable name="entryCount">
<xsl:value-of select="count(ancestor::TGROUP/THEAD/ROW[1]/ENTRY)"/>
</xsl:variable>
<xsl:variable name="getTotalColNumber">
<xsl:choose>
<xsl:when test="child::ENTRY[@NAMEST and @NAMEEND and @NAMEST != @NAMEEND]">
<xsl:variable name="getTotalColString">
<xsl:for-each select="child::ENTRY[@NAMEST and @NAMEEND and @NAMEST != @NAMEEND]">
<xsl:variable name="namest">
<xsl:value-of select="translate(@NAMEST, $upperCase, $lowerCase)"/>
</xsl:variable>
<xsl:variable name="nameend">
<xsl:value-of select="translate(@NAMEEND, $upperCase, $lowerCase)"/>
</xsl:variable>
<xsl:variable name="colCount">
<xsl:value-of select="count(ancestor::TGROUP/child::COLSPEC[translate(@COLNAME, $upperCase, $lowerCase) = $nameend]/preceding-sibling::COLSPEC) - count(ancestor::TGROUP/child::COLSPEC[translate(@COLNAME, $upperCase, $lowerCase) = $namest]/preceding-sibling::COLSPEC)"/>
</xsl:variable>
<xsl:value-of select="concat($colCount, '+')"/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="getTotalCol">
<xsl:call-template name="getSum">
<xsl:with-param name="list">
<xsl:value-of select="$getTotalColString"/>
</xsl:with-param>
<xsl:with-param name="separator">+</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$entryCount + number($getTotalCol)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$entryCount"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fo:table-row>
<fo:table-cell number-columns-spanned="{$getTotalColNumber}" text-align="left" border="0.5pt solid black">
<fo:block font-style="italic" font-family="sans-serif" font-weight="bold" margin-left="0pt" padding="4pt" color="red">
<xsl:text>**ON A/C ALL</xsl:text>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:if>
-->
<fo:table-row>
<xsl:call-template name="ROW"/>
<xsl:apply-templates select="ENTRY[1]">
<xsl:with-param name="spans" select="$spans"/>
</xsl:apply-templates>
</fo:table-row>
<xsl:if test="following-sibling::ROW">
<xsl:variable name="nextspans">
<xsl:apply-templates select="ENTRY[1]" mode="span">
<xsl:with-param name="spans" select="$spans"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:apply-templates select="following-sibling::ROW[1]">
<xsl:with-param name="spans" select="$nextspans"/>
</xsl:apply-templates>
</xsl:if>
</xsl:template>
<!-- 计算加总 -->
<xsl:template name="getSum">
<xsl:param name="list"/>
<xsl:param name="separator"/>
<xsl:variable name="newlist" select="concat($list, $separator)"/>
<xsl:variable name="first" select="substring-before($newlist, $separator)"/>
<xsl:variable name="remaining" select="substring-after($newlist, $separator)"/>
<xsl:variable name="next-num">
<xsl:choose>
<xsl:when test="substring-before($remaining, $separator) != ''">
<xsl:call-template name="getSum">
<xsl:with-param name="list" select="$remaining"/>
<xsl:with-param name="separator" select="$separator"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of>0</xsl:value-of>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="number($first) + number($next-num)"/>
</xsl:template>
<xsl:template match="ENTRY" name="entry-template">
<xsl:param name="col" select="1"/>
<xsl:param name="spans"/>
<xsl:variable name="row" select="parent::ROW"/>
<xsl:variable name="group" select="$row/parent::*[1]"/>
<xsl:variable name="empty.cell" select="count(node()) = 0"/>
<xsl:variable name="named.colnum">
<xsl:call-template name="entry.colnum"/>
</xsl:variable>
<xsl:variable name="entry.colnum">
<xsl:choose>
<xsl:when test="$named.colnum &gt; 0">
<xsl:value-of select="$named.colnum"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$col"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="entry.colspan">
<xsl:choose>
<xsl:when test="@SPANNAME or @NAMEST">
<xsl:call-template name="calculate.colspan"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="following.spans">
<xsl:call-template name="calculate.following.spans">
<xsl:with-param name="colspan" select="$entry.colspan"/>
<xsl:with-param name="spans" select="$spans"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="lastrow">
<xsl:choose>
<xsl:when test="ancestor::THEAD">0</xsl:when>
<xsl:when test="ancestor::TFOOT
and not(ancestor::ROW/following-sibling::ROW)">1
</xsl:when>
<xsl:when test="not(ancestor::TFOOT)
and ancestor::TGROUP/TFOOT">0
</xsl:when>
<xsl:when test="not(ancestor::TFOOT)
and not(ancestor::TGROUP/TFOOT)
and not(ancestor::ROW/following-sibling::ROW)">1
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="lastcol">
<xsl:choose>
<xsl:when test="$col &lt; ancestor::TGROUP/@COLS">0</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="rowsep">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="."/>
<xsl:with-param name="colnum" select="$entry.colnum"/>
<xsl:with-param name="attribute" select="'rowsep'"/>
<xsl:with-param name="lastrow" select="$lastrow"/>
<xsl:with-param name="lastcol" select="$lastcol"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="colsep">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="."/>
<xsl:with-param name="colnum" select="$entry.colnum"/>
<xsl:with-param name="attribute" select="'colsep'"/>
<xsl:with-param name="lastrow" select="$lastrow"/>
<xsl:with-param name="lastcol" select="$lastcol"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="valign">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="."/>
<xsl:with-param name="colnum" select="$entry.colnum"/>
<xsl:with-param name="attribute" select="'valign'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="align">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="."/>
<xsl:with-param name="colnum" select="$entry.colnum"/>
<xsl:with-param name="attribute" select="'align'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="char">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="."/>
<xsl:with-param name="colnum" select="$entry.colnum"/>
<xsl:with-param name="attribute" select="'char'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="charoff">
<xsl:call-template name="inherited.table.attribute">
<xsl:with-param name="entry" select="."/>
<xsl:with-param name="colnum" select="$entry.colnum"/>
<xsl:with-param name="attribute" select="'charoff'"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$spans != '' and not(starts-with($spans,'0:'))">
<xsl:call-template name="entry-template">
<xsl:with-param name="col" select="$col + 1"/>
<xsl:with-param name="spans" select="substring-after($spans,':')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$entry.colnum &gt; $col">
<xsl:call-template name="empty.table.cell">
<xsl:with-param name="colnum" select="$col"/>
</xsl:call-template>
<xsl:call-template name="entry-template">
<xsl:with-param name="col" select="$col+1"/>
<xsl:with-param name="spans" select="substring-after($spans,':')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="cell.content">
<fo:block>
<!-- highlight this entry? -->
<xsl:if test="ancestor::THEAD">
<xsl:attribute name="font-weight">bold</xsl:attribute>
</xsl:if>
<!-- are we missing any indexterms? -->
<xsl:if test="not(preceding-sibling::ENTRY)
and not(parent::ROW/preceding-sibling::ROW)">
<!-- this is the first entry of the first row -->
<xsl:if test="ancestor::THEAD or
(ancestor::TBODY
and not(ancestor::TBODY/preceding-sibling::THEAD
or ancestor::TBODY/preceding-sibling::TBODY))">
<!-- of the thead or the first tbody -->
<xsl:apply-templates select="ancestor::TGROUP/preceding-sibling::INDEXTERM"/>
</xsl:if>
</xsl:if>
<xsl:choose>
<xsl:when test="$empty.cell">
<xsl:text>&#160;</xsl:text>
</xsl:when>
<xsl:otherwise>
<!-- <xsl:call-template name="zero_width_space_1">
<xsl:with-param name="data" select="."/>
</xsl:call-template> -->
<!-- 川航 PRETOPIC内 不显示适用性 -->
<xsl:choose>
<xsl:when test="ancestor::PRETOPIC">
<xsl:apply-templates select="*[not(self::EFFECT)]"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</xsl:variable>
<fo:table-cell xsl:use-attribute-sets="table.cell.padding">
<xsl:call-template name="ENTRY"/>
<xsl:if test="$rowsep &gt; 0">
<xsl:call-template name="border">
<xsl:with-param name="side" select="'bottom'"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="$colsep &gt; 0">
<xsl:call-template name="border">
<xsl:with-param name="side" select="'right'"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="@MOREROWS != ''">
<xsl:variable name="numRowSpan">
<xsl:choose>
<xsl:when test="@MOREROWS != '' and @MOREROWS &gt; 0">
<xsl:variable name="v_moreRowPlusOne">
<xsl:value-of select="number(@MOREROWS + 1)"/>
</xsl:variable>
<xsl:choose>
<!-- 优化跨行 ROW 适用性显示 川航 PRETOPIC内,不显示适用性 -->
<xsl:when
test="parent::ROW/following-sibling::ROW[position() &lt; $v_moreRowPlusOne]/EFFECT and not(ancestor::PRETOPIC)">
<xsl:value-of
select="count(parent::ROW/following-sibling::ROW[position() &lt; $v_moreRowPlusOne]/EFFECT) + number(@MOREROWS + 1)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="number(@MOREROWS + 1)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="number(@MOREROWS + 1)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:attribute name="number-rows-spanned">
<xsl:value-of select="$numRowSpan"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="$entry.colspan &gt; 1">
<xsl:attribute name="number-columns-spanned">
<xsl:value-of select="$entry.colspan"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="$valign != ''">
<xsl:attribute name="display-align">
<xsl:choose>
<xsl:when test="$valign='top'">before</xsl:when>
<xsl:when test="$valign='middle'">center</xsl:when>
<xsl:when test="$valign='bottom'">after</xsl:when>
<xsl:otherwise>
center
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:if>
<xsl:if test="$align != ''">
<xsl:attribute name="text-align">
<xsl:value-of select="$align"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="$char != ''">
<xsl:attribute name="text-align">
<xsl:value-of select="$char"/>
</xsl:attribute>
</xsl:if>
<!--
<xsl:if test="@CHAROFF">
<xsl:attribute name="charoff">
<xsl:value-of select="@CHAROFF"/>
</xsl:attribute>
</xsl:if>
-->
<!-- ***** first added call to handle _cellfont ***** -->
<xsl:call-template name="just-after-table-cell-stag"/>
<!-- ***** end added line ***** -->
<xsl:copy-of select="$cell.content"/>
<!-- ***** second added call to handle _cellfont ***** -->
<xsl:call-template name="just-before-table-cell-etag"/>
<!-- ***** end added line ***** -->
</fo:table-cell>
<xsl:choose>
<xsl:when test="following-sibling::ENTRY">
<xsl:apply-templates select="following-sibling::ENTRY[1]">
<xsl:with-param name="col" select="$col+$entry.colspan"/>
<xsl:with-param name="spans" select="$following.spans"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="finaltd">
<xsl:with-param name="spans" select="$following.spans"/>
<xsl:with-param name="col" select="$col+$entry.colspan"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="ENTRY" name="sentry" mode="span">
<xsl:param name="col" select="1"/>
<xsl:param name="spans"/>
<xsl:variable name="entry.colnum">
<xsl:call-template name="entry.colnum"/>
</xsl:variable>
<xsl:variable name="entry.colspan">
<xsl:choose>
<xsl:when test="@SPANNAME or @NAMEST">
<xsl:call-template name="calculate.colspan"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="following.spans">
<xsl:call-template name="calculate.following.spans">
<xsl:with-param name="colspan" select="$entry.colspan"/>
<xsl:with-param name="spans" select="$spans"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$spans != '' and not(starts-with($spans,'0:'))">
<xsl:value-of select="number(substring-before($spans, ':')) - 1"/>
<xsl:text>:</xsl:text>
<xsl:call-template name="sentry">
<xsl:with-param name="col" select="$col+1"/>
<xsl:with-param name="spans" select="substring-after($spans,':')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$entry.colnum &gt; $col">
<xsl:text>0:</xsl:text>
<xsl:call-template name="sentry">
<xsl:with-param name="col" select="$col+$entry.colspan"/>
<xsl:with-param name="spans" select="$following.spans"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="copy-string">
<xsl:with-param name="count" select="$entry.colspan"/>
<xsl:with-param name="string">
<xsl:choose>
<xsl:when test="@MOREROWS">
<xsl:value-of select="@MOREROWS"/>
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
<xsl:text>:</xsl:text>
</xsl:with-param>
</xsl:call-template>
<xsl:choose>
<xsl:when test="following-sibling::ENTRY">
<xsl:apply-templates select="following-sibling::ENTRY[1]"
mode="span">
<xsl:with-param name="col" select="$col+$entry.colspan"/>
<xsl:with-param name="spans" select="$following.spans"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="sfinaltd">
<xsl:with-param name="spans" select="$following.spans"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="generate.colgroup.raw">
<xsl:param name="cols" select="1"/>
<xsl:param name="count" select="1"/>
<xsl:choose>
<xsl:when test="$count>$cols"></xsl:when>
<xsl:otherwise>
<xsl:call-template name="generate.col.raw">
<xsl:with-param name="countcol" select="$count"/>
</xsl:call-template>
<xsl:call-template name="generate.colgroup.raw">
<xsl:with-param name="cols" select="$cols"/>
<xsl:with-param name="count" select="$count+1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="generate.colgroup">
<xsl:param name="cols" select="1"/>
<xsl:param name="count" select="1"/>
<xsl:choose>
<xsl:when test="$count>$cols"></xsl:when>
<xsl:otherwise>
<xsl:call-template name="generate.col">
<xsl:with-param name="countcol" select="$count"/>
</xsl:call-template>
<xsl:call-template name="generate.colgroup">
<xsl:with-param name="cols" select="$cols"/>
<xsl:with-param name="count" select="$count+1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="generate.col.raw">
<!-- generate the table-column for column countcol -->
<xsl:param name="countcol">1</xsl:param>
<xsl:param name="colspecs" select="./COLSPEC"/>
<xsl:param name="count">1</xsl:param>
<xsl:param name="colnum">1</xsl:param>
<xsl:choose>
<xsl:when test="$count>count($colspecs)">
<fo:table-column column-number="{$countcol}"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="colspec" select="$colspecs[$count=position()]"/>
<xsl:variable name="colspec.colnum">
<xsl:choose>
<xsl:when test="$colspec/@COLNUM">
<xsl:value-of select="$colspec/@COLNUM"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$colnum"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="colspec.colwidth">
<xsl:choose>
<xsl:when test="$colspec/@COLWIDTH">
<xsl:value-of select="$colspec/@COLWIDTH"/>
</xsl:when>
<xsl:otherwise>1*</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$colspec.colnum = $countcol">
<fo:table-column column-number="{$countcol}">
<xsl:attribute name="column-width">
<xsl:value-of select="$colspec.colwidth"/>
</xsl:attribute>
</fo:table-column>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="generate.col.raw">
<xsl:with-param name="countcol" select="$countcol"/>
<xsl:with-param name="colspecs" select="$colspecs"/>
<xsl:with-param name="count" select="$count+1"/>
<xsl:with-param name="colnum">
<xsl:choose>
<xsl:when test="$colspec/@COLNUM">
<xsl:value-of select="$colspec/@COLNUM + 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$colnum + 1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="generate.col">
<!-- generate the table-column for column countcol -->
<xsl:param name="countcol">1</xsl:param>
<xsl:param name="colspecs" select="./COLSPEC"/>
<xsl:param name="count">1</xsl:param>
<xsl:param name="colnum">1</xsl:param>
<xsl:choose>
<xsl:when test="$count > count($colspecs)">
<fo:table-column column-number="{$countcol}">
<xsl:variable name="colwidth">
<xsl:call-template name="calc.column.width"/>
</xsl:variable>
<xsl:if test="$colwidth != 'proportional-column-width(1)'">
<xsl:attribute name="column-width">
<xsl:value-of select="$colwidth"/>
</xsl:attribute>
</xsl:if>
</fo:table-column>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="colspec" select="$colspecs[$count = position()]"/>
<xsl:variable name="colspec.colnum">
<xsl:choose>
<xsl:when test="$colspec/@COLNUM">
<xsl:value-of select="$colspec/@COLNUM"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$colnum"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="colspec.colwidth">
<xsl:choose>
<xsl:when test="$colspec/@COLWIDTH">
<xsl:value-of select="$colspec/@COLWIDTH"/>
</xsl:when>
<xsl:otherwise>1*</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$colspec.colnum = $countcol">
<fo:table-column column-number="{$countcol}">
<xsl:variable name="colwidth">
<xsl:call-template name="calc.column.width">
<xsl:with-param name="colwidth">
<xsl:value-of select="$colspec.colwidth"/>
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:if test="$colwidth != 'proportional-column-width(1)'">
<xsl:attribute name="column-width">
<xsl:value-of select="$colwidth"/>
</xsl:attribute>
</xsl:if>
</fo:table-column>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="generate.col">
<xsl:with-param name="countcol" select="$countcol"/>
<xsl:with-param name="colspecs" select="$colspecs"/>
<xsl:with-param name="count" select="$count + 1"/>
<xsl:with-param name="colnum">
<xsl:choose>
<xsl:when test="$colspec/@COLNUM">
<xsl:value-of select="$colspec/@COLNUM + 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$colnum + 1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="calc.column.width">
<xsl:param name="colwidth">1*</xsl:param>
<!-- Ok, the colwidth could have any one of the following forms: -->
<!-- 1* = proportional width -->
<!-- 1unit = 1.0 units wide -->
<!-- 1 = 1pt wide -->
<!-- 1*+1unit = proportional width + some fixed width -->
<!-- 1*+1 = proportional width + some fixed width -->
<xsl:variable name="lower-case-colwidth">
<xsl:call-template name="lower-case">
<xsl:with-param name="parameter" select="$colwidth"/>
</xsl:call-template>
</xsl:variable>
<xsl:text>proportional-column-width(</xsl:text>
<xsl:variable name="total-colwidth">
<xsl:variable name="width">
<xsl:for-each select="child::COLSPEC">
<xsl:choose>
<xsl:when test="@COLWIDTH">
<xsl:value-of select="concat(@COLWIDTH, '+')"/>
</xsl:when>
<xsl:otherwise>1*+</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:call-template name="lower-case">
<xsl:with-param name="parameter" select="$width"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<!-- 无 COLSPEC 标签时,设每列列宽相同。 modify by:ZJX-->
<xsl:when test="not(child::COLSPEC)">
<xsl:value-of>1</xsl:value-of>
</xsl:when>
<xsl:when test="contains($total-colwidth, '*')">
<xsl:variable name="proportional-colwidth">
<xsl:call-template name="output-tokens">
<xsl:with-param name="list" select="$total-colwidth"/>
<xsl:with-param name="separator">+</xsl:with-param>
<xsl:with-param name="type">P</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="fixed-colwidth">
<xsl:call-template name="output-tokens">
<xsl:with-param name="list" select="$total-colwidth"/>
<xsl:with-param name="separator">+</xsl:with-param>
<xsl:with-param name="type">F</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="X">
<xsl:choose>
<xsl:when test="/*[1]/*[1]/ROTATE = 'Y'">
<!-- 横版打印表格总宽度:297mm - 2*10mm -3pt = 785pt -->
<xsl:value-of select="(785 - number($fixed-colwidth)) div number($proportional-colwidth)"/>
</xsl:when>
<xsl:when test="/*[1]/*[1]/ROTATE = 'N'">
<!-- 竖版打印表格总宽度:210mm - 2*10mm -3pt = 537pt -->
<xsl:value-of select="(537 - number($fixed-colwidth)) div number($proportional-colwidth)"/>
</xsl:when>
<xsl:when test="/*[1]/descendant::*[@ROTATE = '1']">
<xsl:value-of select="(785 - number($fixed-colwidth)) div number($proportional-colwidth)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="(537 - number($fixed-colwidth)) div number($proportional-colwidth)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains($lower-case-colwidth, '+')">
<xsl:variable name="first-width">
<xsl:value-of select="normalize-space(substring-before($lower-case-colwidth, '+'))"/>
</xsl:variable>
<xsl:variable name="second-width">
<xsl:value-of select="normalize-space(substring-after($lower-case-colwidth, '+'))"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains($first-width, '*')">
<xsl:variable name="first-width-after-conversion">
<xsl:call-template name="proportional-measure">
<xsl:with-param name="colwidth" select="$first-width"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="second-width-after-conversion">
<xsl:call-template name="fixed-measure">
<xsl:with-param name="colwidth" select="$second-width"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of
select="number($first-width-after-conversion) * number($X) + number($second-width-after-conversion)"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="first-width-after-conversion">
<xsl:call-template name="fixed-measure">
<xsl:with-param name="colwidth" select="$first-width"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="second-width-after-conversion">
<xsl:call-template name="proportional-measure">
<xsl:with-param name="colwidth" select="$second-width"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of
select="number($first-width-after-conversion) + number($second-width-after-conversion) * number($X) "/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="not(contains($lower-case-colwidth, '+')) and contains($lower-case-colwidth, '*')">
<xsl:variable name="proportional-colwidth">
<xsl:call-template name="proportional-measure">
<xsl:with-param name="colwidth" select="$lower-case-colwidth"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="number($proportional-colwidth) * number($X)"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="fixed-measure">
<xsl:with-param name="colwidth" select="$lower-case-colwidth"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="fixed-measure">
<xsl:with-param name="colwidth" select="$lower-case-colwidth"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
<xsl:text>)</xsl:text>
</xsl:template>
<xsl:template name="output-tokens">
<xsl:param name="list"/>
<xsl:param name="separator"/>
<xsl:param name="type"/>
<xsl:variable name="newlist" select="concat($list, $separator)"/>
<xsl:variable name="first" select="substring-before($newlist, $separator)"/>
<xsl:variable name="remaining" select="substring-after($newlist, $separator)"/>
<xsl:variable name="current-width">
<xsl:choose>
<xsl:when test="contains($first, '*') and contains($type, 'P')">
<xsl:call-template name="proportional-measure">
<xsl:with-param name="colwidth" select="$first"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="not(contains($first, '*')) and contains($type, 'F')">
<xsl:call-template name="fixed-measure">
<xsl:with-param name="colwidth" select="$first"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of>0</xsl:value-of>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="next-width">
<xsl:choose>
<xsl:when test="substring-before($remaining, $separator) != ''">
<xsl:call-template name="output-tokens">
<xsl:with-param name="list" select="$remaining"/>
<xsl:with-param name="separator" select="$separator"/>
<xsl:with-param name="type" select="$type"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of>0</xsl:value-of>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="number($current-width) + number($next-width)"/>
</xsl:template>
<xsl:template name="proportional-measure">
<xsl:param name="colwidth"/>
<xsl:choose>
<xsl:when test="'*' = $colwidth">1</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-before($colwidth, '*')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="fixed-measure">
<xsl:param name="colwidth"/>
<xsl:choose>
<xsl:when test="contains($colwidth, 'cm')">
<xsl:variable name="width">
<xsl:value-of select="substring-before($colwidth, 'cm')"/>
</xsl:variable>
<xsl:value-of select="$width * 28"/>
</xsl:when>
<xsl:when test="contains($colwidth, 'mm')">
<xsl:variable name="width">
<xsl:value-of select="substring-before($colwidth, 'mm')"/>
</xsl:variable>
<xsl:value-of select="$width * 3"/>
</xsl:when>
<!-- " pi/pc " (picas) -->
<xsl:when test="contains($colwidth, 'pi') or contains($colwidth, 'pc')">
<xsl:variable name="width">
<xsl:value-of select="substring-before($colwidth, 'p')"/>
</xsl:variable>
<xsl:value-of select="$width * 12"/>
</xsl:when>
<!-- " in " (inches) -->
<xsl:when test="contains($colwidth, 'in')">
<xsl:variable name="width">
<xsl:value-of select="substring-before($colwidth, 'in')"/>
</xsl:variable>
<xsl:value-of select="$width * 72"/>
</xsl:when>
<!-- pixel, px = pt * DPI / 72) -->
<xsl:when test="contains($colwidth, 'px')">
<xsl:variable name="width">
<xsl:value-of select="substring-before($colwidth, 'px')"/>
</xsl:variable>
<xsl:value-of select="$width * 0.75"/>
</xsl:when>
<xsl:when test="contains($colwidth, 'em')">
<xsl:variable name="width">
<xsl:value-of select="substring-before($colwidth, 'em')"/>
</xsl:variable>
<xsl:value-of select="$width * 12"/>
</xsl:when>
<xsl:when test="contains($colwidth, 'pt')">
<xsl:variable name="width">
<xsl:value-of select="substring-before($colwidth, 'pt')"/>
</xsl:variable>
<xsl:value-of select="$width"/>
</xsl:when>
<!-- 无单位,默认为" pt " (points) -->
<xsl:otherwise>
<xsl:value-of select="$colwidth"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="lower-case">
<xsl:param name="parameter"/>
<xsl:variable name="lcletters">abcdefghijklmnopqrstuvwxyz</xsl:variable>
<xsl:variable name="ucletters">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>
<xsl:value-of select="translate($parameter,$ucletters,$lcletters)"/>
</xsl:template>
<xsl:template name="zero_width_space_1">
<xsl:param name="data"/>
<xsl:param name="counter" select="0"/>
<xsl:choose>
<xsl:when test="$counter &lt; string-length($data)">
<!-- 每个字符后增加零宽空格 -->
<xsl:value-of select='concat(substring($data, $counter, 1), "&#8203;")'/>
<xsl:call-template name="zero_width_space_2">
<xsl:with-param name="data" select="$data"/>
<xsl:with-param name="counter" select="$counter + 1"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$counter = string-length($data)">
<xsl:value-of select='concat(substring($data, $counter, 1), "&#8203;")'/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>
<xsl:template name="zero_width_space_2">
<xsl:param name="data"/>
<xsl:param name="counter"/>
<xsl:value-of select='concat(substring($data, $counter, 1), "&#8203;")'/>
<xsl:call-template name="zero_width_space_1">
<xsl:with-param name="data" select="$data"/>
<xsl:with-param name="counter" select="$counter + 1"/>
</xsl:call-template>
</xsl:template>
<!-- The templates above call various named templates to set style
properties on various table related FOs. When this file is
used via TurboStyler, appropriately defined named templates
are emitted by TurboStyler.
If this file is used in another context, the following (commented out)
empty named template definitions can be used:-->
<xsl:template name="TGROUP.first"/>
<xsl:template name="TGROUP.notfirst"/>
<xsl:template name="THEAD"/>
<xsl:template name="TFOOT"/>
<xsl:template name="TBODY"/>
<xsl:template name="ROW"/>
<xsl:template name="ENTRY"/>
<xsl:template name="just-before-table-cell-etag"/>
<xsl:template name="just-after-table-cell-stag"/>
<!-- **********************************************************
end tbl.xsl
********************************************************** -->
</xsl:stylesheet>
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:se="http://www.syntext.com/XSL/Format-1.0" version="2.0"
xmlns:xsk="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="EFFECT">
<xsl:variable name="isStandAlone">
<xsl:call-template name="isStandAlone"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$show_eff='Y'">
<xsl:call-template name="showEffect">
<xsl:with-param name="showEFF" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="showEffect">
<xsl:with-param name="showEFF" select="false()"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="CHGDESC">
<!--<fo:block space-before="3pt" space-after="3pt"><xsl:attribute name="font-style">italic</xsl:attribute><xsl:value-of select="concat('CHGDESC: ', text())" /></fo:block>-->
</xsl:template>
<xsl:template match="SUBTASK">
<xsl:variable name="needToShow">
<xsl:call-template name="showAncestorEFFECT"/>
</xsl:variable>
<!-- 工卡最后的非例行工作记录不显示适用性 -->
<!--
<xsl:if test="$needToShow='true' and not(@CHAPNBR='99' and @SECTNBR='99')">
<fo:block>
<xsl:apply-templates select="ancestor::*[child::EFFECT][1]/EFFECT"/>
</fo:block>
</xsl:if>
-->
<xsl:choose>
<xsl:when test="preceding-sibling::*[1]/EFFECT/@EFFRG">
<xsk:choose>
<xsl:when test="EFFECT/@EFFRG != preceding-sibling::*[1]/EFFECT/@EFFRG">
<xsl:apply-templates select="EFFECT"/>
</xsl:when>
</xsk:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="ancestor::*[child::EFFECT][1]/EFFECT/@EFFRG and EFFECT/@EFFRG = ancestor::*[child::EFFECT][1]/EFFECT/@EFFRG"/>
<xsl:otherwise>
<xsl:if test="child::EFFECT">
<xsl:apply-templates select="EFFECT"/>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
<fo:block padding="0.5mm">
<xsl:variable name="title">
<xsl:value-of select="'subtask_title'"/>
</xsl:variable>
<xsl:call-template name="generateTaskTitle">
<xsl:with-param name="title" select="$title"/>
</xsl:call-template>
</fo:block>
<fo:block padding="0.5mm">
<xsl:apply-templates select="*[not(self::EFFECT)]"/>
</fo:block>
</xsl:template>
<xsl:template match="reqSupportEquips">
<fo:block>
<xsl:apply-templates select="supportEquipDescrGroup"/>
</fo:block>
</xsl:template>
<xsl:template match="supportEquipDescrGroup">
<fo:table>
<fo:table-column column-number="1" column-width="25%"/>
<fo:table-column column-number="2" column-width="15%"/>
<fo:table-column column-number="3" column-width="60%"/>
<fo:table-header>
<fo:table-cell background-color="#E5E4E2" border="1pt solid black" text-align="center">
<fo:block font-weight="bold" padding="4pt">REFERENCE</fo:block>
</fo:table-cell>
<fo:table-cell background-color="#E5E4E2" border="1pt solid black" text-align="center">
<fo:block font-weight="bold" padding="4pt">QTY</fo:block>
</fo:table-cell>
<fo:table-cell background-color="#E5E4E2" border="1pt solid black" text-align="center">
<fo:block font-weight="bold" padding="4pt">DESIGNATION</fo:block>
</fo:table-cell>
</fo:table-header>
<fo:table-body>
<xsl:apply-templates select="supportEquipDescr"/>
</fo:table-body>
</fo:table>
</xsl:template>
<xsl:template match="supportEquipDescr">
<fo:table-row>
<fo:table-cell border="1pt solid black">
<fo:block margin-left="2mm" padding="4pt" text-align="left">
<xsl:apply-templates select="toolRef"/>
</fo:block>
</fo:table-cell>
<fo:table-cell border="1pt solid black">
<fo:block margin-left="2mm" padding="4pt" text-align="left">
<xsl:apply-templates select="reqQuantity"/>
</fo:block>
</fo:table-cell>
<fo:table-cell border="1pt solid black">
<fo:block margin-left="2mm" padding="4pt" text-align="left">
<xsl:apply-templates select="name"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
<xsl:template match="toolRef">
<xsl:value-of select="@toolNumber"/>
</xsl:template>
<xsl:template match="reqQuantity">
<xsl:value-of select="text()"/>
</xsl:template>
<xsl:template match="name">
<xsl:value-of select="text()"/>
</xsl:template>
<!--===============================================-->
<!--Template for superscript -->
<!--===============================================-->
<xsl:template match="SUPERSCRIPT | SUPER | superscript | super">
<fo:inline font-size="8pt" baseline-shift="super">
<!--<xsl:apply-templates/>-->
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
<!--===============================================-->
<!--Template for subscript -->
<!--===============================================-->
<xsl:template match="SUBSCRIPT | SUB | subscript | sub">
<fo:inline font-size="8pt" baseline-shift="sub">
<!--<xsl:apply-templates/>-->
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
<!-- 'CBDATA' ELEMENT zjx -->
<xsl:template match="CBLST">
<fo:table text-align="left">
<fo:table-column column-width="25%" border="1px solid black"/>
<fo:table-column column-width="25%" border="1px solid black"/>
<fo:table-column column-width="25%" border="1px solid black"/>
<fo:table-column column-width="25%" border="1px solid black"/>
<fo:table-header font-weight="bold" text-align="center">
<fo:table-row border="1px solid black" background-color="#DCDCDC">
<fo:table-cell>
<fo:block padding="0.5mm" margin="0.5mm">
<xsl:text>面板 PANEL</xsl:text>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block padding="0.5mm" margin="0.5mm">
<xsl:text>说明 DESIGNATION</xsl:text>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block padding="0.5mm" margin="0.5mm">
<xsl:text>功能号 FIN</xsl:text>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block padding="0.5mm" margin="0.5mm">
<xsl:text>位置 LOCATION</xsl:text>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-header>
<fo:table-body>
<xsl:apply-templates select="CBSUBLST"/>
</fo:table-body>
</fo:table>
</xsl:template>
<xsl:template match="CBSUBLST">
<!--<fo:table-row border="1px solid black">
<fo:table-cell number-columns-spanned="4">
<fo:block padding="0.5mm" margin="0.5mm">
<xsl:text>FOR FIN </xsl:text>
<xsl:value-of select="child::EIN"/>
<xsl:text> (</xsl:text>
<xsl:value-of select="child::EQUNAME"/>
<xsl:text>)</xsl:text>
</fo:block>
</fo:table-cell>
</fo:table-row>-->
<xsl:choose>
<xsl:when test="not(child::*)">
<fo:table-row>
<fo:table-cell border="1pt solid black" number-columns-spanned="4">
<fo:block padding="0.5mm" start-indent="0">
<xsl:text></xsl:text>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:when>
<xsl:otherwise>
<xsl:if test="*[not(self::CBDATA)]">
<fo:table-row>
<fo:table-cell border="1pt solid black" number-columns-spanned="4">
<fo:block padding="0.5mm" start-indent="0">
<xsl:apply-templates select="*[not(self::CBDATA)]"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:if>
<xsl:apply-templates select="CBDATA"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="CBDATA">
<xsl:choose>
<xsl:when test="preceding-sibling::*[1]/EFFECT/@EFFRG">
<xsk:choose>
<xsl:when test="EFFECT/@EFFRG != preceding-sibling::*[1]/EFFECT/@EFFRG">
<fo:table-row border="1px solid black">
<fo:table-cell number-columns-spanned="4">
<fo:block text-align="left">
<xsl:apply-templates select="EFFECT"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:when>
</xsk:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="parent::CBSUBLST/preceding-sibling::CBSUBLST and EFFECT/@EFFRG != parent::CBSUBLST/preceding-sibling::CBSUBLST[1]/CBDATA[last()]/EFFECT/@EFFRG">
<fo:table-row border="1px solid black">
<fo:table-cell number-columns-spanned="4">
<fo:block text-align="left">
<xsl:apply-templates select="EFFECT"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:when>
<xsl:when test="ancestor::*[child::EFFECT][1]/EFFECT/@EFFRG and EFFECT/@EFFRG = ancestor::*[child::EFFECT][1]/EFFECT/@EFFRG"/>
<xsl:otherwise>
<xsl:if test="child::EFFECT">
<fo:table-row border="1px solid black">
<fo:table-cell number-columns-spanned="4">
<fo:block text-align="left">
<xsl:apply-templates select="EFFECT"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
<fo:table-row border="1px solid black">
<fo:table-cell>
<fo:block padding="0.5mm" margin="0.5mm">
<xsl:value-of select="PAN"/>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block padding="0.5mm" margin="0.5mm">
<xsl:value-of select="CBNAME"/>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block padding="0.5mm" margin="0.5mm">
<xsl:value-of select="replace(CB,'-','')"/>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block padding="0.5mm" margin="0.5mm">
<xsl:value-of select="CBLOC"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
<!-- 未使用,未有入口 -->
<xsl:template match="ATANBR">
<fo:inline>
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
<xsl:template match="MFR">
<fo:inline>
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
<xsl:template match="SBNBR">
<fo:inline>
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
<xsl:template match="TITLE">
<xsl:if test="$is_all or $is_en">
<xsl:call-template name="showTitle"/>
</xsl:if>
</xsl:template>
<xsl:template match="TITLEC">
<xsl:if test="$is_all or $is_cn">
<xsl:call-template name="showTitle"/>
</xsl:if>
</xsl:template>
<xsl:template match="SBEFFC">
<xsl:choose>
<xsl:when test="ancestor::REFINT">
<fo:inline font-style="italic" font-weight="bold" padding="0.5mm">
<xsl:variable name="eff_prefix">** ON A/C:</xsl:variable>
<xsl:variable name="effGrp">
<xsl:value-of select="translate(@EFFRG,' ','')"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$effGrp = '001999'">
<xsl:value-of select="concat(@SBCOND,' ','SB',' ',@SBNBR,' for A/C ALL')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(@SBCOND,' ','SB',' ',@SBNBR,' for A/C ')"/>
<xsl:call-template name="getEffGrpStr">
<xsl:with-param name="eff" select="$effGrp"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</fo:inline>
</xsl:when>
<xsl:otherwise>
<fo:block font-style="italic" font-weight="bold" padding="0.5mm">
<xsl:variable name="eff_prefix">** ON A/C:</xsl:variable>
<xsl:variable name="effGrp">
<xsl:value-of select="translate(@EFFRG,' ','')"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$effGrp = '001999'">
<xsl:value-of select="concat(' ',@SBCOND,' ','SB',' ',@SBNBR,' for A/C ALL ')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(' ',@SBCOND,' ','SB',' ',@SBNBR, ' for A/C ')"/>
<xsl:call-template name="getEffGrpStr">
<xsl:with-param name="eff" select="$effGrp"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="SBEFF">
<xsl:choose>
<xsl:when test="ancestor::REFINT">
<fo:inline font-style="italic" font-weight="bold" padding="0.5mm">
<xsl:variable name="eff_prefix">** ON A/C:</xsl:variable>
<xsl:variable name="effGrp">
<xsl:value-of select="translate(@EFFRG,' ','')"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$effGrp = '001999'">
<xsl:value-of select="concat(@SBCOND,' ','SB',' ',@SBNBR,' for A/C ALL')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(@SBCOND,' ','SB',' ',@SBNBR,' for A/C ')"/>
<xsl:call-template name="getEffGrpStr">
<xsl:with-param name="eff" select="$effGrp"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</fo:inline>
</xsl:when>
<xsl:otherwise>
<fo:block font-style="italic" font-weight="bold" padding="0.5mm">
<xsl:variable name="eff_prefix">** ON A/C:</xsl:variable>
<xsl:variable name="effGrp">
<xsl:value-of select="translate(@EFFRG,' ','')"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$effGrp = '001999'">
<xsl:value-of select="concat(' ',@SBCOND,' ','SB',' ',@SBNBR,' for A/C ALL ')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(' ',@SBCOND,' ','SB',' ',@SBNBR,' for A/C')"/>
<xsl:call-template name="getEffGrpStr">
<xsl:with-param name="eff" select="$effGrp"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="CONEFFECT">
<xsl:call-template name="showConEffect"/>
</xsl:template>
<!-- EQUIPMENTS elements display zjx -->
<!-- The EQULST structure is delivered only in a PRCITEM element contained in a PRCITEM1 element ( EQULST is allowed only at first level of the PRCLIST1 structure). Extract from USER GUIDE-->
<xsl:template match="EQULST">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
</xsl:stylesheet>
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:param name="default.table.width" select="''"/>
<xsl:param name="table.border.color" select="'black'"/>
<xsl:param name="table.border.style" select="'solid'"/>
<xsl:param name="table.border.thickness" select="'0.5pt'"/>
<xsl:param name="table.cell.padding.amount" select="'2pt'"/>
<xsl:param name="inhibit-default-colwidth-emission" select='1'/>
</xsl:stylesheet>
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:se="http://www.syntext.com/XSL/Format-1.0"
version="2.0">
<!--======================================================-->
<!-- set the global variable of customer name -->
<!--======================================================-->
<xsl:variable name="g_customer_name" select="''"/>
<!--======================================================-->
<!-- set the global variable of manual name -->
<!--======================================================-->
<xsl:variable name="g_manual_name" select="''"/>
<!-- <xsl:variable name="metaFilePath" select="concat($printScriptPath,$jcId,'_Param.xml')"/>
<xsl:variable name="metaFileDoc" select="document($metaFilePath)"/> -->
<xsl:variable name="lowerCase">abcdefghijklmnopqrstuvwxyz</xsl:variable>
<xsl:variable name="upperCase">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>
<!-- <xsl:variable name="metaFilePathWP" select="''"/>
<xsl:variable name="compatible" select="false()"/> -->
</xsl:stylesheet>
\ No newline at end of file
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format" version="2.0">
<xsl:import href="common/variables.xsl"/>
<xsl:import href="common/params.xsl"/>
<xsl:import href="common/functions.xsl"/>
<xsl:import href="common/page-sets.xsl"/>
<xsl:import href="common/graphic.xsl"/>
<xsl:import href="common/list.xsl"/>
<xsl:import href="common/table_uppercase.xsl"/>
<xsl:import href="common/taskcard-elements.xsl"/>
<xsl:import href="common/change_tracking.xsl"/>
<xsl:import href="common/appendix.xsl"/>
<xsl:import href="common/link.xsl"/>
<xsl:import href="common/cwnfo.xsl"/>
<xsl:import href="common/environment.xsl"/>
<xsl:import href="common/3of9.xsl"/>
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!--======================================================-->
<!--match the root of the document -->
<!--======================================================-->
<xsl:template match="/">
<fo:root>
<xsl:apply-templates/>
</fo:root>
</xsl:template>
<xsl:template match="JOBCARD">
<xsl:call-template name="createroot"/>
</xsl:template>
<xsl:template match="TASK">
<xsl:choose>
<xsl:when test="parent::TOPIC">
<xsl:apply-templates select="/JOBCARD/CEP/TOPIC/TASK/*"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="createroot"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="CEP">
<xsl:choose>
<xsl:when test="parent::JOBCARD"></xsl:when>
<xsl:otherwise>
<xsl:call-template name="createroot"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="createroot">
<xsl:choose>
<xsl:when test=".//SMJC-HEADER">
<xsl:choose>
<xsl:when test=".//SMJC-HEADER/ROTATE = 'Y'">
<xsl:call-template name="smjcRoot">
<xsl:with-param name="printHorizontal" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:when test=".//SMJC-HEADER/ROTATE = 'N'">
<xsl:call-template name="smjcRoot"/>
</xsl:when>
<xsl:when test="descendant::*[@ROTATE = '1']">
<xsl:call-template name="smjcRoot">
<xsl:with-param name="printHorizontal" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="smjcRoot"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test=".//NRCJC-HEADER">
<xsl:choose>
<xsl:when test=".//NRCJC-HEADER/ROTATE = 'Y'">
<xsl:call-template name="nrcjcRoot">
<xsl:with-param name="printHorizontal" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:when test=".//NRCJC-HEADER/ROTATE = 'N'">
<xsl:call-template name="nrcjcRoot"/>
</xsl:when>
<xsl:when test="descendant::*[@ROTATE = '1']">
<xsl:call-template name="nrcjcRoot">
<xsl:with-param name="printHorizontal" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="nrcjcRoot"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test=".//CMJC-HEADER">
<xsl:choose>
<xsl:when test=".//CMJC-HEADER/ROTATE = 'Y'">
<xsl:call-template name="cmjcRoot">
<xsl:with-param name="printHorizontal" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:when test=".//CMJC-HEADER/ROTATE = 'N'">
<xsl:call-template name="cmjcRoot"/>
</xsl:when>
<xsl:when test="descendant::*[@ROTATE = '1']">
<xsl:call-template name="cmjcRoot">
<xsl:with-param name="printHorizontal" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="cmjcRoot"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test=".//LMJC-HEADER">
<xsl:choose>
<xsl:when test=".//LMJC-HEADER/ROTATE = 'Y'">
<xsl:call-template name="lmjcRoot">
<xsl:with-param name="printHorizontal" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:when test=".//LMJC-HEADER/ROTATE = 'N'">
<xsl:call-template name="lmjcRoot"/>
</xsl:when>
<xsl:when test="descendant::*[@ROTATE = '1']">
<xsl:call-template name="lmjcRoot">
<xsl:with-param name="printHorizontal" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="lmjcRoot"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test=".//EOTK-HEADER or .//TOTK-HEADER">
<xsl:choose>
<xsl:when test=".//EOTK-HEADER/ROTATE = 'Y' or .//TOTK-HEADER/ROTATE = 'Y'">
<xsl:call-template name="otkRoot">
<xsl:with-param name="printHorizontal" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:when test=".//EOTK-HEADER/ROTATE = 'N' or .//TOTK-HEADER/ROTATE = 'N'">
<xsl:call-template name="otkRoot"/>
</xsl:when>
<xsl:when test="descendant::*[@ROTATE = '1']">
<xsl:call-template name="otkRoot">
<xsl:with-param name="printHorizontal" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="otkRoot"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="descendant::*[@ROTATE = '1']">
<xsl:call-template name="noheaderRoot">
<xsl:with-param name="printHorizontal" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="noheaderRoot"/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="TOPIC-GROUP">
<fo:block width="100%" background-color="#cccccc" padding-top="3pt" padding-bottom="2pt" text-align="center">
<fo:block font-weight="bold">
<xsl:value-of select="TITLEC"/>
<xsl:text></xsl:text>
<xsl:value-of select="TITLE"/>
</fo:block>
</fo:block>
</xsl:template>
<xsl:template match="PARA">
<xsl:choose>
<xsl:when test="parent::RECORD-LINE">
<fo:inline>
<xsl:apply-templates/>
</fo:inline>
</xsl:when>
<xsl:when test="parent::RECORD">
<fo:inline>
<xsl:value-of select="text()"/>
</fo:inline>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="//LMJC-HEADER">
<xsl:choose>
<xsl:when test="parent::ENTRY">
<fo:inline font-size="9pt">
<xsl:apply-templates/>
</fo:inline>
</xsl:when>
<xsl:otherwise>
<fo:block space-before="3pt" space-after="3pt" line-height="120%">
<!-- 给para添加Id属性-->
<xsl:call-template name="generateID"/>
<xsl:apply-templates/>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<fo:block space-before="3pt" space-after="3pt" line-height="120%">
<xsl:if test="@MERGED='TRUE'">
<xsl:attribute name="background">yellow</xsl:attribute>
</xsl:if>
<!-- 给para添加Id属性-->
<xsl:call-template name="generateID"/>
<xsl:apply-templates/>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="PARAC">
<xsl:choose>
<xsl:when test="parent::RECORD-LINE">
<fo:inline>
<xsl:apply-templates/>
</fo:inline>
</xsl:when>
<xsl:when test="parent::STEP/@APPLIC-LMJC = 'NFE' and child::SP">
<fo:inline>
<xsl:call-template name="insertStar"/>
<xsl:value-of select="SP"/>
</fo:inline>
</xsl:when>
<xsl:when test="parent::STEP/@APPLIC-LMJC = 'FE' and child::SP">
<fo:inline>
<xsl:call-template name="insertStar"/>
<xsl:call-template name="insertStar"/>
<xsl:value-of select="SP"/>
</fo:inline>
</xsl:when>
<xsl:when test="//LMJC-HEADER">
<xsl:choose>
<xsl:when test="parent::ENTRY and not(ancestor::THEAD)">
<fo:block font-size="9pt">
<xsl:call-template name="generateID"/>
<xsl:apply-templates/>
</fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block space-before="3pt" space-after="3pt" line-height="120%">
<!-- 给para添加Id属性-->
<xsl:call-template name="generateID"/>
<xsl:apply-templates/>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="//EOTK-HEADER and parent::TOPIC and normalize-space(preceding-sibling::TITLE/text())='' and normalize-space(preceding-sibling::TITLEC/text())=''">
<fo:inline>
<xsl:value-of select="text()"/>
</fo:inline>
</xsl:when>
<xsl:when test="//DRJC-HEADER and parent::TOPIC and normalize-space(preceding-sibling::TITLE/text())='' and normalize-space(preceding-sibling::TITLEC/text())=''">
<fo:inline>
<xsl:value-of select="text()"/>
</fo:inline>
</xsl:when>
<xsl:when test="//QECJC-HEADER and parent::TOPIC and normalize-space(preceding-sibling::TITLE/text())='' and normalize-space(preceding-sibling::TITLEC/text())=''">
<fo:inline>
<xsl:value-of select="text()"/>
</fo:inline>
</xsl:when>
<xsl:otherwise>
<fo:block space-before="3pt" space-after="3pt" line-height="120%">
<xsl:if test="@MERGED='TRUE'">
<xsl:attribute name="background">yellow</xsl:attribute>
</xsl:if>
<!-- 给para添加Id属性-->
<xsl:call-template name="generateID"/>
<xsl:apply-templates/>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="IF-HL">
<fo:inline color="red">
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
<xsl:template match="EFFBLOCK">
<fo:inline text-decoration="underline">
<xsl:value-of select="text()"></xsl:value-of>
</fo:inline>
<fo:inline color="red" text-decoration="none">
<xsl:value-of> (</xsl:value-of>
<xsl:value-of select="@EFFDESC"></xsl:value-of>
<xsl:value-of>) </xsl:value-of>
</fo:inline>
</xsl:template>
<xsl:template match="IF-BOLD">
<fo:inline font-weight="bold">
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
<xsl:template match="TXTGRPHC">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="TXTLINE">
<fo:block white-space-collapse="false" linefeed-treatment="preserve" white-space-treatment="preserve">
<xsl:apply-templates/>
</fo:block>
<xsl:if test=".=''">
<fo:block>
<xsl:value-of select="' '"/>
</fo:block>
</xsl:if>
</xsl:template>
<xsl:template match="EXPD">
<!-- @shiro 2019/09/29 此标签未修改之前 会涉及到航材 和 工步中 需要区分开来 对于航材 和 在工步中 使用不同逻辑 -->
<xsl:choose>
<!-- 如果当前标签的父标签的父标签为 ‘ENTRY’当前为航材 -->
<xsl:when test="..[ancestor::ENTRY]">
<xsl:choose>
<xsl:when test="@EXPDTYPE='AFRM' or @EXPDTYPE='ENG'">
<xsl:variable name="csninfo">
<xsl:value-of select="CSN"/>
</xsl:variable>
<fo:inline>
<xsl:text></xsl:text>
<!-- <xsl:text>IPC-CSN (</xsl:text> -->
<xsl:value-of select="concat(substring($csninfo,1,2),'-',substring($csninfo,3,2),'-',substring($csninfo,5,2),'-',substring($csninfo,7,2)),' ITEM ',substring-after($csninfo,'-')"></xsl:value-of>
<!--<xsl:text>) </xsl:text> -->
</fo:inline>
</xsl:when>
<xsl:otherwise>
<fo:inline>
<xsl:text></xsl:text>
<xsl:value-of select="CSN"/>
<xsl:text></xsl:text>
</fo:inline>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<!-- 如果当前标签的父标签的父标签为 ‘L2ITEM/L3ITEM’当前为工步 -->
<xsl:choose>
<xsl:when test="@EXPDTYPE='AFRM' or @EXPDTYPE='ENG'">
<xsl:variable name="csninfo">
<xsl:value-of select="CSN"/>
</xsl:variable>
<fo:inline>
<xsl:text></xsl:text>
<xsl:text>IPC-CSN (</xsl:text>
<xsl:value-of select="concat(substring($csninfo,1,2),'-',substring($csninfo,3,2),'-',substring($csninfo,5,2),'-',substring($csninfo,7,2)),' ITEM ',substring-after($csninfo,'-')"></xsl:value-of>
<xsl:text>) </xsl:text>
</fo:inline>
</xsl:when>
<xsl:otherwise>
<fo:inline>
<xsl:text></xsl:text>
<xsl:value-of select="CSN"/>
<xsl:text></xsl:text>
</fo:inline>
</xsl:otherwise>
</xsl:choose>
<fo:inline>
<xsl:value-of select="EXPDNAME"/>
<xsl:if test="ITEMNBR != ''">
<xsl:text> (</xsl:text>
<xsl:value-of select="ITEMNBR"/>
<xsl:text>) </xsl:text>
</xsl:if>
</fo:inline>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="CON">
<fo:inline>
<xsl:text></xsl:text>
<xsl:value-of select="CONNAME"/>
<xsl:text> (Material Ref. </xsl:text>
<xsl:value-of select="CONNBR"/>
<xsl:text>) </xsl:text>
</fo:inline>
</xsl:template>
<xsl:template match="TED">
<fo:inline>
<xsl:text></xsl:text>
<xsl:value-of select="TOOLNAME"/>
<xsl:text> (</xsl:text>
<xsl:value-of select="TOOLNBR"/>
<xsl:text>) </xsl:text>
</fo:inline>
</xsl:template>
<xsl:template match="STDNAME|PAN">
<fo:inline>
<xsl:text></xsl:text>
<xsl:value-of select="."/>
<xsl:text></xsl:text>
</fo:inline>
</xsl:template>
<xsl:template match="TOR">
<xsl:apply-templates select="TORVALUE[1]"/>
<xsl:if test="TORVALUE[2] and (TORVALUE[1]/@UNIT != TORVALUE[2]/@UNIT)">
<xsl:text>(</xsl:text>
<xsl:apply-templates select="TORVALUE[2]"/>
</xsl:if>
</xsl:template>
<xsl:template match="TORVALUE">
<xsl:variable name="unit" select="translate(@UNIT,$upperCase,$lowerCase)"/>
<xsl:choose>
<xsl:when test="ancestor::PARAC">
<fo:inline>
<xsl:choose>
<xsl:when test="count(preceding-sibling::TORVALUE)=0">
<xsl:choose>
<xsl:when test="@MAX">
<xsl:choose>
<xsl:when test="contains($unit,'m.n')">
<xsl:value-of select="concat(@MIN,'~',@MAX,' ','m.N','')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(@MIN,'~',@MAX,' ',$unit,'')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="contains($unit,'m.n')">
<xsl:value-of select="concat(@MIN,' ','m.N')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(@MIN,' ',$unit)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="@MAX">
<xsl:choose>
<xsl:when test="contains($unit,'m.n')">
<xsl:value-of select="concat('',@MIN,'~',@MAX,' ','m.N','')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('',@MIN,'~',@MAX,' ',$unit,'')"/>
</xsl:otherwise>
</xsl:choose>
<xsl:text>)</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="contains($unit,'m.n')">
<xsl:value-of select="concat('',@MIN,' ','m.N','')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('',@MIN,' ',$unit,'')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="contains($unit,'m.dan')">
<fo:inline color="red">
<xsl:text>(</xsl:text>
<xsl:value-of select="concat('',format-number(@MIN*10,'#0.00'),'~',format-number(@MAX*10,'#0.00'),' ','NM')"/>
<xsl:text>)</xsl:text>
</fo:inline>
</xsl:if>
</fo:inline>
</xsl:when>
<xsl:otherwise>
<fo:inline>
<xsl:choose>
<xsl:when test="count(preceding-sibling::TORVALUE)=0">
<xsl:choose>
<xsl:when test="@MAX">
<xsl:choose>
<xsl:when test="contains($unit,'m.n')">
<xsl:value-of select="concat('to between ',@MIN,' and ',@MAX,' ','m.N')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('to between ',@MIN,' and ',@MAX,' ',$unit)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="contains($unit,'m.n')">
<xsl:value-of select="concat('to ',@MIN,' ','m.N')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('to ',@MIN,' ',$unit)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="@MAX">
<xsl:choose>
<xsl:when test="contains($unit,'m.n')">
<xsl:value-of select="concat('',@MIN,' and ',@MAX,' ','m.N','')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('',@MIN,' and ',@MAX,' ',$unit,'')"/>
</xsl:otherwise>
</xsl:choose>
<xsl:text>)</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="contains($unit,'m.n')">
<xsl:value-of select="concat('',@MIN,' ','m.N','')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('',@MIN,' ',$unit,'')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="contains($unit,'m.dan')">
<fo:inline color="red">
<xsl:text>(</xsl:text>
<xsl:value-of select="concat('',format-number(@MIN*10,'#0.00'),' and ',format-number(@MAX*10,'#0.00'),' ','NM')"/>
<xsl:text>)</xsl:text>
</fo:inline>
</xsl:if>
</fo:inline>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="RECORD-LINE">
<xsl:choose>
<xsl:when test="//SMJC-HEADER or //NRCJC-HEADER or //TCJC-HEADER or //QECJC-HEADER or //EOTK-HEADER">
<fo:block margin-bottom="6mm">
<fo:inline font-size="12pt" font-weight="bold">
<xsl:apply-templates/>
</fo:inline>
</fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block>
<fo:inline>
<xsl:apply-templates/>
</fo:inline>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="RECORD">
<xsl:choose>
<xsl:when test="@MULTI='Y'">
<fo:block-container border="0.5pt solid black" width="300px" height="150px" wrap-option="wrap" white-space-collapse="true" white-space-treatment="ignore-if-surrounding-linefeed">
<xsl:if test="ancestor::ENTRY">
<xsl:attribute name="width">96%</xsl:attribute>
</xsl:if>
<xsl:if test="@WIDTH">
<xsl:attribute name="width">
<xsl:choose>
<xsl:when test="string(number(@WIDTH)) = 'NaN'">
<xsl:value-of select="@WIDTH"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(@WIDTH, 'px')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:if>
<xsl:if test="@HEIGHT">
<xsl:attribute name="height">
<xsl:choose>
<xsl:when test="string(number(@HEIGHT)) = 'NaN'">
<xsl:value-of select="@HEIGHT"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(@HEIGHT, 'px')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:if>
<fo:block white-space-collapse="true" white-space-treatment="ignore-if-surrounding-linefeed" wrap-option="wrap">
<xsl:if test="@WIDTH">
<xsl:attribute name="width">
<xsl:choose>
<xsl:when test="string(number(@WIDTH)) = 'NaN'">
<xsl:value-of select="@WIDTH"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(@WIDTH, 'px')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:if>
<xsl:if test="@HEIGHT">
<xsl:attribute name="height">
<xsl:choose>
<xsl:when test="string(number(@HEIGHT)) = 'NaN'">
<xsl:value-of select="@HEIGHT"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(@HEIGHT, 'px')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:if>
<xsl:choose>
<xsl:when test="string-length(.) &gt; 0">
<xsl:value-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="isArchive">
<xsl:call-template name="isArchive"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$isArchive='true'">
<xsl:text>N/A</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text></xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</fo:block>
<xsl:choose>
<xsl:when test="@MANDATORY='N'"/>
<xsl:otherwise>
<fo:block end-indent="-10pt" text-align="right" padding="-10pt">
<xsl:if test="ancestor::ENTRY">
<xsl:attribute name="end-indent">
<xsl:value-of select="'-5pt'"/>
</xsl:attribute>
<xsl:attribute name="display-align">
<xsl:value-of select="'after'"/>
</xsl:attribute>
<xsl:attribute name="padding-top">0px</xsl:attribute>
</xsl:if>
<fo:inline color="red">
<xsl:text>*</xsl:text>
</fo:inline>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</fo:block-container>
</xsl:when>
<xsl:otherwise>
<fo:inline border-bottom="0.5px solid black" text-align="center">
<xsl:choose>
<xsl:when test="string-length(.) &gt; 0">
<xsl:text></xsl:text>
<xsl:value-of select="."/>
<xsl:text></xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="isArchive">
<xsl:call-template name="isArchive"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$isArchive='true'">
<xsl:text>    N/A    </xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text></xsl:text>
<!--
<xsl:choose>
<xsl:when test="@WIDTH">
<xsl:variable name="con">
<xsl:value-of select="number(@WIDTH)"/>
</xsl:variable>
<xsl:call-template name="for-loop">
<xsl:with-param name="i">1</xsl:with-param>
<xsl:with-param name="count" select="$con" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:text>                     </xsl:text>
</xsl:otherwise>
</xsl:choose>
-->
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</fo:inline>
<xsl:choose>
<xsl:when test="@MANDATORY='N'"/>
<xsl:otherwise>
<fo:inline color="red">
<xsl:text>*</xsl:text>
</fo:inline>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="SELECTION">
<fo:block>
<xsl:choose>
<xsl:when test="//LMJC-HEADER">
<xsl:attribute name="font-weight">bold</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="margin-bottom">6mm</xsl:attribute>
<xsl:attribute name="font-size">12pt</xsl:attribute>
<xsl:attribute name="font-weight">bold</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<fo:table table-layout="fixed" width="100%" padding="4pt">
<xsl:call-template name="generateID"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell>
<fo:block margin-left="0pt">
<xsl:apply-templates/>
<xsl:variable name="isArchive">
<xsl:call-template name="isArchive"/>
</xsl:variable>
<xsl:choose>
<!-- @TYPE = 'LIST' 时, * 和 SELECTION-ITEM 在一行显示 -->
<xsl:when test="@TYPE = 'LIST'"/>
<!-- 航线卡不论是否必填,在预览、模版、单机 PDF 都不需要显示 * -->
<xsl:when test="//LMJC-HEADER and $isArchive != 'true'">
</xsl:when>
<!-- MANDATORY 为 Y,或者未有 MANDATORY 属性时,显示* (除了航线卡,其他卡以及航线存档都按这个规则显示*) -->
<xsl:when test="@MANDATORY = 'Y' or not(@MANDATORY)">
<fo:inline color="red">
<xsl:text>*</xsl:text>
</fo:inline>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:block>
</xsl:template>
<xsl:template match="SELECTION-ITEM">
<xsl:choose>
<xsl:when test="parent::SELECTION[@TYPE = 'LIST']">
<fo:table table-layout="fixed" width="100%">
<xsl:call-template name="generateID"/>
<!-- <xsl:call-template name="showRevMarker"/> -->
<fo:table-body>
<fo:table-row>
<fo:table-cell>
<fo:block margin-left="0pt">
<xsl:call-template name="insertCheckbox"/>
<xsl:apply-templates select="SELECTION-LBL-CN | SELECTION-LBL-EN"/>
<fo:inline>
<xsl:text></xsl:text>
</fo:inline>
<fo:inline>
<xsl:text></xsl:text>
</fo:inline>
<xsl:apply-templates select="*[not(self::SELECTION-LBL-CN) and not(self::SELECTION-LBL-EN)]"/>
<fo:inline>
<xsl:text></xsl:text>
</fo:inline>
<xsl:if test="not(following-sibling::SELECTION-ITEM)">
<xsl:variable name="isArchive">
<xsl:call-template name="isArchive"/>
</xsl:variable>
<xsl:choose>
<!-- 航线卡不论是否必填,在预览、模版、单机 PDF 都不需要显示 * -->
<xsl:when test="//LMJC-HEADER and $isArchive != 'true'">
</xsl:when>
<!-- MANDATORY 为 Y,或者未有 MANDATORY 属性时,显示* (除了航线卡,其他卡以及航线存档都按这个规则显示*) -->
<xsl:when test="parent::SELECTION[@MANDATORY = 'Y' or not(@MANDATORY)]">
<fo:inline color="red">
<xsl:text>*</xsl:text>
</fo:inline>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:if>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</xsl:when>
<xsl:otherwise>
<fo:inline>
<xsl:call-template name="generateID"/>
<xsl:call-template name="showRevMarker"/>
<xsl:apply-templates select="SELECTION-LBL-CN | SELECTION-LBL-EN"/>
<xsl:call-template name="insertCheckbox"/>
<fo:inline>
<xsl:text></xsl:text>
</fo:inline>
<fo:inline>
<xsl:text></xsl:text>
</fo:inline>
<xsl:apply-templates select="*[not(self::SELECTION-LBL-CN) and not(self::SELECTION-LBL-EN)]"/>
<fo:inline>
<xsl:text></xsl:text>
</fo:inline>
</fo:inline>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="SELECTION-LBL-CN">
<xsl:if test="$is_cn or $is_all">
<xsl:apply-templates/>
</xsl:if>
</xsl:template>
<xsl:template match="SELECTION-LBL-EN">
<xsl:if test="$is_en or $is_all">
<xsl:apply-templates/>
</xsl:if>
</xsl:template>
<xsl:template match="SIGNOFF">
<!-- 创建个容器,摆脱父节点对其影响 -->
<fo:block-container start-indent="0pt">
<xsl:choose>
<xsl:when test="@SKIP and @SKIP != ''and @SKIP = 'Y'">
<xsl:attribute name="break-after" select="'page'"></xsl:attribute>
</xsl:when>
</xsl:choose>
<fo:block margin-left="26pt">
<xsl:apply-templates select="memo[@memo-type='normal']"/>
<xsl:apply-templates select="memo[@memo-type='invalid']"/>
</fo:block>
<!-- 默认检验级别:LMJC 设为 A。EOTK 设为 B。SMJC 设为 C。根据业务需要,SMJC 第四大步中的检验级别降一级。 -->
<xsl:variable name="CK-LEVEL">
<xsl:choose>
<xsl:when test="@CK-LEVEL and @CK-LEVEL != ''">
<xsl:value-of select="@CK-LEVEL"/>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="//LMJC-HEADER">
<xsl:value-of>A</xsl:value-of>
</xsl:when>
<xsl:when test="//SMJC-HEADER">
<xsl:value-of>B</xsl:value-of>
</xsl:when>
<xsl:when test="//NRCJC-HEADER">
<xsl:value-of>B</xsl:value-of>
</xsl:when>
<xsl:when test="//TCJC-HEADER">
<xsl:value-of>B</xsl:value-of>
</xsl:when>
<xsl:when test="//EOTK-HEADER">
<xsl:value-of>B</xsl:value-of>
</xsl:when>
<xsl:when test="//DRJC-HEADER">
<xsl:value-of>B</xsl:value-of>
</xsl:when>
<xsl:otherwise>
<xsl:value-of>C</xsl:value-of>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fo:table keep-together.within-page="always" keep-with-previous.within-page="always" margin-bottom="0.5mm" margin-top="2mm" table-layout="fixed" width="100%">
<fo:table-column column-number="1" column-width="1%"/>
<fo:table-column column-number="2" column-width="11%"/>
<fo:table-column column-number="3" column-width="12%"/>
<fo:table-column column-number="4" column-width="12%"/>
<fo:table-column column-number="5" column-width="16%"/>
<fo:table-column column-number="6" column-width="16%"/>
<fo:table-column column-number="7" column-width="16%"/>
<fo:table-column column-number="8" column-width="16%"/>
<fo:table-body>
<fo:table-row text-align="center" height="10mm" display-align="center">
<fo:table-cell number-rows-spanned="2">
<fo:block/>
</fo:table-cell>
<fo:table-cell font-weight="normal" text-align="right" padding-right="5pt" display-align="after" number-rows-spanned="2">
<xsl:choose>
<xsl:when test="$CK-LEVEL ='A' or $CK-LEVEL ='N'">
<xsl:attribute name="number-columns-spanned">5</xsl:attribute>
</xsl:when>
<xsl:when test="$CK-LEVEL ='B' or $CK-LEVEL ='C' or $CK-LEVEL='D' or $CK-LEVEL='E'">
<xsl:attribute name="number-columns-spanned">3</xsl:attribute>
</xsl:when>
</xsl:choose>
<!-- 川航 签字点 去掉前缀:部件卡为P,定检卡为S -->
<!--
<fo:block text-align="right">
<xsl:if test="@SR-ID">
<xsl:value-of select="concat($att_prefix, ' ', @SR-ID)"/>
</xsl:if>
</fo:block>
-->
<fo:block text-align="right">
<xsl:if test="@TAG">
<xsl:value-of select="@TAG"/>
</xsl:if>
</fo:block>
</fo:table-cell>
<fo:table-cell border="1pt solid black" number-rows-spanned="2">
<fo:block>
<xsl:choose>
<xsl:when test="//LMJC-HEADER">
<xsl:choose>
<xsl:when test="@IF-RP = 'Y'">
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="'lm_pr'"/>
<xsl:with-param name="wrapped" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="'signoff_mech_header_lmjc'"/>
<xsl:with-param name="wrapped" select="true()"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$CK-LEVEL = 'E'">
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="'signoff_mech_header_drjc'"/>
<xsl:with-param name="wrapped" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$CK-LEVEL = 'N'">
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="'signoff_ndt_header'"/>
<xsl:with-param name="wrapped" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="'signoff_mech_header'"/>
<xsl:with-param name="wrapped" select="true()"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</fo:table-cell>
<fo:table-cell border="1pt solid black" font-weight="normal">
<xsl:if test="not(string-length(@disStime) > 0 and (string-length(@mech) > 0 or string-length(@insp) > 0))">
<xsl:attribute name="number-rows-spanned">2</xsl:attribute>
</xsl:if>
<xsl:call-template name="createNaBlock"/>
<fo:block>
<!--<xsl:call-template name="create_signoff_key">
<xsl:with-param name="type">A</xsl:with-param>
</xsl:call-template>-->
<xsl:value-of select="concat(@mech,' ',@mechName)"/>
</fo:block>
</fo:table-cell>
<xsl:if test="$CK-LEVEL = 'B' or $CK-LEVEL = 'D' or $CK-LEVEL = 'E'">
<fo:table-cell border="1pt solid black" number-rows-spanned="2">
<fo:block>
<xsl:choose>
<xsl:when test="//LMJC-HEADER">
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="'signoff_insp_header_lmjc'"/>
<xsl:with-param name="wrapped" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$CK-LEVEL = 'D'">
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="'signoff_verify_header'"/>
<xsl:with-param name="wrapped" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$CK-LEVEL = 'E'">
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="'signoff_verify_header_drjc'"/>
<xsl:with-param name="wrapped" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="'signoff_insp_header'"/>
<xsl:with-param name="wrapped" select="true()"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</fo:table-cell>
<fo:table-cell border="1pt solid black" font-weight="normal">
<xsl:if test="not(string-length(@disStime) > 0 and (string-length(@mech) > 0 or string-length(@insp) > 0))">
<xsl:attribute name="number-rows-spanned">2</xsl:attribute>
</xsl:if>
<xsl:call-template name="createNaBlock"/>
<fo:block>
<!--<xsl:call-template name="create_signoff_key">
<xsl:with-param name="type">B</xsl:with-param>
</xsl:call-template>-->
<xsl:value-of select="concat(@insp,' ',@inspName)"/>
</fo:block>
</fo:table-cell>
</xsl:if>
<xsl:if test="$CK-LEVEL = 'C'">
<fo:table-cell font-weight="bold" border="1pt solid black" number-rows-spanned="2">
<fo:block>
<xsl:choose>
<xsl:when test="//LMJC-HEADER">
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="'signoff_verify_header_lmjc'"/>
<xsl:with-param name="wrapped" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="'signoff_verify_header'"/>
<xsl:with-param name="wrapped" select="true()"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</fo:table-cell>
<fo:table-cell border="1pt solid black" font-weight="normal">
<xsl:if test="not(string-length(@disStime) > 0 and (string-length(@mech) > 0 or string-length(@insp) > 0))">
<xsl:attribute name="number-rows-spanned">2</xsl:attribute>
</xsl:if>
<xsl:call-template name="createNaBlock"/>
<fo:block>
<!--<xsl:call-template name="create_signoff_key">
<xsl:with-param name="type">C</xsl:with-param>
</xsl:call-template>-->
<xsl:value-of select="concat(@verf,' ',@verfName)"/>
</fo:block>
</fo:table-cell>
</xsl:if>
</fo:table-row>
<xsl:if test="string-length(@disStime) > 0">
<fo:table-row font-size="6.5pt" height="4mm" display-align="center" text-align="center">
<fo:table-cell border="1pt solid black">
<xsl:if test="$CK-LEVEL = 'A'">
<xsl:attribute name="number-columns-spanned">2</xsl:attribute>
</xsl:if>
<fo:block>
<xsl:value-of select="@disStime"/>
</fo:block>
</fo:table-cell>
<xsl:if test="$CK-LEVEL ='B' or $CK-LEVEL ='C'">
<fo:table-cell border="1pt solid black">
<fo:block>
<xsl:choose>
<xsl:when test="string-length(@SUP_DIS_STIME) > 0">
<xsl:value-of select="@SUP_DIS_STIME"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@disStime"/>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</fo:table-cell>
</xsl:if>
<xsl:if test="$CK-LEVEL='C'">
<fo:table-cell border="1pt solid black">
<fo:block>
<xsl:choose>
<xsl:when test="string-length(@SUP_DIS_STIME) > 0">
<xsl:value-of select="@SUP_DIS_STIME"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@disStime"/>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</fo:table-cell>
</xsl:if>
</fo:table-row>
</xsl:if>
</fo:table-body>
</fo:table>
</fo:block-container>
</xsl:template>
<xsl:template match="memo">
<xsl:call-template name="memoTable"/>
</xsl:template>
<xsl:template name="memoTable">
<fo:table-and-caption table-layout="fixed" width="100%">
<fo:table margin-top="3mm" text-align="left">
<fo:table-column column-width="25mm"/>
<fo:table-column column-width="55mm"/>
<fo:table-body>
<fo:table-row font-weight="bold">
<fo:table-cell number-columns-spanned="2">
<fo:block>
<xsl:choose>
<xsl:when test="@memo-type='invalid'">
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="'reset_title'"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="translate-value">
<xsl:with-param name="value" select="'memo_title'"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:table-and-caption>
<fo:table-and-caption table-layout="fixed" width="100%">
<fo:table text-align="left">
<fo:table-body>
<fo:table-row height="30mm">
<fo:table-cell border="0.5px solid black" width="70mm">
<fo:block linefeed-treatment="preserve" padding="0.5mm" white-space-collapse="false" white-space-treatment="preserve">
<xsl:value-of select="."/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:table-and-caption>
</xsl:template>
<xsl:template name="createNaBlock">
<xsl:if test="$showNaAbove = 'Y' and @ACTION ='NA'">
<fo:block color="red">
<xsl:choose>
<xsl:when test="$naTitle">
<xsl:value-of select="$naTitle"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'N/A'"/>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</xsl:if>
</xsl:template>
<xsl:template name="create_signoff_key">
<xsl:param name="type"/>
<xsl:variable name="uniqueId">
<xsl:value-of select="@SIGNOFF-ID"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$type='A'">
<xsl:value-of select="concat($uniqueId,'_A')"/>
</xsl:when>
<xsl:when test="$type='B'">
<xsl:value-of select="concat($uniqueId,'_B')"/>
</xsl:when>
<xsl:when test="$type='C'">
<xsl:value-of select="concat($uniqueId,'_C')"/>
</xsl:when>
<xsl:when test="$type='D'">
<xsl:value-of select="concat($uniqueId,'_D')"/>
</xsl:when>
<xsl:when test="$type='E'">
<xsl:value-of select="concat($uniqueId,'_E')"/>
</xsl:when>
</xsl:choose>
</xsl:template>
<!--
<xsl:template match="TOPIC">
<fo:block>
<xsl:variable name= "seqNum">
<xsl:call-template name= "makeSeqNum" />
</xsl:variable>
<xsl:call-template name= "createBodyHeader">
<xsl:with-param name= "seqNum" select= "$seqNum" />
<xsl:with-param name= "name">
<xsl:call-template name= "selectLangEle">
<xsl:with-param name= "elementName" select= "'TITLE'"/>
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
<xsl:apply-templates select= "*[not(self::TITLE) and not(self::TITLEC)]" />
</fo:block>
</xsl:template>
-->
<xsl:template match="PRETOPIC | TOPIC">
<xsl:choose>
<xsl:when test="preceding::EOTK-HEADER or preceding::DRJC-HEADER or preceding::QECJC-HEADER">
<xsl:choose>
<xsl:when test="normalize-space(./TITLEC/text())='' and normalize-space(./TITLE/text())=''">
<fo:block space-before="6pt" space-after="6pt">
<xsl:variable name="seqNum">
<xsl:call-template name="makeSeqNum"/>
</xsl:variable>
<xsl:call-template name="createBodyHeader">
<xsl:with-param name="seqNum" select="$seqNum"/>
<xsl:with-param name="name">
<xsl:value-of select="./PARAC[1]"/>
</xsl:with-param>
<xsl:with-param name="textType">
<xsl:text>para</xsl:text>
</xsl:with-param>
</xsl:call-template>
<fo:list-block provisional-label-separation="0.2em" provisional-distance-between-starts="2em" space-before="2pt">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block></fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates select="*[not(self::PARAC[not(preceding-sibling::PARAC)])]"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block space-before="6pt" space-after="6pt">
<xsl:variable name="seqNum">
<xsl:call-template name="makeSeqNum"/>
</xsl:variable>
<xsl:call-template name="createBodyHeader">
<xsl:with-param name="seqNum" select="$seqNum"/>
<xsl:with-param name="name">
<xsl:value-of select="./TITLEC"/>
</xsl:with-param>
<xsl:with-param name="textType">
<xsl:text>title</xsl:text>
</xsl:with-param>
</xsl:call-template>
<fo:list-block provisional-label-separation="0.2em" provisional-distance-between-starts="2em">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block/>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:value-of select="./TITLE"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
<fo:list-block provisional-label-separation="0.2em" provisional-distance-between-starts="2em" space-before="2pt">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block></fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates select="*[not(self::TITLEC) and not(self::TITLE)]"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<fo:block space-before="6pt" space-after="6pt">
<xsl:variable name="seqNum">
<xsl:call-template name="makeSeqNum"/>
</xsl:variable>
<xsl:call-template name="createBodyHeader">
<xsl:with-param name="seqNum" select="$seqNum"/>
<xsl:with-param name="name">
<xsl:call-template name="selectLangEle">
<xsl:with-param name="elementName" select="'TITLE'"/>
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
<fo:list-block provisional-label-separation="0.2em" provisional-distance-between-starts="2em" space-before="2pt">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block></fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates select="*[not(self::TITLE) and not(self::TITLEC)]"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="JC-TASK">
<fo:block space-before="2pt">
<!--
<xsl:value-of select="concat('任务 TASK ', @CHAPNBR, '-', @SECTNBR, '-', @SUBJNBR, '-', @FUNC, '-', @SEQ)"/>
-->
<xsl:variable name="title">
<xsl:value-of select="'task_title'"/>
</xsl:variable>
<xsl:call-template name="generateTaskTitle">
<xsl:with-param name="title" select="$title"/>
</xsl:call-template>
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:block>
</xsl:template>
<xsl:template match="SP">
<fo:inline font-weight="bold">
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
<xsl:template match="STEP">
<!--Skip empty steps, used for correct numbering for unstructured manuals-->
<fo:block>
<xsl:choose>
<xsl:when test="@ACRHIVE">
<xsl:choose>
<xsl:when test="@ACRHIVE = 'N' and preceding::CMJC-HEADER"/>
<xsl:otherwise>
<xsl:call-template name="make.para.title"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="@IF_LR = 'Y'">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="make.para.title"/>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</xsl:template>
<xsl:template name="make.para.title">
<fo:block space-before="2pt">
<xsl:if test="@displayApplic = 'show'">
<xsl:call-template name="insert-inline-applic">
<xsl:with-param name="applic-Ref-Id" select="@refapplic"/>
</xsl:call-template>
</xsl:if>
<fo:list-block provisional-label-separation="5pt" provisional-distance-between-starts="28pt" margin-left="0pt">
<xsl:call-template name="para.title.list"/>
</fo:list-block>
</fo:block>
</xsl:template>
<xsl:template name="para.title.list">
<!-- makes numbering for step and paras -->
<xsl:variable name="number">
<xsl:call-template name="makeStepNum"/>
</xsl:variable>
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>
<xsl:value-of select="$number"/>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<xsl:if test="@MAV or @RII">
<fo:block font-weight="bold">
<xsl:choose>
<xsl:when test="@MAV='MAV'">
<xsl:text>M AV</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@MAV"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="@RII='Y'">
<xsl:text>&#160;&#160;</xsl:text>
<fo:external-graphic content-height="5mm">
<xsl:attribute name="src">
url(<xsl:value-of select="concat($v_icon,'rii.png')"/>
)
</xsl:attribute>
</fo:external-graphic>
</xsl:if>
<xsl:if test="@DM='Y'">
<xsl:text>&#160;&#160;</xsl:text>
<fo:external-graphic content-height="5mm">
<xsl:attribute name="src">
url(<xsl:value-of select="concat($v_icon,'dm.png')"/>
)
</xsl:attribute>
</fo:external-graphic>
</xsl:if>
</fo:block>
</xsl:if>
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>
<xsl:template name="makeParaNumber">
<xsl:param name="make-xref-number" select="false()"/>
<xsl:variable name="number">
<xsl:choose>
<xsl:when test="self::STEP">
<xsl:number format="1"/>
.
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="$number"/>
</xsl:template>
<xsl:template name="insert-inline-applic">
<xsl:param name="applic-Ref-Id"/>
<fo:block>
<xsl:apply-templates select="//applic[@id = $applic-Ref-Id]"/>
</fo:block>
</xsl:template>
<!--RECORD-ITEMS, RECORD-ITEM在航线卡时打印时不显示,逻辑从旧版样式表拷贝过来 -->
<xsl:template match="RECORD-ITEM">
<!-- 改为所有卡都不显示 -->
<!-- <xsl:choose>
<xsl:when test="count(preceding::LMJC-HEADER) > 0">
</xsl:when>
<xsl:otherwise>
<fo:block>
<xsl:value-of select="text()"></xsl:value-of>
</fo:block>
</xsl:otherwise>
</xsl:choose> -->
</xsl:template>
<xsl:template match="RII">
<fo:block-container width="21mm" height="14mm">
<fo:block margin-top="3mm" border="3pt solid red" color="red" padding="2pt" text-align="center" font-weight="bold" line-height="6mm">
<xsl:text>RII</xsl:text>
</fo:block>
</fo:block-container>
</xsl:template>
</xsl:stylesheet>
export default {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
}
import{y as e,F as o,b4 as r,bY as n,J as t,p as i,h as a,I as s,k as l,bv as d,bn as c,bp as u,bk as f,d as b,Q as h,f as p,bD as v,a2 as g,j as y,bC as m,bZ as x,Z as w,R as C,r as $,Y as S,b as z,e as B,q as k,a0 as P,ah as H,u as F,at as T,g as R,ao as E,v as j,P as A,i as D,aj as W,ak as O}from"./index-BGNM-WBG.js";import{u as I}from"./use-rtl-iN3przWb.js";const M="undefined"!=typeof document&&"undefined"!=typeof window;function _(e){return e.replace(/#|\(|\)|,|\s|\./g,"_")}function L(e,...o){if(!Array.isArray(e))return e(...o);e.forEach(e=>L(e,...o))}function q(n,t=!0,i=[]){return n.forEach(n=>{if(null!==n)if("object"==typeof n)if(Array.isArray(n))q(n,t,i);else if(n.type===o){if(null===n.children)return;Array.isArray(n.children)&&q(n.children,t,i)}else{if(n.type===r&&t)return;i.push(n)}else"string"!=typeof n&&"number"!=typeof n||i.push(e(String(n)))}),i}function V(e,o="default",r=[]){const n=e.$slots[o];return void 0===n?r:n()}function K(e){return e.some(e=>!n(e)||e.type!==r&&!(e.type===o&&!K(e.children)))?e:null}function N(e,o){return e&&K(e())||o()}function Q(e,o,r){return e&&K(e(o))||r(o)}function G(e,o){return o(e&&K(e())||null)}function Y(e){return!(e&&K(e()))}const Z=l("n-form-item");function J(e,{defaultSize:o="medium",mergedSize:r,mergedDisabled:n}={}){const l=t(Z,null);i(Z,null);const d=a(r?()=>r(l):()=>{const{size:r}=e;if(r)return r;if(l){const{mergedSize:e}=l;if(void 0!==e.value)return e.value}return o}),c=a(n?()=>n(l):()=>{const{disabled:o}=e;return void 0!==o?o:!!l&&l.disabled.value}),u=a(()=>{const{status:o}=e;return o||(null==l?void 0:l.mergedValidationStatus.value)});return s(()=>{l&&l.restoreValidation()}),{mergedSizeRef:d,mergedDisabledRef:c,mergedStatusRef:u,nTriggerFormBlur(){l&&l.handleContentBlur()},nTriggerFormChange(){l&&l.handleContentChange()},nTriggerFormFocus(){l&&l.handleContentFocus()},nTriggerFormInput(){l&&l.handleContentInput()}}}function U(e){return"symbol"==typeof e||d(e)&&"[object Symbol]"==c(e)}function X(e,o){for(var r=-1,n=null==e?0:e.length,t=Array(n);++r<n;)t[r]=o(e[r],r,e);return t}var ee=u?u.prototype:void 0,oe=ee?ee.toString:void 0;function re(e){if("string"==typeof e)return e;if(f(e))return X(e,re)+"";if(U(e))return oe?oe.call(e):"";var o=e+"";return"0"==o&&1/e==-1/0?"-0":o}function ne(e){return null==e?"":re(e)}function te(e,o,r){var n=e.length;return r=void 0===r?n:r,!o&&r>=n?e:function(e,o,r){var n=-1,t=e.length;o<0&&(o=-o>t?0:t+o),(r=r>t?t:r)<0&&(r+=t),t=o>r?0:r-o>>>0,o>>>=0;for(var i=Array(t);++n<t;)i[n]=e[n+o];return i}(e,o,r)}var ie=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\ufe0e\\ufe0f]");function ae(e){return ie.test(e)}var se="\\ud800-\\udfff",le="["+se+"]",de="[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]",ce="\\ud83c[\\udffb-\\udfff]",ue="[^"+se+"]",fe="(?:\\ud83c[\\udde6-\\uddff]){2}",be="[\\ud800-\\udbff][\\udc00-\\udfff]",he="(?:"+de+"|"+ce+")"+"?",pe="[\\ufe0e\\ufe0f]?",ve=pe+he+("(?:\\u200d(?:"+[ue,fe,be].join("|")+")"+pe+he+")*"),ge="(?:"+[ue+de+"?",de,fe,be,le].join("|")+")",ye=RegExp(ce+"(?="+ce+")|"+ge+ve,"g");function me(e){return ae(e)?function(e){return e.match(ye)||[]}(e):function(e){return e.split("")}(e)}var xe,we=(xe="toUpperCase",function(e){var o=ae(e=ne(e))?me(e):void 0,r=o?o[0]:e.charAt(0),n=o?te(o,1).join(""):e.slice(1);return r[xe]()+n});const Ce=b("base-icon","\n height: 1em;\n width: 1em;\n line-height: 1em;\n text-align: center;\n display: inline-block;\n position: relative;\n fill: currentColor;\n transform: translateZ(0);\n",[h("svg","\n height: 1em;\n width: 1em;\n ")]),$e=p({name:"BaseIcon",props:{role:String,ariaLabel:String,ariaDisabled:{type:Boolean,default:void 0},ariaHidden:{type:Boolean,default:void 0},clsPrefix:{type:String,required:!0},onClick:Function,onMousedown:Function,onMouseup:Function},setup(e){v("-base-icon",Ce,g(e,"clsPrefix"))},render(){return y("i",{class:`${this.clsPrefix}-base-icon`,onClick:this.onClick,onMousedown:this.onMousedown,onMouseup:this.onMouseup,role:this.role,"aria-label":this.ariaLabel,"aria-hidden":this.ariaHidden,"aria-disabled":this.ariaDisabled},this.$slots)}});function Se(e,o){const r=p({render:()=>o()});return p({name:we(e),setup(){var o;const n=null===(o=t(m,null))||void 0===o?void 0:o.mergedIconsRef;return()=>{var o;const t=null===(o=null==n?void 0:n.value)||void 0===o?void 0:o[e];return t?t():y(r,null)}}})}const ze=p({name:"FadeInExpandTransition",props:{appear:Boolean,group:Boolean,mode:String,onLeave:Function,onAfterLeave:Function,onAfterEnter:Function,width:Boolean,reverse:Boolean},setup(e,{slots:o}){function r(o){e.width?o.style.maxWidth=`${o.offsetWidth}px`:o.style.maxHeight=`${o.offsetHeight}px`,o.offsetWidth}function n(o){e.width?o.style.maxWidth="0":o.style.maxHeight="0",o.offsetWidth;const{onLeave:r}=e;r&&r()}function t(o){e.width?o.style.maxWidth="":o.style.maxHeight="";const{onAfterLeave:r}=e;r&&r()}function i(o){if(o.style.transition="none",e.width){const e=o.offsetWidth;o.style.maxWidth="0",o.offsetWidth,o.style.transition="",o.style.maxWidth=`${e}px`}else if(e.reverse)o.style.maxHeight=`${o.offsetHeight}px`,o.offsetHeight,o.style.transition="",o.style.maxHeight="0";else{const e=o.offsetHeight;o.style.maxHeight="0",o.offsetWidth,o.style.transition="",o.style.maxHeight=`${e}px`}o.offsetWidth}function a(o){var r;e.width?o.style.maxWidth="":e.reverse||(o.style.maxHeight=""),null===(r=e.onAfterEnter)||void 0===r||r.call(e)}return()=>{const{group:s,width:l,appear:d,mode:c}=e,u=s?x:w,f={name:l?"fade-in-width-expand-transition":"fade-in-height-expand-transition",appear:d,onEnter:i,onAfterEnter:a,onBeforeLeave:r,onLeave:n,onAfterLeave:t};return s||(f.mode=c),y(u,f,o)}}}),{cubicBezierEaseInOut:Be}=C;const ke=b("base-wave","\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n border-radius: inherit;\n"),Pe=p({name:"BaseWave",props:{clsPrefix:{type:String,required:!0}},setup(e){v("-base-wave",ke,g(e,"clsPrefix"));const o=$(null),r=$(!1);let n=null;return s(()=>{null!==n&&window.clearTimeout(n)}),{active:r,selfRef:o,play(){null!==n&&(window.clearTimeout(n),r.value=!1,n=null),S(()=>{var e;null===(e=o.value)||void 0===e||e.offsetHeight,r.value=!0,n=window.setTimeout(()=>{r.value=!1,n=null},1e3)})}}},render(){const{clsPrefix:e}=this;return y("div",{ref:"selfRef","aria-hidden":!0,class:[`${e}-base-wave`,this.active&&`${e}-base-wave--active`]})}}),He=M&&"chrome"in window;M&&navigator.userAgent.includes("Firefox");const Fe=M&&navigator.userAgent.includes("Safari")&&!He;function Te(e){return z(e,[255,255,255,.16])}function Re(e){return z(e,[0,0,0,.12])}const Ee=l("n-button-group"),je=h([b("button","\n margin: 0;\n font-weight: var(--n-font-weight);\n line-height: 1;\n font-family: inherit;\n padding: var(--n-padding);\n height: var(--n-height);\n font-size: var(--n-font-size);\n border-radius: var(--n-border-radius);\n color: var(--n-text-color);\n background-color: var(--n-color);\n width: var(--n-width);\n white-space: nowrap;\n outline: none;\n position: relative;\n z-index: auto;\n border: none;\n display: inline-flex;\n flex-wrap: nowrap;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n user-select: none;\n -webkit-user-select: none;\n text-align: center;\n cursor: pointer;\n text-decoration: none;\n transition:\n color .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n opacity .3s var(--n-bezier),\n border-color .3s var(--n-bezier);\n ",[B("color",[k("border",{borderColor:"var(--n-border-color)"}),B("disabled",[k("border",{borderColor:"var(--n-border-color-disabled)"})]),P("disabled",[h("&:focus",[k("state-border",{borderColor:"var(--n-border-color-focus)"})]),h("&:hover",[k("state-border",{borderColor:"var(--n-border-color-hover)"})]),h("&:active",[k("state-border",{borderColor:"var(--n-border-color-pressed)"})]),B("pressed",[k("state-border",{borderColor:"var(--n-border-color-pressed)"})])])]),B("disabled",{backgroundColor:"var(--n-color-disabled)",color:"var(--n-text-color-disabled)"},[k("border",{border:"var(--n-border-disabled)"})]),P("disabled",[h("&:focus",{backgroundColor:"var(--n-color-focus)",color:"var(--n-text-color-focus)"},[k("state-border",{border:"var(--n-border-focus)"})]),h("&:hover",{backgroundColor:"var(--n-color-hover)",color:"var(--n-text-color-hover)"},[k("state-border",{border:"var(--n-border-hover)"})]),h("&:active",{backgroundColor:"var(--n-color-pressed)",color:"var(--n-text-color-pressed)"},[k("state-border",{border:"var(--n-border-pressed)"})]),B("pressed",{backgroundColor:"var(--n-color-pressed)",color:"var(--n-text-color-pressed)"},[k("state-border",{border:"var(--n-border-pressed)"})])]),B("loading","cursor: wait;"),b("base-wave","\n pointer-events: none;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n animation-iteration-count: 1;\n animation-duration: var(--n-ripple-duration);\n animation-timing-function: var(--n-bezier-ease-out), var(--n-bezier-ease-out);\n ",[B("active",{zIndex:1,animationName:"button-wave-spread, button-wave-opacity"})]),M&&"MozBoxSizing"in document.createElement("div").style?h("&::moz-focus-inner",{border:0}):null,k("border, state-border","\n position: absolute;\n left: 0;\n top: 0;\n right: 0;\n bottom: 0;\n border-radius: inherit;\n transition: border-color .3s var(--n-bezier);\n pointer-events: none;\n "),k("border",{border:"var(--n-border)"}),k("state-border",{border:"var(--n-border)",borderColor:"#0000",zIndex:1}),k("icon","\n margin: var(--n-icon-margin);\n margin-left: 0;\n height: var(--n-icon-size);\n width: var(--n-icon-size);\n max-width: var(--n-icon-size);\n font-size: var(--n-icon-size);\n position: relative;\n flex-shrink: 0;\n ",[b("icon-slot","\n height: var(--n-icon-size);\n width: var(--n-icon-size);\n position: absolute;\n left: 0;\n top: 50%;\n transform: translateY(-50%);\n display: flex;\n align-items: center;\n justify-content: center;\n ",[H({top:"50%",originalTransform:"translateY(-50%)"})]),function({duration:e=".2s",delay:o=".1s"}={}){return[h("&.fade-in-width-expand-transition-leave-from, &.fade-in-width-expand-transition-enter-to",{opacity:1}),h("&.fade-in-width-expand-transition-leave-to, &.fade-in-width-expand-transition-enter-from","\n opacity: 0!important;\n margin-left: 0!important;\n margin-right: 0!important;\n "),h("&.fade-in-width-expand-transition-leave-active",`\n overflow: hidden;\n transition:\n opacity ${e} ${Be},\n max-width ${e} ${Be} ${o},\n margin-left ${e} ${Be} ${o},\n margin-right ${e} ${Be} ${o};\n `),h("&.fade-in-width-expand-transition-enter-active",`\n overflow: hidden;\n transition:\n opacity ${e} ${Be} ${o},\n max-width ${e} ${Be},\n margin-left ${e} ${Be},\n margin-right ${e} ${Be};\n `)]}()]),k("content","\n display: flex;\n align-items: center;\n flex-wrap: nowrap;\n min-width: 0;\n ",[h("~",[k("icon",{margin:"var(--n-icon-margin)",marginRight:0})])]),B("block","\n display: flex;\n width: 100%;\n "),B("dashed",[k("border, state-border",{borderStyle:"dashed !important"})]),B("disabled",{cursor:"not-allowed",opacity:"var(--n-opacity-disabled)"})]),h("@keyframes button-wave-spread",{from:{boxShadow:"0 0 0.5px 0 var(--n-ripple-color)"},to:{boxShadow:"0 0 0.5px 4.5px var(--n-ripple-color)"}}),h("@keyframes button-wave-opacity",{from:{opacity:"var(--n-wave-opacity)"},to:{opacity:0}})]),Ae=Object.assign(Object.assign({},F.props),{color:String,textColor:String,text:Boolean,block:Boolean,loading:Boolean,disabled:Boolean,circle:Boolean,size:String,ghost:Boolean,round:Boolean,secondary:Boolean,tertiary:Boolean,quaternary:Boolean,strong:Boolean,focusable:{type:Boolean,default:!0},keyboard:{type:Boolean,default:!0},tag:{type:String,default:"button"},type:{type:String,default:"default"},dashed:Boolean,renderIcon:Function,iconPlacement:{type:String,default:"left"},attrType:{type:String,default:"button"},bordered:{type:Boolean,default:!0},onClick:[Function,Array],nativeFocusBehavior:{type:Boolean,default:!Fe}}),De=p({name:"Button",props:Ae,slots:Object,setup(e){const o=$(null),r=$(null),n=$(!1),i=T(()=>!e.quaternary&&!e.tertiary&&!e.secondary&&!e.text&&(!e.color||e.ghost||e.dashed)&&e.bordered),s=t(Ee,{}),{mergedSizeRef:l}=J({},{defaultSize:"medium",mergedSize:o=>{const{size:r}=e;if(r)return r;const{size:n}=s;if(n)return n;const{mergedSize:t}=o||{};return t?t.value:"medium"}}),d=a(()=>e.focusable&&!e.disabled),{inlineThemeDisabled:c,mergedClsPrefixRef:u,mergedRtlRef:f}=R(e),b=F("Button","-button",je,E,e,u),h=I("Button",f,u),p=a(()=>{const o=b.value,{common:{cubicBezierEaseInOut:r,cubicBezierEaseOut:n},self:t}=o,{rippleDuration:i,opacityDisabled:a,fontWeight:s,fontWeightStrong:d}=t,c=l.value,{dashed:u,type:f,ghost:h,text:p,color:v,round:g,circle:y,textColor:m,secondary:x,tertiary:w,quaternary:C,strong:$}=e,S={"--n-font-weight":$?d:s};let z={"--n-color":"initial","--n-color-hover":"initial","--n-color-pressed":"initial","--n-color-focus":"initial","--n-color-disabled":"initial","--n-ripple-color":"initial","--n-text-color":"initial","--n-text-color-hover":"initial","--n-text-color-pressed":"initial","--n-text-color-focus":"initial","--n-text-color-disabled":"initial"};const B="tertiary"===f,k="default"===f,P=B?"default":f;if(p){const e=m||v;z={"--n-color":"#0000","--n-color-hover":"#0000","--n-color-pressed":"#0000","--n-color-focus":"#0000","--n-color-disabled":"#0000","--n-ripple-color":"#0000","--n-text-color":e||t[j("textColorText",P)],"--n-text-color-hover":e?Te(e):t[j("textColorTextHover",P)],"--n-text-color-pressed":e?Re(e):t[j("textColorTextPressed",P)],"--n-text-color-focus":e?Te(e):t[j("textColorTextHover",P)],"--n-text-color-disabled":e||t[j("textColorTextDisabled",P)]}}else if(h||u){const e=m||v;z={"--n-color":"#0000","--n-color-hover":"#0000","--n-color-pressed":"#0000","--n-color-focus":"#0000","--n-color-disabled":"#0000","--n-ripple-color":v||t[j("rippleColor",P)],"--n-text-color":e||t[j("textColorGhost",P)],"--n-text-color-hover":e?Te(e):t[j("textColorGhostHover",P)],"--n-text-color-pressed":e?Re(e):t[j("textColorGhostPressed",P)],"--n-text-color-focus":e?Te(e):t[j("textColorGhostHover",P)],"--n-text-color-disabled":e||t[j("textColorGhostDisabled",P)]}}else if(x){const e=k?t.textColor:B?t.textColorTertiary:t[j("color",P)],o=v||e,r="default"!==f&&"tertiary"!==f;z={"--n-color":r?A(o,{alpha:Number(t.colorOpacitySecondary)}):t.colorSecondary,"--n-color-hover":r?A(o,{alpha:Number(t.colorOpacitySecondaryHover)}):t.colorSecondaryHover,"--n-color-pressed":r?A(o,{alpha:Number(t.colorOpacitySecondaryPressed)}):t.colorSecondaryPressed,"--n-color-focus":r?A(o,{alpha:Number(t.colorOpacitySecondaryHover)}):t.colorSecondaryHover,"--n-color-disabled":t.colorSecondary,"--n-ripple-color":"#0000","--n-text-color":o,"--n-text-color-hover":o,"--n-text-color-pressed":o,"--n-text-color-focus":o,"--n-text-color-disabled":o}}else if(w||C){const e=k?t.textColor:B?t.textColorTertiary:t[j("color",P)],o=v||e;w?(z["--n-color"]=t.colorTertiary,z["--n-color-hover"]=t.colorTertiaryHover,z["--n-color-pressed"]=t.colorTertiaryPressed,z["--n-color-focus"]=t.colorSecondaryHover,z["--n-color-disabled"]=t.colorTertiary):(z["--n-color"]=t.colorQuaternary,z["--n-color-hover"]=t.colorQuaternaryHover,z["--n-color-pressed"]=t.colorQuaternaryPressed,z["--n-color-focus"]=t.colorQuaternaryHover,z["--n-color-disabled"]=t.colorQuaternary),z["--n-ripple-color"]="#0000",z["--n-text-color"]=o,z["--n-text-color-hover"]=o,z["--n-text-color-pressed"]=o,z["--n-text-color-focus"]=o,z["--n-text-color-disabled"]=o}else z={"--n-color":v||t[j("color",P)],"--n-color-hover":v?Te(v):t[j("colorHover",P)],"--n-color-pressed":v?Re(v):t[j("colorPressed",P)],"--n-color-focus":v?Te(v):t[j("colorFocus",P)],"--n-color-disabled":v||t[j("colorDisabled",P)],"--n-ripple-color":v||t[j("rippleColor",P)],"--n-text-color":m||(v?t.textColorPrimary:B?t.textColorTertiary:t[j("textColor",P)]),"--n-text-color-hover":m||(v?t.textColorHoverPrimary:t[j("textColorHover",P)]),"--n-text-color-pressed":m||(v?t.textColorPressedPrimary:t[j("textColorPressed",P)]),"--n-text-color-focus":m||(v?t.textColorFocusPrimary:t[j("textColorFocus",P)]),"--n-text-color-disabled":m||(v?t.textColorDisabledPrimary:t[j("textColorDisabled",P)])};let H={"--n-border":"initial","--n-border-hover":"initial","--n-border-pressed":"initial","--n-border-focus":"initial","--n-border-disabled":"initial"};H=p?{"--n-border":"none","--n-border-hover":"none","--n-border-pressed":"none","--n-border-focus":"none","--n-border-disabled":"none"}:{"--n-border":t[j("border",P)],"--n-border-hover":t[j("borderHover",P)],"--n-border-pressed":t[j("borderPressed",P)],"--n-border-focus":t[j("borderFocus",P)],"--n-border-disabled":t[j("borderDisabled",P)]};const{[j("height",c)]:F,[j("fontSize",c)]:T,[j("padding",c)]:R,[j("paddingRound",c)]:E,[j("iconSize",c)]:D,[j("borderRadius",c)]:W,[j("iconMargin",c)]:O,waveOpacity:I}=t,M={"--n-width":y&&!p?F:"initial","--n-height":p?"initial":F,"--n-font-size":T,"--n-padding":y||p?"initial":g?E:R,"--n-icon-size":D,"--n-icon-margin":O,"--n-border-radius":p?"initial":y||g?F:W};return Object.assign(Object.assign(Object.assign(Object.assign({"--n-bezier":r,"--n-bezier-ease-out":n,"--n-ripple-duration":i,"--n-opacity-disabled":a,"--n-wave-opacity":I},S),z),H),M)}),v=c?D("button",a(()=>{let o="";const{dashed:r,type:n,ghost:t,text:i,color:a,round:s,circle:d,textColor:c,secondary:u,tertiary:f,quaternary:b,strong:h}=e;r&&(o+="a"),t&&(o+="b"),i&&(o+="c"),s&&(o+="d"),d&&(o+="e"),u&&(o+="f"),f&&(o+="g"),b&&(o+="h"),h&&(o+="i"),a&&(o+=`j${_(a)}`),c&&(o+=`k${_(c)}`);const{value:p}=l;return o+=`l${p[0]}`,o+=`m${n[0]}`,o}),p,e):void 0;return{selfElRef:o,waveElRef:r,mergedClsPrefix:u,mergedFocusable:d,mergedSize:l,showBorder:i,enterPressed:n,rtlEnabled:h,handleMousedown:r=>{var n;d.value||r.preventDefault(),e.nativeFocusBehavior||(r.preventDefault(),e.disabled||d.value&&(null===(n=o.value)||void 0===n||n.focus({preventScroll:!0})))},handleKeydown:o=>{if("Enter"===o.key){if(!e.keyboard||e.loading)return void o.preventDefault();n.value=!0}},handleBlur:()=>{n.value=!1},handleKeyup:o=>{if("Enter"===o.key){if(!e.keyboard)return;n.value=!1}},handleClick:o=>{var n;if(!e.disabled&&!e.loading){const{onClick:t}=e;t&&L(t,o),e.text||null===(n=r.value)||void 0===n||n.play()}},customColorCssVars:a(()=>{const{color:o}=e;if(!o)return null;const r=Te(o);return{"--n-border-color":o,"--n-border-color-hover":r,"--n-border-color-pressed":Re(o),"--n-border-color-focus":r,"--n-border-color-disabled":o}}),cssVars:c?void 0:p,themeClass:null==v?void 0:v.themeClass,onRender:null==v?void 0:v.onRender}},render(){const{mergedClsPrefix:e,tag:o,onRender:r}=this;null==r||r();const n=G(this.$slots.default,o=>o&&y("span",{class:`${e}-button__content`},o));return y(o,{ref:"selfElRef",class:[this.themeClass,`${e}-button`,`${e}-button--${this.type}-type`,`${e}-button--${this.mergedSize}-type`,this.rtlEnabled&&`${e}-button--rtl`,this.disabled&&`${e}-button--disabled`,this.block&&`${e}-button--block`,this.enterPressed&&`${e}-button--pressed`,!this.text&&this.dashed&&`${e}-button--dashed`,this.color&&`${e}-button--color`,this.secondary&&`${e}-button--secondary`,this.loading&&`${e}-button--loading`,this.ghost&&`${e}-button--ghost`],tabindex:this.mergedFocusable?0:-1,type:this.attrType,style:this.cssVars,disabled:this.disabled,onClick:this.handleClick,onBlur:this.handleBlur,onMousedown:this.handleMousedown,onKeyup:this.handleKeyup,onKeydown:this.handleKeydown},"right"===this.iconPlacement&&n,y(ze,{width:!0},{default:()=>G(this.$slots.icon,o=>(this.loading||this.renderIcon||o)&&y("span",{class:`${e}-button__icon`,style:{margin:Y(this.$slots.default)?"0":""}},y(W,null,{default:()=>this.loading?y(O,{clsPrefix:e,key:"loading",class:`${e}-icon-slot`,strokeWidth:20}):y("div",{key:"icon",class:`${e}-icon-slot`,role:"none"},this.renderIcon?this.renderIcon():o)})))}),"left"===this.iconPlacement&&n,this.text?null:y(Pe,{ref:"waveElRef",clsPrefix:e}),this.showBorder?y("div",{"aria-hidden":!0,class:`${e}-button__border`,style:this.customColorCssVars}):null,this.showBorder?y("div",{"aria-hidden":!0,class:`${e}-button__state-border`,style:this.customColorCssVars}):null)}}),We=De,Oe=De;export{$e as N,Oe as X,We as _,ze as a,N as b,G as c,Q as d,L as e,Z as f,q as g,V as h,M as i,Y as j,U as k,X as l,_ as m,Fe as n,K as o,Se as r,ne as t,J as u};
This source diff could not be displayed because it is too large. You can view the blob instead.
import{b8 as e,b9 as t,f as n,K as o,M as r,I as i,aT as l,S as a,d as s,Q as c,e as u,q as d,ad as v,u as h,g as f,r as b,s as p,h as g,az as w,a4 as m,i as y,j as z,F as x,Z as S,T}from"./index-BGNM-WBG.js";import{u as R}from"./use-rtl-iN3przWb.js";function E(e){return e.composedPath()[0]||null}function B(e){return e.composedPath()[0]}const M={mousemoveoutside:new WeakMap,clickoutside:new WeakMap};function O(e,t,n){const o=M[e];let r=o.get(t);void 0===r&&o.set(t,r=new WeakMap);let i=r.get(n);return void 0===i&&r.set(n,i=function(e,t,n){if("mousemoveoutside"===e){const e=e=>{t.contains(B(e))||n(e)};return{mousemove:e,touchstart:e}}if("clickoutside"===e){let e=!1;const o=n=>{e=!t.contains(B(n))},r=o=>{e&&(t.contains(B(o))||n(o))};return{mousedown:o,mouseup:r,touchstart:o,touchend:r}}return{}}(e,t,n)),i}const{on:P,off:k}=function(){if("undefined"==typeof window)return{on:()=>{},off:()=>{}};const e=new WeakMap,t=new WeakMap;function n(){e.set(this,!0)}function o(){e.set(this,!0),t.set(this,!0)}function r(e,t,n){const o=e[t];return e[t]=function(){return n.apply(e,arguments),o.apply(e,arguments)},e}function i(e,t){e[t]=Event.prototype[t]}const l=new WeakMap,a=Object.getOwnPropertyDescriptor(Event.prototype,"currentTarget");function s(){var e;return null!==(e=l.get(this))&&void 0!==e?e:null}function c(e,t){void 0!==a&&Object.defineProperty(e,"currentTarget",{configurable:!0,enumerable:!0,get:null!=t?t:a.get})}const u={bubble:{},capture:{}},d={},v=function(){const a=function(a){const{type:d,eventPhase:v,bubbles:h}=a,f=B(a);if(2===v)return;const b=1===v?"capture":"bubble";let p=f;const g=[];for(;null===p&&(p=window),g.push(p),p!==window;)p=p.parentNode||null;const w=u.capture[d],m=u.bubble[d];if(r(a,"stopPropagation",n),r(a,"stopImmediatePropagation",o),c(a,s),"capture"===b){if(void 0===w)return;for(let n=g.length-1;n>=0&&!e.has(a);--n){const e=g[n],o=w.get(e);if(void 0!==o){l.set(a,e);for(const e of o){if(t.has(a))break;e(a)}}if(0===n&&!h&&void 0!==m){const n=m.get(e);if(void 0!==n)for(const e of n){if(t.has(a))break;e(a)}}}}else if("bubble"===b){if(void 0===m)return;for(let n=0;n<g.length&&!e.has(a);++n){const e=g[n],o=m.get(e);if(void 0!==o){l.set(a,e);for(const e of o){if(t.has(a))break;e(a)}}}}i(a,"stopPropagation"),i(a,"stopImmediatePropagation"),c(a)};return a.displayName="evtdUnifiedHandler",a}(),h=function(){const e=function(e){const{type:t,eventPhase:n}=e;if(2!==n)return;const o=d[t];void 0!==o&&o.forEach(t=>t(e))};return e.displayName="evtdUnifiedWindowEventHandler",e}();function f(e,t){const n=u[e];return void 0===n[t]&&(n[t]=new Map,window.addEventListener(t,v,"capture"===e)),n[t]}function b(e,t){let n=e.get(t);return void 0===n&&e.set(t,n=new Set),n}function p(e,t,n,o){const r=function(e,t,n,o){if("mousemoveoutside"===e||"clickoutside"===e){const r=O(e,t,n);return Object.keys(r).forEach(e=>{k(e,document,r[e],o)}),!0}return!1}(e,t,n,o);if(r)return;const i=!0===o||"object"==typeof o&&!0===o.capture,l=i?"capture":"bubble",a=f(l,e),s=b(a,t);if(t===window){if(!function(e,t,n,o){const r=u[t][n];if(void 0!==r){const t=r.get(e);if(void 0!==t&&t.has(o))return!0}return!1}(t,i?"bubble":"capture",e,n)&&function(e,t){const n=d[e];return!(void 0===n||!n.has(t))}(e,n)){const t=d[e];t.delete(n),0===t.size&&(window.removeEventListener(e,h),d[e]=void 0)}}s.has(n)&&s.delete(n),0===s.size&&a.delete(t),0===a.size&&(window.removeEventListener(e,v,"capture"===l),u[l][e]=void 0)}return{on:function(e,t,n,o){let r;r="object"==typeof o&&!0===o.once?i=>{p(e,t,r,o),n(i)}:n;if(function(e,t,n,o){if("mousemoveoutside"===e||"clickoutside"===e){const r=O(e,t,n);return Object.keys(r).forEach(e=>{P(e,document,r[e],o)}),!0}return!1}(e,t,r,o))return;const i=b(f(!0===o||"object"==typeof o&&!0===o.capture?"capture":"bubble",e),t);if(i.has(r)||i.add(r),t===window){const t=function(e){return void 0===d[e]&&(d[e]=new Set,window.addEventListener(e,h)),d[e]}(e);t.has(r)||t.add(r)}},off:p}}(),C="undefined"!=typeof window&&(/iPad|iPhone|iPod/.test(navigator.platform)||"MacIntel"===navigator.platform&&navigator.maxTouchPoints>1)&&!window.MSStream;function W(n){const o={isDeactivated:!1};let r=!1;return e(()=>{o.isDeactivated=!1,r?n():r=!0}),t(()=>{o.isDeactivated=!0,r||(r=!0)}),o}var $,D,L=[],H=function(){return L.some(function(e){return e.activeTargets.length>0})},I="ResizeObserver loop completed with undelivered notifications.";(D=$||($={})).BORDER_BOX="border-box",D.CONTENT_BOX="content-box",D.DEVICE_PIXEL_CONTENT_BOX="device-pixel-content-box";var N,X=function(e){return Object.freeze(e)},j=function(){return function(e,t){this.inlineSize=e,this.blockSize=t,X(this)}}(),F=function(){function e(e,t,n,o){return this.x=e,this.y=t,this.width=n,this.height=o,this.top=this.y,this.left=this.x,this.bottom=this.top+this.height,this.right=this.left+this.width,X(this)}return e.prototype.toJSON=function(){var e=this;return{x:e.x,y:e.y,top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:e.width,height:e.height}},e.fromRect=function(t){return new e(t.x,t.y,t.width,t.height)},e}(),_=function(e){return e instanceof SVGElement&&"getBBox"in e},V=function(e){if(_(e)){var t=e.getBBox(),n=t.width,o=t.height;return!n&&!o}var r=e,i=r.offsetWidth,l=r.offsetHeight;return!(i||l||e.getClientRects().length)},A=function(e){var t;if(e instanceof Element)return!0;var n=null===(t=null==e?void 0:e.ownerDocument)||void 0===t?void 0:t.defaultView;return!!(n&&e instanceof n.Element)},Y="undefined"!=typeof window?window:{},U=new WeakMap,q=/auto|scroll/,G=/^tb|vertical/,J=/msie|trident/i.test(Y.navigator&&Y.navigator.userAgent),K=function(e){return parseFloat(e||"0")},Q=function(e,t,n){return void 0===e&&(e=0),void 0===t&&(t=0),void 0===n&&(n=!1),new j((n?t:e)||0,(n?e:t)||0)},Z=X({devicePixelContentBoxSize:Q(),borderBoxSize:Q(),contentBoxSize:Q(),contentRect:new F(0,0,0,0)}),ee=function(e,t){if(void 0===t&&(t=!1),U.has(e)&&!t)return U.get(e);if(V(e))return U.set(e,Z),Z;var n=getComputedStyle(e),o=_(e)&&e.ownerSVGElement&&e.getBBox(),r=!J&&"border-box"===n.boxSizing,i=G.test(n.writingMode||""),l=!o&&q.test(n.overflowY||""),a=!o&&q.test(n.overflowX||""),s=o?0:K(n.paddingTop),c=o?0:K(n.paddingRight),u=o?0:K(n.paddingBottom),d=o?0:K(n.paddingLeft),v=o?0:K(n.borderTopWidth),h=o?0:K(n.borderRightWidth),f=o?0:K(n.borderBottomWidth),b=d+c,p=s+u,g=(o?0:K(n.borderLeftWidth))+h,w=v+f,m=a?e.offsetHeight-w-e.clientHeight:0,y=l?e.offsetWidth-g-e.clientWidth:0,z=r?b+g:0,x=r?p+w:0,S=o?o.width:K(n.width)-z-y,T=o?o.height:K(n.height)-x-m,R=S+b+y+g,E=T+p+m+w,B=X({devicePixelContentBoxSize:Q(Math.round(S*devicePixelRatio),Math.round(T*devicePixelRatio),i),borderBoxSize:Q(R,E,i),contentBoxSize:Q(S,T,i),contentRect:new F(d,s,S,T)});return U.set(e,B),B},te=function(e,t,n){var o=ee(e,n),r=o.borderBoxSize,i=o.contentBoxSize,l=o.devicePixelContentBoxSize;switch(t){case $.DEVICE_PIXEL_CONTENT_BOX:return l;case $.BORDER_BOX:return r;default:return i}},ne=function(){return function(e){var t=ee(e);this.target=e,this.contentRect=t.contentRect,this.borderBoxSize=X([t.borderBoxSize]),this.contentBoxSize=X([t.contentBoxSize]),this.devicePixelContentBoxSize=X([t.devicePixelContentBoxSize])}}(),oe=function(e){if(V(e))return 1/0;for(var t=0,n=e.parentNode;n;)t+=1,n=n.parentNode;return t},re=function(){var e=1/0,t=[];L.forEach(function(n){if(0!==n.activeTargets.length){var o=[];n.activeTargets.forEach(function(t){var n=new ne(t.target),r=oe(t.target);o.push(n),t.lastReportedSize=te(t.target,t.observedBox),r<e&&(e=r)}),t.push(function(){n.callback.call(n.observer,o,n.observer)}),n.activeTargets.splice(0,n.activeTargets.length)}});for(var n=0,o=t;n<o.length;n++){(0,o[n])()}return e},ie=function(e){L.forEach(function(t){t.activeTargets.splice(0,t.activeTargets.length),t.skippedTargets.splice(0,t.skippedTargets.length),t.observationTargets.forEach(function(n){n.isActive()&&(oe(n.target)>e?t.activeTargets.push(n):t.skippedTargets.push(n))})})},le=function(){var e,t=0;for(ie(t);H();)t=re(),ie(t);return L.some(function(e){return e.skippedTargets.length>0})&&("function"==typeof ErrorEvent?e=new ErrorEvent("error",{message:I}):((e=document.createEvent("Event")).initEvent("error",!1,!1),e.message=I),window.dispatchEvent(e)),t>0},ae=[],se=function(e){if(!N){var t=0,n=document.createTextNode("");new MutationObserver(function(){return ae.splice(0).forEach(function(e){return e()})}).observe(n,{characterData:!0}),N=function(){n.textContent="".concat(t?t--:t++)}}ae.push(e),N()},ce=0,ue={attributes:!0,characterData:!0,childList:!0,subtree:!0},de=["resize","load","transitionend","animationend","animationstart","animationiteration","keyup","keydown","mouseup","mousedown","mouseover","mouseout","blur","focus"],ve=function(e){return void 0===e&&(e=0),Date.now()+e},he=!1,fe=new(function(){function e(){var e=this;this.stopped=!0,this.listener=function(){return e.schedule()}}return e.prototype.run=function(e){var t=this;if(void 0===e&&(e=250),!he){he=!0;var n,o=ve(e);n=function(){var n=!1;try{n=le()}finally{if(he=!1,e=o-ve(),!ce)return;n?t.run(1e3):e>0?t.run(e):t.start()}},se(function(){requestAnimationFrame(n)})}},e.prototype.schedule=function(){this.stop(),this.run()},e.prototype.observe=function(){var e=this,t=function(){return e.observer&&e.observer.observe(document.body,ue)};document.body?t():Y.addEventListener("DOMContentLoaded",t)},e.prototype.start=function(){var e=this;this.stopped&&(this.stopped=!1,this.observer=new MutationObserver(this.listener),this.observe(),de.forEach(function(t){return Y.addEventListener(t,e.listener,!0)}))},e.prototype.stop=function(){var e=this;this.stopped||(this.observer&&this.observer.disconnect(),de.forEach(function(t){return Y.removeEventListener(t,e.listener,!0)}),this.stopped=!0)},e}()),be=function(e){!ce&&e>0&&fe.start(),!(ce+=e)&&fe.stop()},pe=function(){function e(e,t){this.target=e,this.observedBox=t||$.CONTENT_BOX,this.lastReportedSize={inlineSize:0,blockSize:0}}return e.prototype.isActive=function(){var e,t=te(this.target,this.observedBox,!0);return e=this.target,_(e)||function(e){switch(e.tagName){case"INPUT":if("image"!==e.type)break;case"VIDEO":case"AUDIO":case"EMBED":case"OBJECT":case"CANVAS":case"IFRAME":case"IMG":return!0}return!1}(e)||"inline"!==getComputedStyle(e).display||(this.lastReportedSize=t),this.lastReportedSize.inlineSize!==t.inlineSize||this.lastReportedSize.blockSize!==t.blockSize},e}(),ge=function(){return function(e,t){this.activeTargets=[],this.skippedTargets=[],this.observationTargets=[],this.observer=e,this.callback=t}}(),we=new WeakMap,me=function(e,t){for(var n=0;n<e.length;n+=1)if(e[n].target===t)return n;return-1},ye=function(){function e(){}return e.connect=function(e,t){var n=new ge(e,t);we.set(e,n)},e.observe=function(e,t,n){var o=we.get(e),r=0===o.observationTargets.length;me(o.observationTargets,t)<0&&(r&&L.push(o),o.observationTargets.push(new pe(t,n&&n.box)),be(1),fe.schedule())},e.unobserve=function(e,t){var n=we.get(e),o=me(n.observationTargets,t),r=1===n.observationTargets.length;o>=0&&(r&&L.splice(L.indexOf(n),1),n.observationTargets.splice(o,1),be(-1))},e.disconnect=function(e){var t=this,n=we.get(e);n.observationTargets.slice().forEach(function(n){return t.unobserve(e,n.target)}),n.activeTargets.splice(0,n.activeTargets.length)},e}(),ze=function(){function e(e){if(0===arguments.length)throw new TypeError("Failed to construct 'ResizeObserver': 1 argument required, but only 0 present.");if("function"!=typeof e)throw new TypeError("Failed to construct 'ResizeObserver': The callback provided as parameter 1 is not a function.");ye.connect(this,e)}return e.prototype.observe=function(e,t){if(0===arguments.length)throw new TypeError("Failed to execute 'observe' on 'ResizeObserver': 1 argument required, but only 0 present.");if(!A(e))throw new TypeError("Failed to execute 'observe' on 'ResizeObserver': parameter 1 is not of type 'Element");ye.observe(this,e,t)},e.prototype.unobserve=function(e){if(0===arguments.length)throw new TypeError("Failed to execute 'unobserve' on 'ResizeObserver': 1 argument required, but only 0 present.");if(!A(e))throw new TypeError("Failed to execute 'unobserve' on 'ResizeObserver': parameter 1 is not of type 'Element");ye.unobserve(this,e)},e.prototype.disconnect=function(){ye.disconnect(this)},e.toString=function(){return"function ResizeObserver () { [polyfill code] }"},e}();const xe=new class{constructor(){this.handleResize=this.handleResize.bind(this),this.observer=new("undefined"!=typeof window&&window.ResizeObserver||ze)(this.handleResize),this.elHandlersMap=new Map}handleResize(e){for(const t of e){const e=this.elHandlersMap.get(t.target);void 0!==e&&e(t)}}registerHandler(e,t){this.elHandlersMap.set(e,t),this.observer.observe(e)}unregisterHandler(e){this.elHandlersMap.has(e)&&(this.elHandlersMap.delete(e),this.observer.unobserve(e))}},Se=n({name:"ResizeObserver",props:{onResize:Function},setup(e){let t=!1;const n=o().proxy;function l(t){const{onResize:n}=e;void 0!==n&&n(t)}r(()=>{const e=n.$el;void 0!==e&&(e.nextElementSibling!==e.nextSibling&&3===e.nodeType&&""!==e.nodeValue||null!==e.nextElementSibling&&(xe.registerHandler(e.nextElementSibling,l),t=!0))}),i(()=>{t&&xe.unregisterHandler(n.$el.nextElementSibling)})},render(){return l(this.$slots,"default")}});function Te(e){const{left:t,right:n,top:o,bottom:r}=a(e);return`${o} ${t} ${r} ${n}`}const Re=n({render(){var e,t;return null===(t=(e=this.$slots).default)||void 0===t?void 0:t.call(e)}}),Ee=s("scrollbar","\n overflow: hidden;\n position: relative;\n z-index: auto;\n height: 100%;\n width: 100%;\n",[c(">",[s("scrollbar-container","\n width: 100%;\n overflow: scroll;\n height: 100%;\n min-height: inherit;\n max-height: inherit;\n scrollbar-width: none;\n ",[c("&::-webkit-scrollbar, &::-webkit-scrollbar-track-piece, &::-webkit-scrollbar-thumb","\n width: 0;\n height: 0;\n display: none;\n "),c(">",[s("scrollbar-content","\n box-sizing: border-box;\n min-width: 100%;\n ")])])]),c(">, +",[s("scrollbar-rail","\n position: absolute;\n pointer-events: none;\n user-select: none;\n background: var(--n-scrollbar-rail-color);\n -webkit-user-select: none;\n ",[u("horizontal","\n height: var(--n-scrollbar-height);\n ",[c(">",[d("scrollbar","\n height: var(--n-scrollbar-height);\n border-radius: var(--n-scrollbar-border-radius);\n right: 0;\n ")])]),u("horizontal--top","\n top: var(--n-scrollbar-rail-top-horizontal-top); \n right: var(--n-scrollbar-rail-right-horizontal-top); \n bottom: var(--n-scrollbar-rail-bottom-horizontal-top); \n left: var(--n-scrollbar-rail-left-horizontal-top); \n "),u("horizontal--bottom","\n top: var(--n-scrollbar-rail-top-horizontal-bottom); \n right: var(--n-scrollbar-rail-right-horizontal-bottom); \n bottom: var(--n-scrollbar-rail-bottom-horizontal-bottom); \n left: var(--n-scrollbar-rail-left-horizontal-bottom); \n "),u("vertical","\n width: var(--n-scrollbar-width);\n ",[c(">",[d("scrollbar","\n width: var(--n-scrollbar-width);\n border-radius: var(--n-scrollbar-border-radius);\n bottom: 0;\n ")])]),u("vertical--left","\n top: var(--n-scrollbar-rail-top-vertical-left); \n right: var(--n-scrollbar-rail-right-vertical-left); \n bottom: var(--n-scrollbar-rail-bottom-vertical-left); \n left: var(--n-scrollbar-rail-left-vertical-left); \n "),u("vertical--right","\n top: var(--n-scrollbar-rail-top-vertical-right); \n right: var(--n-scrollbar-rail-right-vertical-right); \n bottom: var(--n-scrollbar-rail-bottom-vertical-right); \n left: var(--n-scrollbar-rail-left-vertical-right); \n "),u("disabled",[c(">",[d("scrollbar","pointer-events: none;")])]),c(">",[d("scrollbar","\n z-index: 1;\n position: absolute;\n cursor: pointer;\n pointer-events: all;\n background-color: var(--n-scrollbar-color);\n transition: background-color .2s var(--n-scrollbar-bezier);\n ",[v(),c("&:hover","background-color: var(--n-scrollbar-color-hover);")])])])])]),Be=n({name:"Scrollbar",props:Object.assign(Object.assign({},h.props),{duration:{type:Number,default:0},scrollable:{type:Boolean,default:!0},xScrollable:Boolean,trigger:{type:String,default:"hover"},useUnifiedContainer:Boolean,triggerDisplayManually:Boolean,container:Function,content:Function,containerClass:String,containerStyle:[String,Object],contentClass:[String,Array],contentStyle:[String,Object],horizontalRailStyle:[String,Object],verticalRailStyle:[String,Object],onScroll:Function,onWheel:Function,onResize:Function,internalOnUpdateScrollLeft:Function,internalHoistYRail:Boolean,yPlacement:{type:String,default:"right"},xPlacement:{type:String,default:"bottom"}}),inheritAttrs:!1,setup(e){const{mergedClsPrefixRef:t,inlineThemeDisabled:n,mergedRtlRef:o}=f(e),l=R("Scrollbar",o,t),s=b(null),c=b(null),u=b(null),d=b(null),v=b(null),z=b(null),x=b(null),S=b(null),T=b(null),B=b(null),M=b(null),O=b(0),$=b(0),D=b(!1),L=b(!1);let H,I,N=!1,X=!1,j=0,F=0,_=0,V=0;const A=C,Y=h("Scrollbar","-scrollbar",Ee,p,e,t),U=g(()=>{const{value:e}=S,{value:t}=z,{value:n}=B;return null===e||null===t||null===n?0:Math.min(e,n*e/t+1.5*w(Y.value.self.width))}),q=g(()=>`${U.value}px`),G=g(()=>{const{value:e}=T,{value:t}=x,{value:n}=M;return null===e||null===t||null===n?0:n*e/t+1.5*w(Y.value.self.height)}),J=g(()=>`${G.value}px`),K=g(()=>{const{value:e}=S,{value:t}=O,{value:n}=z,{value:o}=B;if(null===e||null===n||null===o)return 0;{const r=n-e;return r?t/r*(o-U.value):0}}),Q=g(()=>`${K.value}px`),Z=g(()=>{const{value:e}=T,{value:t}=$,{value:n}=x,{value:o}=M;if(null===e||null===n||null===o)return 0;{const r=n-e;return r?t/r*(o-G.value):0}}),ee=g(()=>`${Z.value}px`),te=g(()=>{const{value:e}=S,{value:t}=z;return null!==e&&null!==t&&t>e}),ne=g(()=>{const{value:e}=T,{value:t}=x;return null!==e&&null!==t&&t>e}),oe=g(()=>{const{trigger:t}=e;return"none"===t||D.value}),re=g(()=>{const{trigger:t}=e;return"none"===t||L.value}),ie=g(()=>{const{container:t}=e;return t?t():c.value}),le=g(()=>{const{content:t}=e;return t?t():u.value}),ae=(t,n)=>{if(!e.scrollable)return;if("number"==typeof t)return void ce(t,null!=n?n:0,0,!1,"auto");const{left:o,top:r,index:i,elSize:l,position:a,behavior:s,el:c,debounce:u=!0}=t;void 0===o&&void 0===r||ce(null!=o?o:0,null!=r?r:0,0,!1,s),void 0!==c?ce(0,c.offsetTop,c.offsetHeight,u,s):void 0!==i&&void 0!==l?ce(0,i*l,l,u,s):"bottom"===a?ce(0,Number.MAX_SAFE_INTEGER,0,!1,s):"top"===a&&ce(0,0,0,!1,s)},se=W(()=>{e.container||ae({top:O.value,left:$.value})});function ce(e,t,n,o,r){const{value:i}=ie;if(i){if(o){const{scrollTop:o,offsetHeight:l}=i;if(t>o)return void(t+n<=o+l||i.scrollTo({left:e,top:t+n-l,behavior:r}))}i.scrollTo({left:e,top:t,behavior:r})}}function ue(){!function(){void 0!==I&&window.clearTimeout(I);I=window.setTimeout(()=>{L.value=!1},e.duration)}(),function(){void 0!==H&&window.clearTimeout(H);H=window.setTimeout(()=>{D.value=!1},e.duration)}()}function de(){const{value:e}=ie;e&&(O.value=e.scrollTop,$.value=e.scrollLeft*((null==l?void 0:l.value)?-1:1))}function ve(){const{value:e}=ie;e&&(O.value=e.scrollTop,$.value=e.scrollLeft*((null==l?void 0:l.value)?-1:1),S.value=e.offsetHeight,T.value=e.offsetWidth,z.value=e.scrollHeight,x.value=e.scrollWidth);const{value:t}=v,{value:n}=d;t&&(M.value=t.offsetWidth),n&&(B.value=n.offsetHeight)}function he(){e.scrollable&&(e.useUnifiedContainer?ve():(!function(){const{value:e}=le;e&&(z.value=e.offsetHeight,x.value=e.offsetWidth);const{value:t}=ie;t&&(S.value=t.offsetHeight,T.value=t.offsetWidth);const{value:n}=v,{value:o}=d;n&&(M.value=n.offsetWidth),o&&(B.value=o.offsetHeight)}(),de()))}function fe(e){var t;return!(null===(t=s.value)||void 0===t?void 0:t.contains(E(e)))}function be(t){if(!X)return;void 0!==H&&window.clearTimeout(H),void 0!==I&&window.clearTimeout(I);const{value:n}=T,{value:o}=x,{value:r}=G;if(null===n||null===o)return;const i=(null==l?void 0:l.value)?window.innerWidth-t.clientX-_:t.clientX-_,a=o-n;let s=F+i*(o-n)/(n-r);s=Math.min(a,s),s=Math.max(s,0);const{value:c}=ie;if(c){c.scrollLeft=s*((null==l?void 0:l.value)?-1:1);const{internalOnUpdateScrollLeft:t}=e;t&&t(s)}}function pe(e){e.preventDefault(),e.stopPropagation(),k("mousemove",window,be,!0),k("mouseup",window,pe,!0),X=!1,he(),fe(e)&&ue()}function ge(e){if(!N)return;void 0!==H&&window.clearTimeout(H),void 0!==I&&window.clearTimeout(I);const{value:t}=S,{value:n}=z,{value:o}=U;if(null===t||null===n)return;const r=e.clientY-V,i=n-t;let l=j+r*(n-t)/(t-o);l=Math.min(i,l),l=Math.max(l,0);const{value:a}=ie;a&&(a.scrollTop=l)}function we(e){e.preventDefault(),e.stopPropagation(),k("mousemove",window,ge,!0),k("mouseup",window,we,!0),N=!1,he(),fe(e)&&ue()}m(()=>{const{value:e}=ne,{value:n}=te,{value:o}=t,{value:r}=v,{value:i}=d;r&&(e?r.classList.remove(`${o}-scrollbar-rail--disabled`):r.classList.add(`${o}-scrollbar-rail--disabled`)),i&&(n?i.classList.remove(`${o}-scrollbar-rail--disabled`):i.classList.add(`${o}-scrollbar-rail--disabled`))}),r(()=>{e.container||he()}),i(()=>{void 0!==H&&window.clearTimeout(H),void 0!==I&&window.clearTimeout(I),k("mousemove",window,ge,!0),k("mouseup",window,we,!0)});const me=g(()=>{const{common:{cubicBezierEaseInOut:e},self:{color:t,colorHover:n,height:o,width:r,borderRadius:i,railInsetHorizontalTop:s,railInsetHorizontalBottom:c,railInsetVerticalRight:u,railInsetVerticalLeft:d,railColor:v}}=Y.value,{top:h,right:f,bottom:b,left:p}=a(s),{top:g,right:w,bottom:m,left:y}=a(c),{top:z,right:x,bottom:S,left:T}=a((null==l?void 0:l.value)?Te(u):u),{top:R,right:E,bottom:B,left:M}=a((null==l?void 0:l.value)?Te(d):d);return{"--n-scrollbar-bezier":e,"--n-scrollbar-color":t,"--n-scrollbar-color-hover":n,"--n-scrollbar-border-radius":i,"--n-scrollbar-width":r,"--n-scrollbar-height":o,"--n-scrollbar-rail-top-horizontal-top":h,"--n-scrollbar-rail-right-horizontal-top":f,"--n-scrollbar-rail-bottom-horizontal-top":b,"--n-scrollbar-rail-left-horizontal-top":p,"--n-scrollbar-rail-top-horizontal-bottom":g,"--n-scrollbar-rail-right-horizontal-bottom":w,"--n-scrollbar-rail-bottom-horizontal-bottom":m,"--n-scrollbar-rail-left-horizontal-bottom":y,"--n-scrollbar-rail-top-vertical-right":z,"--n-scrollbar-rail-right-vertical-right":x,"--n-scrollbar-rail-bottom-vertical-right":S,"--n-scrollbar-rail-left-vertical-right":T,"--n-scrollbar-rail-top-vertical-left":R,"--n-scrollbar-rail-right-vertical-left":E,"--n-scrollbar-rail-bottom-vertical-left":B,"--n-scrollbar-rail-left-vertical-left":M,"--n-scrollbar-rail-color":v}}),ye=n?y("scrollbar",void 0,me,e):void 0,ze={scrollTo:ae,scrollBy:(t,n)=>{if(!e.scrollable)return;const{value:o}=ie;o&&("object"==typeof t?o.scrollBy(t):o.scrollBy(t,n||0))},sync:he,syncUnifiedContainer:ve,handleMouseEnterWrapper:function(){!function(){void 0!==H&&window.clearTimeout(H);D.value=!0}(),function(){void 0!==I&&window.clearTimeout(I);L.value=!0}(),he()},handleMouseLeaveWrapper:function(){ue()}};return Object.assign(Object.assign({},ze),{mergedClsPrefix:t,rtlEnabled:l,containerScrollTop:O,wrapperRef:s,containerRef:c,contentRef:u,yRailRef:d,xRailRef:v,needYBar:te,needXBar:ne,yBarSizePx:q,xBarSizePx:J,yBarTopPx:Q,xBarLeftPx:ee,isShowXBar:oe,isShowYBar:re,isIos:A,handleScroll:function(t){const{onScroll:n}=e;n&&n(t),de()},handleContentResize:()=>{se.isDeactivated||he()},handleContainerResize:t=>{if(se.isDeactivated)return;const{onResize:n}=e;n&&n(t),he()},handleYScrollMouseDown:function(e){e.preventDefault(),e.stopPropagation(),N=!0,P("mousemove",window,ge,!0),P("mouseup",window,we,!0),j=O.value,V=e.clientY},handleXScrollMouseDown:function(e){e.preventDefault(),e.stopPropagation(),X=!0,P("mousemove",window,be,!0),P("mouseup",window,pe,!0),F=$.value,_=(null==l?void 0:l.value)?window.innerWidth-e.clientX:e.clientX},cssVars:n?void 0:me,themeClass:null==ye?void 0:ye.themeClass,onRender:null==ye?void 0:ye.onRender})},render(){var e;const{$slots:t,mergedClsPrefix:n,triggerDisplayManually:o,rtlEnabled:r,internalHoistYRail:i,yPlacement:l,xPlacement:a,xScrollable:s}=this;if(!this.scrollable)return null===(e=t.default)||void 0===e?void 0:e.call(t);const c="none"===this.trigger,u=(e,t)=>z("div",{ref:"yRailRef",class:[`${n}-scrollbar-rail`,`${n}-scrollbar-rail--vertical`,`${n}-scrollbar-rail--vertical--${l}`,e],"data-scrollbar-rail":!0,style:[t||"",this.verticalRailStyle],"aria-hidden":!0},z(c?Re:S,c?null:{name:"fade-in-transition"},{default:()=>this.needYBar&&this.isShowYBar&&!this.isIos?z("div",{class:`${n}-scrollbar-rail__scrollbar`,style:{height:this.yBarSizePx,top:this.yBarTopPx},onMousedown:this.handleYScrollMouseDown}):null})),d=()=>{var e,l;return null===(e=this.onRender)||void 0===e||e.call(this),z("div",T(this.$attrs,{role:"none",ref:"wrapperRef",class:[`${n}-scrollbar`,this.themeClass,r&&`${n}-scrollbar--rtl`],style:this.cssVars,onMouseenter:o?void 0:this.handleMouseEnterWrapper,onMouseleave:o?void 0:this.handleMouseLeaveWrapper}),[this.container?null===(l=t.default)||void 0===l?void 0:l.call(t):z("div",{role:"none",ref:"containerRef",class:[`${n}-scrollbar-container`,this.containerClass],style:this.containerStyle,onScroll:this.handleScroll,onWheel:this.onWheel},z(Se,{onResize:this.handleContentResize},{default:()=>z("div",{ref:"contentRef",role:"none",style:[{width:this.xScrollable?"fit-content":null},this.contentStyle],class:[`${n}-scrollbar-content`,this.contentClass]},t)})),i?null:u(void 0,void 0),s&&z("div",{ref:"xRailRef",class:[`${n}-scrollbar-rail`,`${n}-scrollbar-rail--horizontal`,`${n}-scrollbar-rail--horizontal--${a}`],style:this.horizontalRailStyle,"data-scrollbar-rail":!0,"aria-hidden":!0},z(c?Re:S,c?null:{name:"fade-in-transition"},{default:()=>this.needXBar&&this.isShowXBar&&!this.isIos?z("div",{class:`${n}-scrollbar-rail__scrollbar`,style:{width:this.xBarSizePx,right:r?this.xBarLeftPx:void 0,left:r?void 0:this.xBarLeftPx},onMousedown:this.handleXScrollMouseDown}):null}))])},v=this.container?d():z(Se,{onResize:this.handleContainerResize},{default:d});return i?z(x,null,v,u(this.themeClass,this.cssVars)):v}}),Me=Be,Oe=Be;export{Me as N,Se as V,Re as W,Oe as X,k as a,E as g,P as o,xe as r,W as u};
import{r as e,i as t,g as r,h as n}from"./Button-ByITIhW2.js";import{j as o,b3 as a,f as i,g as s,u as l,h as p,b4 as d,v as g,b5 as c,az as C}from"./index-BGNM-WBG.js";import{u as m}from"./use-rtl-iN3przWb.js";const u=e("error",()=>o("svg",{viewBox:"0 0 48 48",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},o("g",{stroke:"none","stroke-width":"1","fill-rule":"evenodd"},o("g",{"fill-rule":"nonzero"},o("path",{d:"M24,4 C35.045695,4 44,12.954305 44,24 C44,35.045695 35.045695,44 24,44 C12.954305,44 4,35.045695 4,24 C4,12.954305 12.954305,4 24,4 Z M17.8838835,16.1161165 L17.7823881,16.0249942 C17.3266086,15.6583353 16.6733914,15.6583353 16.2176119,16.0249942 L16.1161165,16.1161165 L16.0249942,16.2176119 C15.6583353,16.6733914 15.6583353,17.3266086 16.0249942,17.7823881 L16.1161165,17.8838835 L22.233,24 L16.1161165,30.1161165 L16.0249942,30.2176119 C15.6583353,30.6733914 15.6583353,31.3266086 16.0249942,31.7823881 L16.1161165,31.8838835 L16.2176119,31.9750058 C16.6733914,32.3416647 17.3266086,32.3416647 17.7823881,31.9750058 L17.8838835,31.8838835 L24,25.767 L30.1161165,31.8838835 L30.2176119,31.9750058 C30.6733914,32.3416647 31.3266086,32.3416647 31.7823881,31.9750058 L31.8838835,31.8838835 L31.9750058,31.7823881 C32.3416647,31.3266086 32.3416647,30.6733914 31.9750058,30.2176119 L31.8838835,30.1161165 L25.767,24 L31.8838835,17.8838835 L31.9750058,17.7823881 C32.3416647,17.3266086 32.3416647,16.6733914 31.9750058,16.2176119 L31.8838835,16.1161165 L31.7823881,16.0249942 C31.3266086,15.6583353 30.6733914,15.6583353 30.2176119,16.0249942 L30.1161165,16.1161165 L24,22.233 L17.8838835,16.1161165 L17.7823881,16.0249942 L17.8838835,16.1161165 Z"}))))),f=e("info",()=>o("svg",{viewBox:"0 0 28 28",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},o("g",{stroke:"none","stroke-width":"1","fill-rule":"evenodd"},o("g",{"fill-rule":"nonzero"},o("path",{d:"M14,2 C20.6274,2 26,7.37258 26,14 C26,20.6274 20.6274,26 14,26 C7.37258,26 2,20.6274 2,14 C2,7.37258 7.37258,2 14,2 Z M14,11 C13.4477,11 13,11.4477 13,12 L13,12 L13,20 C13,20.5523 13.4477,21 14,21 C14.5523,21 15,20.5523 15,20 L15,20 L15,12 C15,11.4477 14.5523,11 14,11 Z M14,6.75 C13.3096,6.75 12.75,7.30964 12.75,8 C12.75,8.69036 13.3096,9.25 14,9.25 C14.6904,9.25 15.25,8.69036 15.25,8 C15.25,7.30964 14.6904,6.75 14,6.75 Z"}))))),L=e("success",()=>o("svg",{viewBox:"0 0 48 48",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},o("g",{stroke:"none","stroke-width":"1","fill-rule":"evenodd"},o("g",{"fill-rule":"nonzero"},o("path",{d:"M24,4 C35.045695,4 44,12.954305 44,24 C44,35.045695 35.045695,44 24,44 C12.954305,44 4,35.045695 4,24 C4,12.954305 12.954305,4 24,4 Z M32.6338835,17.6161165 C32.1782718,17.1605048 31.4584514,17.1301307 30.9676119,17.5249942 L30.8661165,17.6161165 L20.75,27.732233 L17.1338835,24.1161165 C16.6457281,23.6279612 15.8542719,23.6279612 15.3661165,24.1161165 C14.9105048,24.5717282 14.8801307,25.2915486 15.2749942,25.7823881 L15.3661165,25.8838835 L19.8661165,30.3838835 C20.3217282,30.8394952 21.0415486,30.8698693 21.5323881,30.4750058 L21.6338835,30.3838835 L32.6338835,19.3838835 C33.1220388,18.8957281 33.1220388,18.1042719 32.6338835,17.6161165 Z"}))))),v=e("warning",()=>o("svg",{viewBox:"0 0 24 24",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},o("g",{stroke:"none","stroke-width":"1","fill-rule":"evenodd"},o("g",{"fill-rule":"nonzero"},o("path",{d:"M12,2 C17.523,2 22,6.478 22,12 C22,17.522 17.523,22 12,22 C6.477,22 2,17.522 2,12 C2,6.478 6.477,2 12,2 Z M12.0018002,15.0037242 C11.450254,15.0037242 11.0031376,15.4508407 11.0031376,16.0023869 C11.0031376,16.553933 11.450254,17.0010495 12.0018002,17.0010495 C12.5533463,17.0010495 13.0004628,16.553933 13.0004628,16.0023869 C13.0004628,15.4508407 12.5533463,15.0037242 12.0018002,15.0037242 Z M11.99964,7 C11.4868042,7.00018474 11.0642719,7.38637706 11.0066858,7.8837365 L11,8.00036004 L11.0018003,13.0012393 L11.00857,13.117858 C11.0665141,13.6151758 11.4893244,14.0010638 12.0021602,14.0008793 C12.514996,14.0006946 12.9375283,13.6145023 12.9951144,13.1171428 L13.0018002,13.0005193 L13,7.99964009 L12.9932303,7.8830214 C12.9352861,7.38570354 12.5124758,6.99981552 11.99964,7 Z"})))));const w={name:"Space",self:function(){return a}};let h;function x(){if(!t)return!0;if(void 0===h){const e=document.createElement("div");e.style.display="flex",e.style.flexDirection="column",e.style.rowGap="1px",e.appendChild(document.createElement("div")),e.appendChild(document.createElement("div")),document.body.appendChild(e);const t=1===e.scrollHeight;return document.body.removeChild(e),h=t}return h}const y=i({name:"Space",props:Object.assign(Object.assign({},l.props),{align:String,justify:{type:String,default:"start"},inline:Boolean,vertical:Boolean,reverse:Boolean,size:{type:[String,Number,Array],default:"medium"},wrapItem:{type:Boolean,default:!0},itemClass:String,itemStyle:[String,Object],wrap:{type:Boolean,default:!0},internalUseGap:{type:Boolean,default:void 0}}),setup(e){const{mergedClsPrefixRef:t,mergedRtlRef:r}=s(e),n=l("Space","-space",void 0,w,e,t),o=m("Space",r,t);return{useGap:x(),rtlEnabled:o,mergedClsPrefix:t,margin:p(()=>{const{size:t}=e;if(Array.isArray(t))return{horizontal:t[0],vertical:t[1]};if("number"==typeof t)return{horizontal:t,vertical:t};const{self:{[g("gap",t)]:r}}=n.value,{row:o,col:a}=c(r);return{horizontal:C(a),vertical:C(o)}})}},render(){const{vertical:e,reverse:t,align:a,inline:i,justify:s,itemClass:l,itemStyle:p,margin:g,wrap:c,mergedClsPrefix:C,rtlEnabled:m,useGap:u,wrapItem:f,internalUseGap:L}=this,v=r(n(this),!1);if(!v.length)return null;const w=`${g.horizontal}px`,h=g.horizontal/2+"px",x=`${g.vertical}px`,y=g.vertical/2+"px",b=v.length-1,B=s.startsWith("space-");return o("div",{role:"none",class:[`${C}-space`,m&&`${C}-space--rtl`],style:{display:i?"inline-flex":"flex",flexDirection:e&&!t?"column":e&&t?"column-reverse":!e&&t?"row-reverse":"row",justifyContent:["start","end"].includes(s)?`flex-${s}`:s,flexWrap:!c||e?"nowrap":"wrap",marginTop:u||e?"":`-${y}`,marginBottom:u||e?"":`-${y}`,alignItems:a,gap:u?`${g.vertical}px ${g.horizontal}px`:""}},f||!u&&!L?v.map((t,r)=>t.type===d?t:o("div",{role:"none",class:l,style:[p,{maxWidth:"100%"},u?"":e?{marginBottom:r!==b?x:""}:m?{marginLeft:B?"space-between"===s&&r===b?"":h:r!==b?w:"",marginRight:B?"space-between"===s&&0===r?"":h:"",paddingTop:y,paddingBottom:y}:{marginRight:B?"space-between"===s&&r===b?"":h:r!==b?w:"",marginLeft:B?"space-between"===s&&0===r?"":h:"",paddingTop:y,paddingBottom:y}]},t)):v)}});export{u as E,f as I,L as S,v as W,y as _};
const o=(o,t)=>{const c=o.__vccOpts||o;for(const[s,n]of t)c[s]=n;return c};export{o as _};
This source diff could not be displayed because it is too large. You can view the blob instead.
import{j as t,d as e,q as s,f as n,g as l,u as i,h as r,i as o,t as c,v as a,o as u,x as f,n as d,w as p,y as h,z as v}from"./index-BGNM-WBG.js";import{N as x,_ as g}from"./Button-ByITIhW2.js";import{I as z,S as m,W as w,E as y,_ as C}from"./Space-BiFpHGv7.js";import"./use-rtl-iN3przWb.js";const B=e("result","\n color: var(--n-text-color);\n line-height: var(--n-line-height);\n font-size: var(--n-font-size);\n transition:\n color .3s var(--n-bezier);\n",[e("result-icon","\n display: flex;\n justify-content: center;\n transition: color .3s var(--n-bezier);\n ",[s("status-image","\n font-size: var(--n-icon-size);\n width: 1em;\n height: 1em;\n "),e("base-icon","\n color: var(--n-icon-color);\n font-size: var(--n-icon-size);\n ")]),e("result-content",{marginTop:"24px"}),e("result-footer","\n margin-top: 24px;\n text-align: center;\n "),e("result-header",[s("title","\n margin-top: 16px;\n font-weight: var(--n-title-font-weight);\n transition: color .3s var(--n-bezier);\n text-align: center;\n color: var(--n-title-text-color);\n font-size: var(--n-title-font-size);\n "),s("description","\n margin-top: 4px;\n text-align: center;\n font-size: var(--n-font-size);\n ")])]),F={403:function(){return t("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 36 36"},t("path",{fill:"#EF9645",d:"M15.5 2.965c1.381 0 2.5 1.119 2.5 2.5v.005L20.5.465c1.381 0 2.5 1.119 2.5 2.5V4.25l2.5-1.535c1.381 0 2.5 1.119 2.5 2.5V8.75L29 18H15.458L15.5 2.965z"}),t("path",{fill:"#FFDC5D",d:"M4.625 16.219c1.381-.611 3.354.208 4.75 2.188.917 1.3 1.187 3.151 2.391 3.344.46.073 1.234-.313 1.234-1.397V4.5s0-2 2-2 2 2 2 2v11.633c0-.029 1-.064 1-.082V2s0-2 2-2 2 2 2 2v14.053c0 .017 1 .041 1 .069V4.25s0-2 2-2 2 2 2 2v12.638c0 .118 1 .251 1 .398V8.75s0-2 2-2 2 2 2 2V24c0 6.627-5.373 12-12 12-4.775 0-8.06-2.598-9.896-5.292C8.547 28.423 8.096 26.051 8 25.334c0 0-.123-1.479-1.156-2.865-1.469-1.969-2.5-3.156-3.125-3.866-.317-.359-.625-1.707.906-2.384z"}))},404:function(){return t("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 36 36"},t("circle",{fill:"#FFCB4C",cx:"18",cy:"17.018",r:"17"}),t("path",{fill:"#65471B",d:"M14.524 21.036c-.145-.116-.258-.274-.312-.464-.134-.46.13-.918.59-1.021 4.528-1.021 7.577 1.363 7.706 1.465.384.306.459.845.173 1.205-.286.358-.828.401-1.211.097-.11-.084-2.523-1.923-6.182-1.098-.274.061-.554-.016-.764-.184z"}),t("ellipse",{fill:"#65471B",cx:"13.119",cy:"11.174",rx:"2.125",ry:"2.656"}),t("ellipse",{fill:"#65471B",cx:"24.375",cy:"12.236",rx:"2.125",ry:"2.656"}),t("path",{fill:"#F19020",d:"M17.276 35.149s1.265-.411 1.429-1.352c.173-.972-.624-1.167-.624-1.167s1.041-.208 1.172-1.376c.123-1.101-.861-1.363-.861-1.363s.97-.4 1.016-1.539c.038-.959-.995-1.428-.995-1.428s5.038-1.221 5.556-1.341c.516-.12 1.32-.615 1.069-1.694-.249-1.08-1.204-1.118-1.697-1.003-.494.115-6.744 1.566-8.9 2.068l-1.439.334c-.54.127-.785-.11-.404-.512.508-.536.833-1.129.946-2.113.119-1.035-.232-2.313-.433-2.809-.374-.921-1.005-1.649-1.734-1.899-1.137-.39-1.945.321-1.542 1.561.604 1.854.208 3.375-.833 4.293-2.449 2.157-3.588 3.695-2.83 6.973.828 3.575 4.377 5.876 7.952 5.048l3.152-.681z"}),t("path",{fill:"#65471B",d:"M9.296 6.351c-.164-.088-.303-.224-.391-.399-.216-.428-.04-.927.393-1.112 4.266-1.831 7.699-.043 7.843.034.433.231.608.747.391 1.154-.216.405-.74.546-1.173.318-.123-.063-2.832-1.432-6.278.047-.257.109-.547.085-.785-.042zm12.135 3.75c-.156-.098-.286-.243-.362-.424-.187-.442.023-.927.468-1.084 4.381-1.536 7.685.48 7.823.567.415.26.555.787.312 1.178-.242.39-.776.495-1.191.238-.12-.072-2.727-1.621-6.267-.379-.266.091-.553.046-.783-.096z"}))},418:function(){return t("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 36 36"},t("ellipse",{fill:"#292F33",cx:"18",cy:"26",rx:"18",ry:"10"}),t("ellipse",{fill:"#66757F",cx:"18",cy:"24",rx:"18",ry:"10"}),t("path",{fill:"#E1E8ED",d:"M18 31C3.042 31 1 16 1 12h34c0 2-1.958 19-17 19z"}),t("path",{fill:"#77B255",d:"M35 12.056c0 5.216-7.611 9.444-17 9.444S1 17.271 1 12.056C1 6.84 8.611 3.611 18 3.611s17 3.229 17 8.445z"}),t("ellipse",{fill:"#A6D388",cx:"18",cy:"13",rx:"15",ry:"7"}),t("path",{d:"M21 17c-.256 0-.512-.098-.707-.293-2.337-2.337-2.376-4.885-.125-8.262.739-1.109.9-2.246.478-3.377-.461-1.236-1.438-1.996-1.731-2.077-.553 0-.958-.443-.958-.996 0-.552.491-.995 1.043-.995.997 0 2.395 1.153 3.183 2.625 1.034 1.933.91 4.039-.351 5.929-1.961 2.942-1.531 4.332-.125 5.738.391.391.391 1.023 0 1.414-.195.196-.451.294-.707.294zm-6-2c-.256 0-.512-.098-.707-.293-2.337-2.337-2.376-4.885-.125-8.262.727-1.091.893-2.083.494-2.947-.444-.961-1.431-1.469-1.684-1.499-.552 0-.989-.447-.989-1 0-.552.458-1 1.011-1 .997 0 2.585.974 3.36 2.423.481.899 1.052 2.761-.528 5.131-1.961 2.942-1.531 4.332-.125 5.738.391.391.391 1.023 0 1.414-.195.197-.451.295-.707.295z",fill:"#5C913B"}))},500:function(){return t("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 36 36"},t("path",{fill:"#FFCC4D",d:"M36 18c0 9.941-8.059 18-18 18-9.94 0-18-8.059-18-18C0 8.06 8.06 0 18 0c9.941 0 18 8.06 18 18"}),t("ellipse",{fill:"#664500",cx:"18",cy:"27",rx:"5",ry:"6"}),t("path",{fill:"#664500",d:"M5.999 11c-.208 0-.419-.065-.599-.2-.442-.331-.531-.958-.2-1.4C8.462 5.05 12.816 5 13 5c.552 0 1 .448 1 1 0 .551-.445.998-.996 1-.155.002-3.568.086-6.204 3.6-.196.262-.497.4-.801.4zm24.002 0c-.305 0-.604-.138-.801-.4-2.64-3.521-6.061-3.598-6.206-3.6-.55-.006-.994-.456-.991-1.005C22.006 5.444 22.45 5 23 5c.184 0 4.537.05 7.8 4.4.332.442.242 1.069-.2 1.4-.18.135-.39.2-.599.2zm-16.087 4.5l1.793-1.793c.391-.391.391-1.023 0-1.414s-1.023-.391-1.414 0L12.5 14.086l-1.793-1.793c-.391-.391-1.023-.391-1.414 0s-.391 1.023 0 1.414l1.793 1.793-1.793 1.793c-.391.391-.391 1.023 0 1.414.195.195.451.293.707.293s.512-.098.707-.293l1.793-1.793 1.793 1.793c.195.195.451.293.707.293s.512-.098.707-.293c.391-.391.391-1.023 0-1.414L13.914 15.5zm11 0l1.793-1.793c.391-.391.391-1.023 0-1.414s-1.023-.391-1.414 0L23.5 14.086l-1.793-1.793c-.391-.391-1.023-.391-1.414 0s-.391 1.023 0 1.414l1.793 1.793-1.793 1.793c-.391.391-.391 1.023 0 1.414.195.195.451.293.707.293s.512-.098.707-.293l1.793-1.793 1.793 1.793c.195.195.451.293.707.293s.512-.098.707-.293c.391-.391.391-1.023 0-1.414L24.914 15.5z"}))},info:()=>t(z,null),success:()=>t(m,null),warning:()=>t(w,null),error:()=>t(y,null)},b=n({name:"Result",props:Object.assign(Object.assign({},i.props),{size:{type:String,default:"medium"},status:{type:String,default:"info"},title:String,description:String}),slots:Object,setup(t){const{mergedClsPrefixRef:e,inlineThemeDisabled:s}=l(t),n=i("Result","-result",B,c,t,e),u=r(()=>{const{size:e,status:s}=t,{common:{cubicBezierEaseInOut:l},self:{textColor:i,lineHeight:r,titleTextColor:o,titleFontWeight:c,[a("iconColor",s)]:u,[a("fontSize",e)]:f,[a("titleFontSize",e)]:d,[a("iconSize",e)]:p}}=n.value;return{"--n-bezier":l,"--n-font-size":f,"--n-icon-size":p,"--n-line-height":r,"--n-text-color":i,"--n-title-font-size":d,"--n-title-font-weight":c,"--n-title-text-color":o,"--n-icon-color":u||""}}),f=s?o("result",r(()=>{const{size:e,status:s}=t;let n="";return e&&(n+=e[0]),s&&(n+=s[0]),n}),u,t):void 0;return{mergedClsPrefix:e,cssVars:s?void 0:u,themeClass:null==f?void 0:f.themeClass,onRender:null==f?void 0:f.onRender}},render(){var e;const{status:s,$slots:n,mergedClsPrefix:l,onRender:i}=this;return null==i||i(),t("div",{class:[`${l}-result`,this.themeClass],style:this.cssVars},t("div",{class:`${l}-result-icon`},(null===(e=n.icon)||void 0===e?void 0:e.call(n))||t(x,{clsPrefix:l},{default:()=>F[s]()})),t("div",{class:`${l}-result-header`},this.title?t("div",{class:`${l}-result-header__title`},this.title):null,this.description?t("div",{class:`${l}-result-header__description`},this.description):null),n.default&&t("div",{class:`${l}-result-content`},n),n.footer&&t("div",{class:`${l}-result-footer`},n.footer()))}}),_={class:"flex justify-center items-center h-full"},j=n({__name:"index",setup(t){const e=v(),s=()=>{e.push("/")};return(t,e)=>{const n=g,l=C,i=b;return u(),f("div",_,[d(i,{status:"404",title:"页面不存在"},{footer:p(()=>[d(l,null,{default:p(()=>[d(n,{type:"primary",onClick:s},{default:p(()=>[h("返回")]),_:1})]),_:1})]),_:1})])}}});export{j as default};
import{_ as o}from"./_plugin-vue_export-helper-BCo6x5W8.js";import{c as e,a as r,s as t,b as l,d as n,e as s,f as a,u as i,r as d,g as c,p as u,h as b,i as h,j as v,k as f,l as C,o as p,m,w as g,n as x}from"./index-BGNM-WBG.js";import{u as y,N as S}from"./Scrollbar-DmIJvlAu.js";import"./use-rtl-iN3przWb.js";const B=e({name:"Layout",common:r,peers:{Scrollbar:t},self:function(o){const{baseColor:e,textColor2:r,bodyColor:t,cardColor:n,dividerColor:s,actionColor:a,scrollbarColor:i,scrollbarColorHover:d,invertedColor:c}=o;return{textColor:r,textColorInverted:"#FFF",color:t,colorEmbedded:a,headerColor:n,headerColorInverted:c,footerColor:a,footerColorInverted:c,headerBorderColor:s,headerBorderColorInverted:c,footerBorderColor:s,footerBorderColorInverted:c,siderBorderColor:s,siderBorderColorInverted:c,siderColor:n,siderColorInverted:c,siderToggleButtonBorder:`1px solid ${s}`,siderToggleButtonColor:e,siderToggleButtonIconColor:r,siderToggleButtonIconColorInverted:r,siderToggleBarColor:l(t,i),siderToggleBarColorHover:l(t,d),__invertScrollbar:"true"}}}),z={type:String,default:"static"},I=n("layout","\n color: var(--n-text-color);\n background-color: var(--n-color);\n box-sizing: border-box;\n position: relative;\n z-index: auto;\n flex: auto;\n overflow: hidden;\n transition:\n box-shadow .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n",[n("layout-scroll-container","\n overflow-x: hidden;\n box-sizing: border-box;\n height: 100%;\n "),s("absolute-positioned","\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n ")]),T={embedded:Boolean,position:z,nativeScrollbar:{type:Boolean,default:!0},scrollbarProps:Object,onScroll:Function,contentClass:String,contentStyle:{type:[String,Object],default:""},hasSider:Boolean,siderPlacement:{type:String,default:"left"}},$=f("n-layout");function R(o){return a({name:o?"LayoutContent":"Layout",props:Object.assign(Object.assign({},i.props),T),setup(o){const e=d(null),r=d(null),{mergedClsPrefixRef:t,inlineThemeDisabled:l}=c(o),n=i("Layout","-layout",I,B,o,t);u($,o);let s=0,a=0;y(()=>{if(o.nativeScrollbar){const o=e.value;o&&(o.scrollTop=a,o.scrollLeft=s)}});const v={scrollTo:function(t,l){if(o.nativeScrollbar){const{value:o}=e;o&&(void 0===l?o.scrollTo(t):o.scrollTo(t,l))}else{const{value:o}=r;o&&o.scrollTo(t,l)}}},f=b(()=>{const{common:{cubicBezierEaseInOut:e},self:r}=n.value;return{"--n-bezier":e,"--n-color":o.embedded?r.colorEmbedded:r.color,"--n-text-color":r.textColor}}),C=l?h("layout",b(()=>o.embedded?"e":""),f,o):void 0;return Object.assign({mergedClsPrefix:t,scrollableElRef:e,scrollbarInstRef:r,hasSiderStyle:{display:"flex",flexWrap:"nowrap",width:"100%",flexDirection:"row"},mergedTheme:n,handleNativeElScroll:e=>{var r;const t=e.target;s=t.scrollLeft,a=t.scrollTop,null===(r=o.onScroll)||void 0===r||r.call(o,e)},cssVars:l?void 0:f,themeClass:null==C?void 0:C.themeClass,onRender:null==C?void 0:C.onRender},v)},render(){var e;const{mergedClsPrefix:r,hasSider:t}=this;null===(e=this.onRender)||void 0===e||e.call(this);const l=t?this.hasSiderStyle:void 0,n=[this.themeClass,o&&`${r}-layout-content`,`${r}-layout`,`${r}-layout--${this.position}-positioned`];return v("div",{class:n,style:this.cssVars},this.nativeScrollbar?v("div",{ref:"scrollableElRef",class:[`${r}-layout-scroll-container`,this.contentClass],style:[this.contentStyle,l],onScroll:this.handleNativeElScroll},this.$slots):v(S,Object.assign({},this.scrollbarProps,{onScroll:this.onScroll,ref:"scrollbarInstRef",theme:this.mergedTheme.peers.Scrollbar,themeOverrides:this.mergedTheme.peerOverrides.Scrollbar,contentClass:this.contentClass,contentStyle:[this.contentStyle,l]}),this.$slots))}})}const j=R(!1),O=R(!0),w=n("layout-footer","\n transition:\n box-shadow .3s var(--n-bezier),\n color .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n border-color .3s var(--n-bezier);\n color: var(--n-text-color);\n background-color: var(--n-color);\n box-sizing: border-box;\n",[s("absolute-positioned","\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n "),s("bordered","\n border-top: solid 1px var(--n-border-color);\n ")]),P=Object.assign(Object.assign({},i.props),{inverted:Boolean,position:z,bordered:Boolean}),L=a({name:"LayoutFooter",props:P,setup(o){const{mergedClsPrefixRef:e,inlineThemeDisabled:r}=c(o),t=i("Layout","-layout-footer",w,B,o,e),l=b(()=>{const{common:{cubicBezierEaseInOut:e},self:r}=t.value,l={"--n-bezier":e};return o.inverted?(l["--n-color"]=r.footerColorInverted,l["--n-text-color"]=r.textColorInverted,l["--n-border-color"]=r.footerBorderColorInverted):(l["--n-color"]=r.footerColor,l["--n-text-color"]=r.textColor,l["--n-border-color"]=r.footerBorderColor),l}),n=r?h("layout-footer",b(()=>o.inverted?"a":"b"),l,o):void 0;return{mergedClsPrefix:e,cssVars:r?void 0:l,themeClass:null==n?void 0:n.themeClass,onRender:null==n?void 0:n.onRender}},render(){var o;const{mergedClsPrefix:e}=this;return null===(o=this.onRender)||void 0===o||o.call(this),v("div",{class:[`${e}-layout-footer`,this.themeClass,this.position&&`${e}-layout-footer--${this.position}-positioned`,this.bordered&&`${e}-layout-footer--bordered`],style:this.cssVars},this.$slots)}}),E=n("layout-header","\n transition:\n color .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier),\n border-color .3s var(--n-bezier);\n box-sizing: border-box;\n width: 100%;\n background-color: var(--n-color);\n color: var(--n-text-color);\n",[s("absolute-positioned","\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n "),s("bordered","\n border-bottom: solid 1px var(--n-border-color);\n ")]),k={position:z,inverted:Boolean,bordered:{type:Boolean,default:!1}},_=a({name:"LayoutHeader",props:Object.assign(Object.assign({},i.props),k),setup(o){const{mergedClsPrefixRef:e,inlineThemeDisabled:r}=c(o),t=i("Layout","-layout-header",E,B,o,e),l=b(()=>{const{common:{cubicBezierEaseInOut:e},self:r}=t.value,l={"--n-bezier":e};return o.inverted?(l["--n-color"]=r.headerColorInverted,l["--n-text-color"]=r.textColorInverted,l["--n-border-color"]=r.headerBorderColorInverted):(l["--n-color"]=r.headerColor,l["--n-text-color"]=r.textColor,l["--n-border-color"]=r.headerBorderColor),l}),n=r?h("layout-header",b(()=>o.inverted?"a":"b"),l,o):void 0;return{mergedClsPrefix:e,cssVars:r?void 0:l,themeClass:null==n?void 0:n.themeClass,onRender:null==n?void 0:n.onRender}},render(){var o;const{mergedClsPrefix:e}=this;return null===(o=this.onRender)||void 0===o||o.call(this),v("div",{class:[`${e}-layout-header`,this.themeClass,this.position&&`${e}-layout-header--${this.position}-positioned`,this.bordered&&`${e}-layout-header--bordered`],style:this.cssVars},this.$slots)}});const V=o({},[["render",function(o,e){const r=_,t=C("router-view"),l=O,n=L,s=j;return p(),m(s,{class:"h-full overflow-hidden"},{default:g(()=>[x(r),x(l,{"content-class":"flex flex-col flex-auto overflow-x-auto bg-codeColor",class:"h-full flex flex-col"},{default:g(()=>[x(t)]),_:1}),x(n)]),_:1})}]]);export{V as default};
This source diff could not be displayed because it is too large. You can view the blob instead.
import{_ as r}from"./_plugin-vue_export-helper-BCo6x5W8.js";import{o as e,x as o}from"./index-BGNM-WBG.js";const t=r({},[["render",function(r,t){return e(),o("div")}]]);export{t as default};
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:"";content:none}table{border-collapse:collapse;border-spacing:0}html,body,#app{width:100%;height:100%;overflow:hidden;font-size:14px;font-family:PuHuiRegular}div{box-sizing:border-box}::-webkit-scrollbar{border-radius:5px;-moz-border-radius:5px;-webkit-border-radius:5px;width:5px;height:5px}::-webkit-scrollbar-thumb{border-radius:5px;-moz-border-radius:5px;-webkit-border-radius:5px;background-color:var(--scrollbar-color);background-clip:padding-box;min-height:28px}::-webkit-scrollbar-thumb:hover{border-radius:5px;-moz-border-radius:5px;-webkit-border-radius:5px;background-color:var(--scrollbar-color-hover)}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.\!container{width:100%!important}.container{width:100%}@media (min-width: 640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width: 768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width: 1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width: 1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width: 1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.z-10{z-index:10}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mr-\[15px\]{margin-right:15px}.mt-3{margin-top:.75rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-full{height:100%}.max-h-48{max-height:12rem}.max-h-\[300px\]{max-height:300px}.min-h-\[100px\]{min-height:100px}.w-\[350px\]{width:350px}.w-full{width:100%}.min-w-\[72px\]{min-width:72px}.flex-1{flex:1 1 0%}.flex-auto{flex:1 1 auto}.flex-shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-normal{white-space:normal}.rounded{border-radius:.25rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-solid{border-style:solid}.border-borderColor{border-color:var(--border-color)}.border-green-100{--tw-border-opacity: 1;border-color:rgb(220 252 231 / var(--tw-border-opacity))}.border-red-100{--tw-border-opacity: 1;border-color:rgb(254 226 226 / var(--tw-border-opacity))}.bg-baseColor{background-color:var(--base-color)}.bg-codeColor{background-color:var(--code-color)}.bg-green-50{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity))}.bg-progressRailColor{background-color:var(--progress-rail-color)}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity))}.bg-warningColorSuppl{background-color:var(--warning-color-suppl)}.\!p-0{padding:0!important}.p-\[10px\]{padding:10px}.px-3{padding-left:.75rem;padding-right:.75rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-\[15px\]{padding-top:15px;padding-bottom:15px}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[11px\]{font-size:11px}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.italic{font-style:italic}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.opacity-60{opacity:.6}.blur{--tw-blur: blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}:before,:after{z-index:1}.w-e-text-container [data-slate-editor]{font-size:13px;line-height:1.6}img{width:100%}a{text-decoration:none}text{text-indent:0!important;display:inline-block!important}[data-w-e-type=TITLE],[data-w-e-type=HTML-TITLE],[data-w-e-type=TITLEC]{font-weight:700;font-size:15px}[data-w-e-type=PARAC]>*,[data-w-e-type=PARA]>*{display:inline!important;text-indent:0!important}[data-w-e-type=TITLE]>*,[data-w-e-type=TITLEC]>*,[data-w-e-type=HTML-TITLE]>*{display:inline-block!important;text-indent:0!important}[data-w-e-type=ZONELST]{display:none!important}[data-w-e-type=STDNAME]{display:inline-block!important;text-indent:0!important;margin:0 5px!important}:not([data-w-e-type=CEP],[data-w-e-type=SUBTASK])[MERGED=TRUE]{background-color:#ff0!important}[MERGED=TRUE]:before{background-color:#ff0!important}[data-w-e-type=JOBCARD] [data-w-e-type=REFBLOCK] [data-w-e-type=GRPHCREF]{text-decoration:none;color:#00f;cursor:pointer}[data-w-e-type=CON]{display:inline!important}[data-w-e-type=CON] [data-w-e-type=CONNBR]{text-indent:0!important;display:inline!important;white-space:nowrap!important}[data-w-e-type=CON] [data-w-e-type=CONNAME]{text-indent:0!important;display:inline!important}[data-w-e-type=SPECNOTE] [data-w-e-type=PARAC]:before{font-weight:700;content:"特 殊 注 释: "}[data-w-e-type=SPECNOTE] [data-w-e-type=PARA]:before{font-weight:700;content:"SPECIAL NOTE: "}[data-w-e-type=EIN]{display:inline-flex!important;text-indent:0!important;margin:0 3px!important;cursor:pointer;color:#00f}[data-w-e-type=EIN]:before{content:"FIN";color:var(--w-e-textarea-color);margin-right:4px}[data-w-e-type=EIN] span{text-decoration:underline}[data-w-e-type=EINDATA]{display:inline!important;text-indent:0!important}[data-w-e-type=EINLST][data-same-effect=true]{display:inline-flex!important;flex-wrap:wrap;align-items:center;text-indent:0!important;gap:0}[data-w-e-type=EINLST][data-same-effect=true]:before{content:"FIN:";color:var(--w-e-textarea-color);font-weight:400;margin-right:4px;white-space:nowrap}[data-w-e-type=EINLST][data-same-effect=true] [data-w-e-type=EINDATA]{display:inline!important;text-indent:0!important}[data-w-e-type=EINLST][data-same-effect=true] [data-w-e-type=EFFECT],[data-w-e-type=EINLST][data-same-effect=true] [data-w-e-type=CONEFFECT]{display:none!important}[data-w-e-type=EINLST][data-same-effect=true] [data-w-e-type=EIN]{display:inline!important;margin:0 4px!important}[data-w-e-type=EINLST][data-same-effect=true] [data-w-e-type=EIN]:before{content:""!important}[data-w-e-type=EINLST][data-same-effect=true] [data-w-e-type=EINMFR]{display:inline!important;margin:0 4px!important;text-indent:0!important}[data-w-e-type=EINLST][data-same-effect=true] [data-w-e-type=EINMFR] [data-w-e-type=MFR]{display:none!important}[data-w-e-type=EINLST][data-same-effect=false]{display:block!important;text-indent:0!important}[data-w-e-type=EINLST][data-same-effect=false]:before{content:"FIN:"!important;display:block!important;color:var(--w-e-textarea-color);font-weight:400}[data-w-e-type=EINLST][data-same-effect=false] [data-w-e-type=EINDATA]{display:block!important;text-indent:0!important;padding:1px 0}[data-w-e-type=EINLST][data-same-effect=false] [data-w-e-type=EFFECT],[data-w-e-type=EINLST][data-same-effect=false] [data-w-e-type=CONEFFECT]{display:block!important;font-style:italic;font-weight:700;color:red;padding:0!important;text-indent:0!important}[data-w-e-type=EINLST][data-same-effect=false] [data-w-e-type=EFFECT] [data-slate-leaf=true],[data-w-e-type=EINLST][data-same-effect=false] [data-w-e-type=CONEFFECT] [data-slate-leaf=true]{position:absolute!important;width:1px!important;height:1px!important;opacity:0!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;pointer-events:none!important;-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}[data-w-e-type=EINLST][data-same-effect=false] [data-w-e-type=EIN]{display:inline!important;margin:0 4px!important}[data-w-e-type=EINLST][data-same-effect=false] [data-w-e-type=EIN]:before{content:""!important}[data-w-e-type=EINLST][data-same-effect=false] [data-w-e-type=EINMFR]{display:inline!important;margin:0 4px!important;text-indent:0!important}[data-w-e-type=EINLST][data-same-effect=false] [data-w-e-type=EINMFR] [data-w-e-type=MFR]{display:none!important}[data-w-e-type=EINLST][data-sbeff-mode=true]{display:block!important;text-indent:0!important}[data-w-e-type=EINLST][data-sbeff-mode=true]:before{content:"FIN:"!important;display:block!important;color:var(--w-e-textarea-color);font-weight:400;margin-right:0;white-space:normal}[data-w-e-type=EINLST][data-sbeff-mode=true] [data-w-e-type=EINDATA]{display:inline!important;text-indent:0!important}[data-w-e-type=EINLST][data-sbeff-mode=true] [data-w-e-type=EFFECT],[data-w-e-type=EINLST][data-sbeff-mode=true] [data-w-e-type=CONEFFECT]{display:none!important}[data-w-e-type=EINLST][data-sbeff-mode=true] [data-w-e-type=EIN]{display:inline!important;margin:0 4px!important}[data-w-e-type=EINLST][data-sbeff-mode=true] [data-w-e-type=EIN]:before{content:""!important}[data-w-e-type=EINLST][data-sbeff-mode=true] [data-w-e-type=EINMFR]{display:inline!important;margin:0 4px!important;text-indent:0!important}[data-w-e-type=EINLST][data-sbeff-mode=true] [data-w-e-type=EINMFR] [data-w-e-type=MFR]{display:none!important}[data-w-e-type=REFBLOCK]{display:inline!important;text-indent:0!important;cursor:pointer;text-decoration:none;color:#00f}[data-w-e-type=REFBLOCK][IS-DOUBL=Y]{text-decoration:underline}[data-w-e-type=PARAC] [data-w-e-type=REFBLOCK]:not(:has([data-w-e-type=REFINT],[data-w-e-type=REFEXT],[data-w-e-type=GRPHCREF])):before{content:"(参考.AMM TASK ";color:#00f;text-decoration:none}[data-w-e-type=PARA] [data-w-e-type=REFBLOCK]:not(:has([data-w-e-type=REFINT],[data-w-e-type=REFEXT],[data-w-e-type=GRPHCREF])):before{content:"(Ref.AMM TASK ";color:#00f;text-decoration:none}[data-w-e-type=REFBLOCK]:not(:has([data-w-e-type=REFINT],[data-w-e-type=REFEXT],[data-w-e-type=GRPHCREF])):after{content:")";color:#00f;text-decoration:none}[data-w-e-type=REFBLOCK]:has([data-w-e-type=REFINT],[data-w-e-type=REFEXT],[data-w-e-type=GRPHCREF]):before{content:" "}[data-w-e-type=REFBLOCK]:has([data-w-e-type=REFINT],[data-w-e-type=REFEXT],[data-w-e-type=GRPHCREF]):after{content:" "}[data-w-e-type=REFBLOCK]:before,[data-w-e-type=REFBLOCK]:after{font-size:13px!important}[data-w-e-type=REFEXT]{display:inline!important;text-indent:0!important;text-decoration:none;cursor:pointer;color:#00f}[data-w-e-type=REFEXT] span{text-decoration:underline;font-size:13px!important}[data-w-e-type=REFEXT]:before{content:"("}[data-w-e-type=REFEXT]:after{content:") ";color:#00f}[data-w-e-type=PARAC] [data-w-e-type=REFEXT]:after{content:")"}[data-w-e-type=REFEXT]:before,[data-w-e-type=REFEXT]:after{font-size:13px!important}[data-w-e-type=REFBLOCK] [data-w-e-type=REFEXT]:before,[data-w-e-type=REFBLOCK] [data-w-e-type=REFEXT]:after{content:""!important;text-decoration:none}[data-w-e-type=REFINT]{display:inline!important;text-indent:0!important;cursor:pointer;color:#00f;font-size:13px!important}[data-w-e-type=REFINT] span{text-decoration:underline}[data-w-e-type=REFINT]:before{content:" (Ref.AMM TASK "}[data-w-e-type=PARAC] [data-w-e-type=REFINT]:before{content:"(参考.AMM TASK "}[data-w-e-type=REFINT]:after{content:") "}[data-w-e-type=PARAC] [data-w-e-type=REFINT]:after{content:")"}[data-w-e-type=REFINT]:before,[data-w-e-type=REFINT]:after{font-size:13px!important}[data-w-e-type=EQUNAME]{display:inline!important;text-indent:0!important}[data-w-e-type=EQUNAME]:before{content:"("}[data-w-e-type=EQUNAME]:after{content:")"}[data-w-e-type=EXTERNAL-LINKS]{display:block!important;margin:10px 0!important}[data-w-e-type=EXTERNAL-LINKS]:before{content:"参考文件:";display:block;margin:5px 0 5px 10px;font-weight:700}[data-w-e-type=EXTERNAL-LINK]{display:block!important;margin:5px 0 5px 10px!important;cursor:pointer}[data-w-e-type=EXTERNAL-LINK][FILELINK]:not([FILELINK=""]){color:#00f;text-decoration:underline}[data-w-e-type=GRPHCREF]{font-weight:700;cursor:pointer;display:inline-block!important;width:100%;color:#00f;font-size:13px!important;text-indent:0!important;word-break:break-all;white-space:normal}[data-w-e-type=GRPHCREF]>*:not([data-w-e-type=EFFECT]){display:inline!important;text-decoration:underline}[data-w-e-type=REFBLOCK] [data-w-e-type=GRPHCREF]{display:inline!important;width:auto!important}[data-w-e-type=GRPHCREF]:before{content:"Ref. Fig. ";text-decoration:underline}[data-w-e-type=GRPHCREF]:after{content:"";display:none!important}[data-w-e-type=GRPHCREF]:has(>[data-w-e-type=EFFECT]){display:block!important}[data-w-e-type=GRPHCREF]:has(>[data-w-e-type=EFFECT]):before{content:""!important;display:none!important}[data-w-e-type=GRPHCREF]>[data-w-e-type=EFFECT]+*{display:inline!important}[data-w-e-type=GRPHCREF]>[data-w-e-type=EFFECT]+*:before{content:"Ref. Fig. ";text-decoration:underline!important}[data-w-e-type=GRPHCREF]>[data-w-e-type=EFFECT]{display:block!important}[data-w-e-type=PARAC] [data-w-e-type=GRPHCREF]:before{content:"参考图 ";text-decoration:underline}[data-w-e-type=PARAC] [data-w-e-type=GRPHCREF]>[data-w-e-type=EFFECT]+*:before{content:"参考图 ";text-decoration:underline!important}[data-w-e-type=GRPHCREF]:before,[data-w-e-type=GRPHCREF]:after{font-size:13px!important;text-indent:0!important}[data-w-e-type=REFBLOCK] [data-w-e-type=GRPHCREF]:before,[data-w-e-type=REFBLOCK] [data-w-e-type=GRPHCREF]:after,[data-w-e-type=REFBLOCK] [data-w-e-type=GRPHCREF]>span:before{content:""!important;text-decoration:none}[data-w-e-type=PAN]{display:inline!important;text-indent:0!important}[data-w-e-type=PAN]:before{content:" "}[data-w-e-type=PAN]:after{content:" "}[data-w-e-type=REFBLOCK] [data-w-e-type=REFINT]>[data-w-e-type=EFFECT],[data-w-e-type=REFBLOCK] [data-w-e-type=REFEXT]>[data-w-e-type=EFFECT],[data-w-e-type=REFBLOCK] [data-w-e-type=GRPHCREF]>[data-w-e-type=EFFECT],[data-w-e-type=REFINT]>[data-w-e-type=EFFECT]{display:none!important}[data-w-e-type=ENTRY] [data-w-e-type=CON] [data-w-e-type=CONNBR]:before,[data-w-e-type=ENTRY] [data-w-e-type=CON] [data-w-e-type=CONNBR]:after{content:""!important}[data-w-e-type=ENTRY] [data-w-e-type=REFEXT]:before,[data-w-e-type=ENTRY] [data-w-e-type=REFEXT]:after{content:""!important}[data-w-e-type=ENTRY] [data-w-e-type=REFEXT]>[data-w-e-type=EFFECT]{display:none!important}[data-w-e-type=ENTRY] [data-w-e-type=REFINT]:before{content:"Ref. "!important;text-decoration:underline}[data-w-e-type=ENTRY] [data-w-e-type=REFINT]:after{content:""!important}[data-w-e-type=ENTRY] [data-w-e-type=GRPHCREF]:has([data-w-e-type=EFFECT]){display:flex!important;flex-direction:column;width:100%;text-decoration:none!important}[data-w-e-type=ENTRY] [data-w-e-type=GRPHCREF]:has([data-w-e-type=EFFECT])>[data-w-e-type=EFFECT]{text-align:left;display:inline!important;white-space:nowrap;text-decoration:none!important}[data-w-e-type=ENTRY] [data-w-e-type=TOOLNBR]:before{content:" "!important}[data-w-e-type=ENTRY] [data-w-e-type=TOOLNBR]:after{content:""!important}[data-w-e-type=NOTE]{padding-left:80px!important;color:#00f}[data-w-e-type=NOTE]:before{content:"注意 NOTE:";text-decoration:underline;font-weight:700;position:absolute;left:0}[data-w-e-type=WARNING]{color:red;padding-left:120px!important;font-weight:700;text-transform:uppercase}[data-w-e-type=WARNING]:before{content:"警告 WARNING:";font-weight:700;text-decoration:underline;position:absolute;left:0}[data-w-e-type=CAUTION]{color:#ff6a00;font-weight:700;padding-left:140px!important}[data-w-e-type=CAUTION]:before{content:"警戒 CAUTION:";font-weight:700;text-decoration:underline;position:absolute;left:0}[data-w-e-type=HTML-TABLE] [MERGED=TRUE] [data-w-e-type=ROW],[data-w-e-type=HTML-TABLE] [MERGED=TRUE] [data-w-e-type=ENTRY]{background-color:#ff0!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=TITLEC],[data-w-e-type=HTML-TABLE] [data-w-e-type=TITLE]{text-indent:0!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ROW]{display:contents!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ROW]>span{grid-column:1 / 2;grid-row:1 / 2;height:0!important;width:0!important;overflow:hidden!important;margin:0!important;padding:0!important;border:none!important;opacity:0!important;pointer-events:none!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ROW] [data-w-e-type=NOTE]{text-indent:0!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ROW]>[data-w-e-type=EFFECT],[data-w-e-type=HTML-TABLE] [data-w-e-type=ROW]>[data-w-e-type=CONEFFECT]{grid-column:1 / -1!important;display:block!important;text-indent:0!important;width:100%;border-bottom:1px solid black;font-style:italic;font-weight:700;padding:.5mm}[data-w-e-type=HTML-TABLE] [data-w-e-type=TGROUP]{border:1px solid black;border-collapse:collapse}[data-w-e-type=HTML-TABLE] [data-w-e-type=TGROUP] [data-w-e-type=PARA],[data-w-e-type=HTML-TABLE] [data-w-e-type=TGROUP] [data-w-e-type=PARAC],[data-w-e-type=HTML-TABLE] [data-w-e-type=TGROUP] [data-w-e-type=ZONE]{text-indent:0!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=HTML-THEAD]{display:grid!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=HTML-THEAD] [data-w-e-type=ENTRY]{padding:0 3px;background-color:gray;font-weight:700}[data-w-e-type=HTML-TABLE] [data-w-e-type=HTML-TBODY]{display:grid!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=HTML-TBODY] [data-w-e-type=ENTRY]{padding:6px 3px;min-height:33.8px}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY]{border-right:1px solid black;border-bottom:1px solid black;background-color:var(--table-color-striped);display:flex!important;flex-direction:column!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY][DISABLED=TRUE]{background-color:#fff!important;background-image:linear-gradient(45deg,#cccccc 15%,transparent 15%,transparent 50%,#cccccc 50%,#cccccc 65%,transparent 65%,transparent)!important;background-size:12px 12px!important}body.env-bx [data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY][DISABLED=TRUE]{background-color:#6a7280!important;background-image:none!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY][ALIGN=CENTER]{text-align:center!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY][ALIGN=RIGHT]{text-align:right!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY][ALIGN=LEFT]{text-align:left!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY][ALIGN=JUSTIFY]{text-align:justify!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY][ALIGN=CHAR]{text-align:left!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY]:not([ALIGN])[data-default-align=CENTER]{text-align:center!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY]:not([ALIGN])[data-default-align=RIGHT]{text-align:right!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY]:not([ALIGN])[data-default-align=LEFT]{text-align:left!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY]:not([ALIGN])[data-default-align=JUSTIFY]{text-align:justify!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY]:not([ALIGN])[data-default-align=CHAR]{text-align:left!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY][VALIGN=TOP]{justify-content:flex-start!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY][VALIGN=MIDDLE]{justify-content:center!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY][VALIGN=BOTTOM]{justify-content:flex-end!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY] [data-w-e-type=PARA],[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY] [data-w-e-type=PARAC]{width:100%;flex-shrink:0}[data-w-e-type=GRAPHIC]{display:block!important;margin:10px 0}[data-w-e-type=GRAPHIC]:has(>[data-w-e-type=SHEET]>[data-w-e-type=EFFECT])>[data-w-e-type=EFFECT],[data-w-e-type=GRAPHIC]:has(>[data-w-e-type=SHEET]>[data-w-e-type=CONEFFECT])>[data-w-e-type=CONEFFECT]{display:none!important}[data-w-e-type=GRAPHIC]:has(>[data-w-e-type=SHEET]>[data-w-e-type=TITLE])>[data-w-e-type=TITLE],[data-w-e-type=GRAPHIC]:has(>[data-w-e-type=SHEET]>[data-w-e-type=TITLEC])>[data-w-e-type=TITLEC],[data-w-e-type=GRAPHIC]:has(>[data-w-e-type=SHEET]>[data-w-e-type=HTML-TITLE])>[data-w-e-type=HTML-TITLE]{display:none!important}[data-w-e-type=GRAPHIC] [data-w-e-type=EFFECT],[data-w-e-type=GRAPHIC] [data-w-e-type=CONEFFECT],[data-w-e-type=GRAPHIC] [data-w-e-type=TITLE],[data-w-e-type=GRAPHIC] [data-w-e-type=TITLEC],[data-w-e-type=GRAPHIC] [data-w-e-type=HTML-TITLE]{text-align:center}[data-w-e-type=SHEET]{display:flex!important;flex-direction:column;margin-bottom:20px}[data-w-e-type=SHEET]>[data-sheet-img-container]{order:0}[data-w-e-type=SHEET]>[data-slate-placeholder]{order:1}[data-w-e-type=SHEET]>[data-w-e-type=TITLE][data-sheet-caption=true],[data-w-e-type=SHEET]>[data-w-e-type=TITLEC][data-sheet-caption=true],[data-w-e-type=SHEET]>[data-w-e-type=HTML-TITLE][data-sheet-caption=true]{order:2;display:block!important;margin-top:6px}[data-w-e-type=SHEET]>[data-w-e-type=TITLE][data-sheet-caption=true]:before,[data-w-e-type=SHEET]>[data-w-e-type=TITLEC][data-sheet-caption=true]:before,[data-w-e-type=SHEET]>[data-w-e-type=HTML-TITLE][data-sheet-caption=true]:before{content:attr(data-caption-prefix);display:inline}[data-w-e-type=SHEET]>[data-w-e-type=EFFECT][data-sheet-effect=true],[data-w-e-type=SHEET]>[data-w-e-type=CONEFFECT][data-sheet-effect=true]{order:3;display:block!important}[data-w-e-type=SHEET]>[data-w-e-type=EFFECT][data-sheet-effect=true]>*,[data-w-e-type=SHEET]>[data-w-e-type=CONEFFECT][data-sheet-effect=true]>*{display:none!important}[data-w-e-type=SHEET]>[data-w-e-type=EFFECT][data-sheet-effect=true]:before,[data-w-e-type=SHEET]>[data-w-e-type=CONEFFECT][data-sheet-effect=true]:before{content:"** ON A/C FSN " attr(data-effno);display:block}[data-w-e-type=GDESC]{display:block!important;margin:5px 20px;text-align:left}[data-w-e-type=FTNOTE]{display:block!important;font-size:.9em;margin-top:5px;text-align:left}[data-w-e-type=EFFECT] [data-slate-leaf=true]{position:absolute!important;top:0!important;left:0!important;width:1px!important;height:1px!important;opacity:0!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;pointer-events:none!important;-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}[data-w-e-type=SUPERSCRIPT],[data-w-e-type=SUPER]{font-size:8pt;vertical-align:super;display:inline!important}[data-w-e-type=SUBSCRIPT],[data-w-e-type=SUB]{font-size:8pt;vertical-align:sub;display:inline!important}[data-w-e-type=SBEFFC],[data-w-e-type=SBEFF]{font-style:italic;font-weight:700;padding:.5mm}[data-w-e-type=REFINT] [data-w-e-type=SBEFFC],[data-w-e-type=REFINT] [data-w-e-type=SBEFF]{display:inline!important}[data-w-e-type=SBEFFC]:not([data-w-e-type=REFINT] *),[data-w-e-type=SBEFF]:not([data-w-e-type=REFINT] *){display:block!important}[data-w-e-type=SIGNOFF]{scroll-margin-left:0!important;scroll-margin-right:0!important;scroll-margin-bottom:0!important;text-align:right!important;-webkit-user-select:none;-moz-user-select:none;user-select:none}[data-w-e-type=SIGNOFF] span:before{display:inline-block;width:100%}[data-w-e-type=SIGNOFF] [data-slate-placeholder]{scroll-margin-left:0!important;scroll-margin-right:0!important;scroll-margin-bottom:0!important}.env-dev [data-w-e-type=SIGNOFF]:not([CK-LEVEL]) p:first-child span:first-child:before,.env-dev [data-w-e-type=SIGNOFF][CK-LEVEL=""] p:first-child span:first-child:before,.env-dev [data-w-e-type=SIGNOFF][CK-LEVEL=A] p:first-child span:first-child:before{content:"工作者\aMechanic";white-space:pre-line}.env-dev [data-w-e-type=SIGNOFF][CK-LEVEL=B] p:nth-child(1) span:first-child:before{content:"工作者\aMechanic";white-space:pre-line}.env-dev [data-w-e-type=SIGNOFF][CK-LEVEL=B] p:nth-child(2) span:first-child:before{content:"检查者\aInspector";white-space:pre-line}.env-dev [data-w-e-type=SIGNOFF][CK-LEVEL=C] p:nth-child(1) span:first-child:before{content:"工作者\aMechanic";white-space:pre-line}.env-dev [data-w-e-type=SIGNOFF][CK-LEVEL=C] p:nth-child(2) span:first-child:before{content:"必检\aInspected";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF] p{width:200px}.env-bx [data-w-e-type=SIGNOFF]:not([CK-LEVEL]) p:first-child span:first-child:before,.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=""] p:first-child span:first-child:before,.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=A] p:first-child span:first-child:before{content:"工作者\aMechanic";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=B] p:nth-child(1) span:first-child:before{content:"工作者\aMechanic";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=B] p:nth-child(2) span:first-child:before{content:"检验员\aInspector";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=C] p:nth-child(1) span:first-child:before{content:"工作者\aMechanic";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=C] p:nth-child(2) span:first-child:before{content:"必检\aInspected";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=S] p:first-child span:first-child:before{content:"权限许可\aPermission";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=S] p:first-child span:nth-child(2):before{content:"S";line-height:40px}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=S] p:nth-child(2) span:first-child:before{content:"工作者\aMechanic";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=S] p:nth-child(3) span:first-child:before{content:"检验员\aInspector";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=M] p:first-child span:first-child:before{content:"权限许可\aPermission";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=M] p:first-child span:nth-child(2):before{content:"M";line-height:40px}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=M] p:nth-child(2) span:first-child:before{content:"工作者\aMechanic";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=M] p:nth-child(3) span:first-child:before{content:"检验员\aInspector";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL="M(N/A)"] p:first-child span:first-child:before{content:"权限许可\aPermission";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL="M(N/A)"] p:first-child span:nth-child(2):before{content:"M";line-height:40px}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL="M(N/A)"] p:nth-child(2) span:first-child:before{content:"工作者\aMechanic";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL="M(N/A)"] p:nth-child(3) span:first-child:before{content:"检验员\aInspector";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL="M(N/A)"] p:nth-child(3) span:nth-child(2):before{content:"N/A";line-height:40px}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=T] p:first-child span:first-child:before{content:"权限许可\aPermission";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=T] p:first-child span:nth-child(2):before{content:"T";line-height:40px}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=T] p:nth-child(2) span:first-child:before{content:"工作者\aMechanic";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=T] p:nth-child(3) span:first-child:before{content:"检验员\aInspector";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=R] p:first-child span:first-child:before{content:"权限许可\aPermission";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=R] p:first-child span:nth-child(2):before{content:"R";line-height:40px}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=R] p:nth-child(2) span:first-child:before{content:"工作者\aMechanic";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=R] p:nth-child(3) span:first-child:before{content:"检验员\aInspector";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=G] p:first-child span:first-child:before{content:"权限许可\aPermission";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=G] p:first-child span:nth-child(2):before{content:"G";line-height:40px}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=G] p:nth-child(2) span:first-child:before{content:"工作者\aMechanic";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=G] p:nth-child(3) span:first-child:before{content:"检验员\aInspector";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=SP] p:first-child span:first-child:before{content:"权限许可\aPermission";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=SP] p:first-child span:nth-child(2):before{content:"SP";line-height:40px}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=SP] p:nth-child(2) span:first-child:before{content:"工作者\aMechanic";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=SP] p:nth-child(3) span:first-child:before{content:"SP检验员\aSP Inspector";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=SPAM] p:first-child span:first-child:before{content:"权限许可\aPermission";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=SPAM] p:first-child span:nth-child(2):before{content:"SP";line-height:40px}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=SPAM] p:nth-child(2) span:first-child:before{content:"工作者\aMechanic";white-space:pre-line}.env-bx [data-w-e-type=SIGNOFF][CK-LEVEL=SPAM] p:nth-child(3) span:first-child:before{content:"航材检验员\a AM Inspector";white-space:pre-line}.env-xm [data-w-e-type=SIGNOFF]:not([CK-LEVEL]) p:first-child span:first-child:before,.env-xm [data-w-e-type=SIGNOFF][CK-LEVEL=""] p:first-child span:first-child:before,.env-xm [data-w-e-type=SIGNOFF][CK-LEVEL=A] p:first-child span:first-child:before{content:"工作者\aPerf.By :";white-space:pre-line}.env-xm [data-w-e-type=SIGNOFF][CK-LEVEL=B] p:nth-child(1) span:first-child:before{content:"工作者\aPerf.By :";white-space:pre-line}.env-xm [data-w-e-type=SIGNOFF][CK-LEVEL=B] p:nth-child(2) span:first-child:before{content:"互检者\aRCI.BY :";white-space:pre-line}.env-xm [data-w-e-type=SIGNOFF][CK-LEVEL=C] p:nth-child(1) span:first-child:before{content:"工作者\aPerf.By :";white-space:pre-line}.env-xm [data-w-e-type=SIGNOFF][CK-LEVEL=C] p:nth-child(2) span:first-child:before{content:"检查者\aRll.By :";white-space:pre-line}[data-w-e-type=SIGNOFF] p{width:170px;height:42px;display:inline-block!important;border:1px solid black;font-size:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;cursor:default}[data-w-e-type=SIGNOFF] p:nth-child(2n){border-left:none}[data-w-e-type=SIGNOFF] span{width:50%;height:100%;float:left;text-align:center;font-weight:700;border-right:1px solid black;border-bottom:1px solid black;font-size:14px;vertical-align:top;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none}[data-w-e-type=SIGNOFF] span text{display:block!important}[data-w-e-type=SIGNOFF] span:last-child,[data-w-e-type=SIGNOFF] span:nth-last-child(2){border-bottom:none}[data-w-e-type=SIGNOFF] [data-slate-placeholder]{display:none!important}:not([data-w-e-type=SUBTASK])[chapnbr]:before{content:"任务 " attr(chapnbr) "-" attr(sectnbr) "-" attr(subjnbr) "-" attr(func) "-" attr(seq) "-" attr(confnbr) "-" attr(confltr)}[data-w-e-type=CEP][chapnbr]:before{content:"任务 " attr(chapnbr) "-" attr(sectnbr) "-" attr(subjnbr) "-" attr(func) "-" attr(seq) "-" attr(confltr)}[data-w-e-type=SUBTASK]:before{content:"子任务 " attr(chapnbr) "-" attr(sectnbr) "-" attr(subjnbr) "-" attr(func) "-" attr(seq) "-" attr(confltr)}[data-w-e-type=EFFECT]{scroll-margin-left:0!important;scroll-margin-right:0!important;scroll-margin-bottom:0!important;font-style:italic;font-weight:700;padding:.5mm;color:red}[data-w-e-type=EFFECT][EFFTEXT]:before{content:"** ON A/C: " attr(efftext)}[data-w-e-type=EFFECT]:not([EFFTEXT])[EFFRG="001999"]:before,[data-w-e-type=EFFECT]:not([EFFTEXT])[EFFRG="001-999"]:before{content:"** ON A/C: ALL"}[data-w-e-type=EFFECT]:not([EFFTEXT]):not([EFFRG="001999"]):not([EFFRG="001-999"]):before{content:"** ON A/C: " attr(effrg)}[data-w-e-type=CONEFFECT]{font-style:italic;font-weight:700;padding:.5mm;color:red}[data-w-e-type=CONEFFECT]:before{content:"** CONF: " attr(effrg)}[data-w-e-type=SBEFF]{text-indent:0!important;font-style:italic;font-weight:700;padding:.5mm}[data-w-e-type=SBEFF][EFFRG="001999"]:before,[data-w-e-type=SBEFF][EFFRG="001-999"]:before{content:attr(sbcond) " SB " attr(sbnbr) " for A/C ALL";color:red}[data-w-e-type=SBEFF]:not([EFFRG="001999"]):not([EFFRG="001-999"]):before{content:attr(sbcond) " SB " attr(sbnbr) " for A/C " attr(effrg);color:red}[data-w-e-type=SBEFFC]{text-indent:0!important;font-style:italic;font-weight:700;padding:.5mm}[data-w-e-type=SBEFFC][EFFRG="001999"]:before,[data-w-e-type=SBEFFC][EFFRG="001-999"]:before{content:attr(sbcond) " SB " attr(sbnbr) " for A/C ALL";color:red}[data-w-e-type=SBEFFC]:not([EFFRG="001999"]):not([EFFRG="001-999"]):before{content:attr(sbcond) " SB " attr(sbnbr) " for A/C " attr(effrg);color:red}[data-w-e-type=SUB],[data-w-e-type=SUBSCRIPT]{display:inline!important;text-indent:0!important;font-size:.75em;vertical-align:sub}[data-w-e-type=SUPER],[data-w-e-type=SUPERSCRIPT]{display:inline!important;text-indent:0!important;font-size:.75em;vertical-align:super}[data-w-e-type=RECORD-LINE]{display:block!important}[data-w-e-type=RECORD-LINE]>*{display:inline!important}[data-w-e-type=RECORD-LINE] [data-w-e-type=PARA],[data-w-e-type=RECORD-LINE] [data-w-e-type=PARAC]{display:inline!important;width:auto!important}[data-w-e-type=RECORD-LINE] [data-w-e-type=RECORD]{display:inline-block!important;border-bottom:1px solid black;min-width:50px;text-align:center}[data-w-e-type=SELECTION]{display:inline-block!important}[data-w-e-type=SELECTION]:after{content:"*";color:red}[data-w-e-type=SELECTION][required=NO]:after{content:""!important;color:red}[data-w-e-type=SELECTION-ITEM]{margin-right:50px!important;display:inline-flex!important;align-items:center}[data-w-e-type=SELECTION-ITEM] [data-w-e-type=SELECTION-LBL-CN],[data-w-e-type=SELECTION-ITEM] [data-w-e-type=SELECTION-LBL-EN]{text-indent:0!important}[data-w-e-type=SELECTION-ITEM]:after{content:"□";font-size:18px;position:absolute;top:-6px;right:-15px}[data-w-e-type=SELECTION][multi=NO] [data-w-e-type=SELECTION-ITEM]:after{content:"○"}[data-w-e-type=TXTGRPHC]{display:block!important;font-family:Courier New,Courier,monospace!important;padding:6px 24px!important;margin:6px auto!important;line-height:1.6;text-indent:0!important;box-sizing:border-box}[data-w-e-type=TXTLINE]{display:block!important;white-space:pre-wrap!important;text-indent:0!important;font-family:SimSun,宋体,serif!important}[data-w-e-type=TXTLINE]:empty:before{content:" ";white-space:pre}[data-w-e-type=TOOLNBR]{text-indent:0!important;display:inline!important;color:#00f}[data-w-e-type=TOOLNBR] span{text-decoration:underline}[data-w-e-type=TOOLNAME]{text-indent:0!important;display:inline!important}[data-w-e-type=HNANOTE]{color:#00f}[data-w-e-type=ZONE]{text-decoration:underline;color:#00f}[data-w-e-type=COLSPEC],[data-w-e-type=SPANSPEC]{display:none!important}[data-w-e-type=EXPD]{display:inline-block!important;text-indent:0!important}[data-w-e-type=EXPD] [data-w-e-type=CSN]{text-indent:0!important;display:inline-block!important}[data-w-e-type=EXPD] [data-w-e-type=CSN][data-formatted=true],[data-w-e-type=EXPD] [data-w-e-type=CSN][data-formatted=true] *{font-size:0!important}[data-w-e-type=EXPD] [data-w-e-type=CSN][data-formatted=true]:before{content:attr(data-content)!important;font-size:13px!important;color:inherit}[data-w-e-type=EXPD] [data-w-e-type=CSN][data-formatted=true]:after{content:" "!important;font-size:13px!important}[data-w-e-type=EXPD] [data-w-e-type=CSN]:not([data-formatted=true]):before{content:"IPC-CSN ("}[data-w-e-type=EXPD] [data-w-e-type=CSN]:not([data-formatted=true]):after{content:") "}[data-w-e-type=EXPD] [data-w-e-type=EXPDNAME],[data-w-e-type=EXPD] [data-w-e-type=ITEMNBR]{text-indent:0!important;display:inline!important}[data-w-e-type=ENTRY] [data-w-e-type=EXPD] [data-w-e-type=CSN]:not([data-formatted=true]):before{content:""}[data-w-e-type=ENTRY] [data-w-e-type=EXPD] [data-w-e-type=CSN]:not([data-formatted=true]):after{content:" "}[data-w-e-type=HTML-PLACEHOLDER]{display:inline!important;text-indent:0!important}.column-resize-handle{position:absolute;top:0;width:8px;height:100%;cursor:col-resize;z-index:100;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:0;transition:opacity .2s}[type=HTML-TABLE]:hover .column-resize-handle{opacity:1}.column-resize-handle.resizing{opacity:1!important}.column-resize-indicator{position:absolute;right:3px;top:0;width:2px;height:100%;background-color:#d9d9d9;transition:all .2s;pointer-events:none}.column-resize-handle:hover .column-resize-indicator{background-color:#1890ff;width:3px}.column-resize-handle.resizing .column-resize-indicator{background-color:#1890ff!important;width:3px!important}body.resizing-column,body.resizing-column *{cursor:col-resize!important;-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}:root{--paragraph-indent: 2em;--no-indent: 0}[data-w-e-type=PARA],[data-w-e-type=PARAC]{text-indent:var(--paragraph-indent)}[data-w-e-type=PARA][data-no-indent],[data-w-e-type=PARAC][data-no-indent],[data-w-e-type=PARA]:first-child,[data-w-e-type=PARAC]:first-child,[data-w-e-type=HTML-TABLE] [data-w-e-type=PARA],[data-w-e-type=HTML-TABLE] [data-w-e-type=PARAC],[data-w-e-type=L1ITEM] [data-w-e-type=PARA],[data-w-e-type=L1ITEM] [data-w-e-type=PARAC],[data-w-e-type=L2ITEM] [data-w-e-type=PARA],[data-w-e-type=L2ITEM] [data-w-e-type=PARAC],[data-w-e-type=L3ITEM] [data-w-e-type=PARA],[data-w-e-type=L3ITEM] [data-w-e-type=PARAC],[data-w-e-type=L4ITEM] [data-w-e-type=PARA],[data-w-e-type=L4ITEM] [data-w-e-type=PARAC],[data-w-e-type=L5ITEM] [data-w-e-type=PARA],[data-w-e-type=L5ITEM] [data-w-e-type=PARAC],[data-w-e-type=WARNING] [data-w-e-type=PARA],[data-w-e-type=WARNING] [data-w-e-type=PARAC],[data-w-e-type=CAUTION] [data-w-e-type=PARA],[data-w-e-type=CAUTION] [data-w-e-type=PARAC],[data-w-e-type=SPECNOTE] [data-w-e-type=PARA],[data-w-e-type=SPECNOTE] [data-w-e-type=PARAC],[data-w-e-type=NOTE] [data-w-e-type=PARA],[data-w-e-type=NOTE] [data-w-e-type=PARAC],[data-w-e-type=PRETOPIC] [data-w-e-type=PARA],[data-w-e-type=PRETOPIC] [data-w-e-type=PARAC],[data-w-e-type=STEP] [data-w-e-type=PARA],[data-w-e-type=STEP] [data-w-e-type=PARAC],[data-w-e-type=TITLE],[data-w-e-type=TITLEC],[data-w-e-type=HTML-TITLE],[data-w-e-type=REFINT],[data-w-e-type=REFEXT],[data-w-e-type=GRPHCREF],[data-w-e-type=STDNAME],[data-w-e-type=CON],[data-w-e-type=CONNBR],[data-w-e-type=CONNAME],[data-w-e-type=TED],[data-w-e-type=TOOLNBR],[data-w-e-type=TOOLNAME],[data-w-e-type=EXPD],[data-w-e-type=HTML-PLACEHOLDER],[data-w-e-type=SBEFF],[data-w-e-type=SELECTION-LBL-CN],[data-w-e-type=SELECTION-LBL-EN]{text-indent:var(--no-indent)!important}[data-w-e-type=PARA]>*,[data-w-e-type=PARAC]>*,[data-w-e-type=TITLE]>*,[data-w-e-type=TITLEC]>*,[data-w-e-type=HTML-TITLE]>*{text-indent:var(--no-indent)!important}[data-w-e-type=CON]{display:inline-flex!important;align-items:baseline;flex-wrap:wrap;margin:0 5px!important;gap:0}[data-w-e-type=CONNAME]{order:1;text-indent:0!important;display:inline!important}[data-w-e-type=CONNBR]{order:2;color:#00f;display:inline!important;white-space:nowrap!important}[data-w-e-type=CONNBR] span{text-decoration:underline}[data-w-e-type=CON] [data-w-e-type=CONNBR]:before{content:" (Material Ref. "!important;white-space:nowrap!important}[data-w-e-type=CON] [data-w-e-type=CONNBR]:after{content:")"!important;white-space:nowrap!important}[data-w-e-type=CBLST]{display:block!important;width:100%;margin:10px 0;text-indent:0!important}[data-w-e-type=CBSUBLST]{display:block!important;width:100%;margin-bottom:0;border:1px solid black;border-top:none;border-collapse:collapse;text-indent:0!important;position:relative;padding-top:0}[data-w-e-type=CBSUBLST] *{position:static!important}[data-w-e-type=CBLST]>[data-w-e-type=CBSUBLST]:first-of-type{border-top:1px solid black}[data-w-e-type=CBLST]>[data-w-e-type=CBSUBLST]:first-of-type:before{content:"";display:block;width:100%;height:42px;background-color:gray;border-bottom:2px solid black;box-sizing:border-box;background-image:linear-gradient(to right,#000,#000),linear-gradient(to right,#000,#000),linear-gradient(to right,#000,#000);background-size:1px 42px,1px 42px,1px 42px;background-position:15% 0,50% 0,75% 0;background-repeat:no-repeat}[data-w-e-type=CBLST]>[data-w-e-type=CBSUBLST]:not(:first-of-type):before{display:none!important}[data-w-e-type=CBLST]>[data-w-e-type=CBSUBLST]:first-of-type>[data-w-e-type=CBDATA]:first-of-type>[data-w-e-type=PAN]:before{content:"面板 \aPANEL";position:absolute;top:0;left:0;width:15%;height:42px;display:flex;align-items:center;justify-content:center;font-weight:700;text-align:center;white-space:pre-line;line-height:1.2;font-size:11px;box-sizing:border-box;z-index:10;pointer-events:none}[data-w-e-type=CBLST]>[data-w-e-type=CBSUBLST]:first-of-type>[data-w-e-type=CBDATA]:first-of-type>[data-w-e-type=CBNAME]:before{content:"说明 \a DESIGNATION";position:absolute;top:0;left:15%;width:35%;height:42px;display:flex;align-items:center;justify-content:center;font-weight:700;text-align:center;white-space:pre-line;line-height:1.2;font-size:11px;box-sizing:border-box;z-index:10;pointer-events:none}[data-w-e-type=CBLST]>[data-w-e-type=CBSUBLST]:first-of-type>[data-w-e-type=CBDATA]:first-of-type>[data-w-e-type=CB]:before{content:"功能识别号 \a FIN";position:absolute;top:0;left:50%;width:25%;height:42px;display:flex;align-items:center;justify-content:center;font-weight:700;text-align:center;white-space:pre-line;line-height:1.2;font-size:11px;box-sizing:border-box;z-index:10;pointer-events:none}[data-w-e-type=CBLST]>[data-w-e-type=CBSUBLST]:first-of-type>[data-w-e-type=CBDATA]:first-of-type>[data-w-e-type=CB]:after{content:"位置 \aLOCATION";position:absolute;top:0;left:75%;width:25%;height:42px;display:flex;align-items:center;justify-content:center;font-weight:700;text-align:center;white-space:pre-line;line-height:1.2;font-size:11px;box-sizing:border-box;z-index:10;pointer-events:none}[data-w-e-type=CBSUBLST]>[data-w-e-type=EIN]{display:inline!important;text-indent:0!important;color:#00f}[data-w-e-type=CBSUBLST]>[data-w-e-type=EIN]:before{content:"FOR FIN "}[data-w-e-type=CBSUBLST]>[data-w-e-type=EQUNAME]{display:inline!important;text-indent:0!important}[data-w-e-type=CBSUBLST]>[data-w-e-type=EQUNAME]:before{content:" ("}[data-w-e-type=CBSUBLST]>[data-w-e-type=EQUNAME]:after{content:")"}[data-w-e-type=CBSUBLST]:has(>[data-w-e-type=EIN])>[data-w-e-type=CBDATA]:first-of-type{border-top:1px solid black}[data-w-e-type=CBSUBLST]:has(>[data-w-e-type=EQUNAME])>[data-w-e-type=CBDATA]:first-of-type{border-top:1px solid black}[data-w-e-type=CBSUBLST]>[data-w-e-type=EFFECT],[data-w-e-type=CBSUBLST]>[data-w-e-type=CONEFFECT]{display:block!important;background-color:#ffebee;padding:8px 12px;border-bottom:1px solid black;font-weight:700;color:red;text-indent:0!important}[data-w-e-type=CBSUBLST]>[data-w-e-type=EFFECT][EFFRG="001999"]:before,[data-w-e-type=CBSUBLST]>[data-w-e-type=CONEFFECT][EFFRG="001999"]:before,[data-w-e-type=CBSUBLST]>[data-w-e-type=EFFECT][EFFRG="001-999"]:before,[data-w-e-type=CBSUBLST]>[data-w-e-type=CONEFFECT][EFFRG="001-999"]:before{content:"** ON A/C: ALL";color:red;font-weight:700}[data-w-e-type=CBSUBLST]>[data-w-e-type=EFFECT]:not([EFFRG="001999"]):not([EFFRG="001-999"]):before,[data-w-e-type=CBSUBLST]>[data-w-e-type=CONEFFECT]:not([EFFRG="001999"]):not([EFFRG="001-999"]):before{content:"** ON A/C: " attr(effrg);color:red;font-weight:700}[data-w-e-type=CBSUBLST]>[data-w-e-type=EFFECT] [data-slate-leaf=true],[data-w-e-type=CBSUBLST]>[data-w-e-type=CONEFFECT] [data-slate-leaf=true]{position:absolute!important;top:0!important;left:0!important;width:1px!important;height:1px!important;opacity:0!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;pointer-events:none!important;-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}[data-w-e-type=CBDATA]{display:grid!important;grid-template-columns:15% 35% 25% 25%;grid-template-areas:"pan designation fin location";border-bottom:1px solid black;text-indent:0!important;min-height:40px}[data-w-e-type=CBDATA]:last-child{border-bottom:none}[data-w-e-type=CBDATA]:has(>[data-w-e-type=EFFECT]),[data-w-e-type=CBDATA]:has(>[data-w-e-type=CONEFFECT]){grid-template-areas:"effect effect effect effect" "pan designation fin location"}[data-w-e-type=CBDATA]>[data-w-e-type=EFFECT],[data-w-e-type=CBDATA]>[data-w-e-type=CONEFFECT]{grid-area:effect;display:block!important;padding:6px 12px;border-bottom:1px solid black;font-weight:700;text-indent:0!important}[data-w-e-type=CBDATA]>[data-w-e-type=EFFECT][EFFRG="001999"]:before,[data-w-e-type=CBDATA]>[data-w-e-type=CONEFFECT][EFFRG="001999"]:before,[data-w-e-type=CBDATA]>[data-w-e-type=EFFECT][EFFRG="001-999"]:before,[data-w-e-type=CBDATA]>[data-w-e-type=CONEFFECT][EFFRG="001-999"]:before{content:"** ON A/C: ALL";color:red;font-weight:700}[data-w-e-type=CBDATA]>[data-w-e-type=EFFECT]:not([EFFRG="001999"]):not([EFFRG="001-999"]):before,[data-w-e-type=CBDATA]>[data-w-e-type=CONEFFECT]:not([EFFRG="001999"]):not([EFFRG="001-999"]):before{content:"** ON A/C: " attr(effrg);color:red;font-weight:700}[data-w-e-type=CBDATA]>[data-w-e-type=EFFECT] [data-slate-leaf=true],[data-w-e-type=CBDATA]>[data-w-e-type=CONEFFECT] [data-slate-leaf=true]{position:absolute!important;top:0!important;left:0!important;width:1px!important;height:1px!important;opacity:0!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;pointer-events:none!important;-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}[data-w-e-type=CBDATA]>[data-w-e-type=PAN]{grid-area:pan;padding:8px 12px;border-right:1px solid black;text-align:center;display:flex!important;align-items:center;justify-content:center;text-indent:0!important}[data-w-e-type=CBDATA]>[data-w-e-type=CB]{grid-area:fin;padding:8px 12px;border-right:1px solid black;text-align:center;display:flex!important;align-items:center;justify-content:center;text-indent:0!important}[data-w-e-type=CBDATA]>[data-w-e-type=CBNAME]{grid-area:designation;padding:8px 12px;border-right:1px solid black;display:flex!important;align-items:center;text-indent:0!important}[data-w-e-type=CBDATA]>[data-w-e-type=CBLOC]{grid-area:location;padding:8px 12px;text-align:center;display:flex!important;align-items:center;justify-content:center;text-indent:0!important}.env-xm [data-w-e-type=REFBLOCK]:has([data-w-e-type=REFINT])>[data-w-e-type=HTML-PLACEHOLDER]:first-of-type{display:none!important}[data-w-e-type=TOR]{display:inline!important;text-indent:0!important}[data-w-e-type=TORVALUE]{display:inline!important}[data-w-e-type=TORVALUE]:first-child>[data-torvalue-display][data-is-mdan=true]:before{content:" " attr(data-content);color:#923385}[data-w-e-type=TORVALUE]:not(:first-child)>[data-torvalue-display][data-is-mdan=true]:before{content:" (" attr(data-content) ")";color:#923385}[data-w-e-type=TORVALUE]:first-child>[data-torvalue-display][data-is-mdan=false][data-has-max=true]:before,[data-w-e-type=PARA] [data-w-e-type=TORVALUE]:first-child>[data-torvalue-display][data-is-mdan=false][data-has-max=true]:before{content:" to between " attr(data-content);color:#923385}[data-w-e-type=TORVALUE]:first-child>[data-torvalue-display][data-is-mdan=false][data-has-max=false]:before,[data-w-e-type=PARA] [data-w-e-type=TORVALUE]:first-child>[data-torvalue-display][data-is-mdan=false][data-has-max=false]:before{content:" to " attr(data-content);color:#923385}[data-w-e-type=TORVALUE]:not(:first-child)>[data-torvalue-display][data-is-mdan=false]:before,[data-w-e-type=PARA] [data-w-e-type=TORVALUE]:not(:first-child)>[data-torvalue-display][data-is-mdan=false]:before{content:" (" attr(data-content) ")";color:#923385}[data-w-e-type=PARAC] [data-w-e-type=TORVALUE]:first-child>[data-torvalue-display][data-is-mdan=true]:before{content:" " attr(data-content-parac);color:#923385}[data-w-e-type=PARAC] [data-w-e-type=TORVALUE]:not(:first-child)>[data-torvalue-display][data-is-mdan=true]:before{content:" (" attr(data-content-parac) ")";color:#923385}[data-w-e-type=PARAC] [data-w-e-type=TORVALUE]:first-child>[data-torvalue-display][data-is-mdan=false][data-has-max=true]:before{content:" " attr(data-content-parac);color:#923385}[data-w-e-type=PARAC] [data-w-e-type=TORVALUE]:first-child>[data-torvalue-display][data-is-mdan=false][data-has-max=false]:before{content:" " attr(data-content-parac);color:#923385}[data-w-e-type=PARAC] [data-w-e-type=TORVALUE]:not(:first-child)>[data-torvalue-display][data-is-mdan=false]:before{content:" (" attr(data-content-parac) ")";color:#923385}[data-w-e-type=EFFBLOCK]{text-decoration:underline;display:inline!important}[data-w-e-type=EFFBLOCK]:after{content:" (" attr(EFFDESC) ") ";color:red;text-decoration:none;display:inline-block}[data-w-e-type=IF-HL]{color:red;display:inline!important}[data-w-e-type=IF-BOLD]{font-weight:700;display:inline!important}[data-w-e-type=TOPIC-GROUP]{display:block!important;background-color:#ccc;padding:4px 0;text-align:center;font-weight:700;width:100%}[data-w-e-type=ATANBR],[data-w-e-type=MFR],[data-w-e-type=SBNBR],[data-w-e-type=STDNAME]{display:inline!important;text-indent:0!important}[data-w-e-type=TED]{display:inline!important;text-indent:0!important;margin:0 4px!important}[data-w-e-type=TED]:before{content:" ";white-space:pre}[data-w-e-type=TED] [data-w-e-type=TOOLNAME],[data-w-e-type=TED] [data-w-e-type=TOOLNBR]{display:inline!important;text-indent:0!important}[data-w-e-type=TED] [data-w-e-type=TOOLNBR]:before{content:" ("}[data-w-e-type=TED] [data-w-e-type=TOOLNBR]:after{content:") "}[data-w-e-type=EXPD]{display:inline!important;text-indent:0!important}[data-w-e-type=EXPD] [data-w-e-type=ITEMNBR]:before{content:" ("}[data-w-e-type=EXPD] [data-w-e-type=ITEMNBR]:after{content:")"}[data-page-break=true]{position:relative}[data-page-break=true]:after{content:"━━━━━━━━━━━━━━━━━━━━━━━━ 📄 分页符 ━━━━━━━━━━━━━━━━━━━━━━━━";display:block;text-align:center;color:#06c;font-size:11px;font-weight:500;margin:8px 0;padding:4px 10px;border-top:2px dashed #4d9fff;border-bottom:2px dashed #4d9fff;background:linear-gradient(to bottom,#e6f2ff,#f0f7ff);-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:none;border-radius:2px}@media print{[data-page-break=true]:after{content:none!important;display:none!important;margin:0!important;padding:0!important;border:none!important;background:none!important}}.preview-root [data-page-break=true]:after,.preview-mode [data-page-break=true]:after{content:""!important;display:none!important;height:0;margin:0;padding:0;border:none;background:none}.hover\:border-red-300:hover{--tw-border-opacity: 1;border-color:rgb(252 165 165 / var(--tw-border-opacity))}.hover\:bg-red-100:hover{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity))}.group:hover .group-hover\:opacity-100{opacity:1}
.n-card .modal-content-class{padding-bottom:0;margin-bottom:var(--n-padding-bottom);max-height:75vh;overflow-y:auto}.virtual-tree-container[data-v-cb507100]{position:relative;overflow-y:auto;overflow-x:hidden;padding-bottom:30vh}.virtual-tree-content[data-v-cb507100]{position:relative;width:100%}.virtual-tree-list[data-v-cb507100]{position:absolute;top:0;left:0;right:0;will-change:transform}.tree-node[data-v-cb507100]{-webkit-user-select:none;-moz-user-select:none;user-select:none}.tree-node-content[data-v-cb507100]{display:flex;align-items:center;height:32px;cursor:pointer;border-radius:4px;transition:background-color .2s;position:relative}.tree-node-content[data-v-cb507100]:hover{background-color:#0000000a}.tree-node-selected>.tree-node-content[data-v-cb507100]{background-color:#4098fc33}.tree-node-context-menu>.tree-node-content[data-v-cb507100]{background-color:#0000000a}.tree-node-switcher[data-v-cb507100]{display:flex;align-items:center;justify-content:center;width:24px;height:24px;margin-right:0;cursor:pointer;border-radius:4px;transition:background-color .2s}.tree-node-switcher[data-v-cb507100]:hover{background-color:#0000000f}.tree-switcher-icon[data-v-cb507100]{color:#1890ff}.tree-node-indent[data-v-cb507100]{width:24px;margin-right:0}.tree-node-icon[data-v-cb507100]{display:inline-flex;align-items:center;margin-right:6px;flex-shrink:0;color:#606266}.tree-node[data-page-break=true] .tree-node-label[data-v-cb507100]{color:#1890ff!important;font-weight:600}.tree-node-title[data-v-cb507100]{flex:1;display:flex;align-items:center;min-width:0;font-size:12px;line-height:1.4}.tree-node-label[data-v-cb507100]{color:#000000d9;margin-right:4px;font-weight:500;word-break:keep-all}.tree-node-selected>.tree-node-content .tree-node-label[data-v-cb507100]{color:var(--info-color-suppl)}.tree-node-text[data-v-cb507100]{color:#00000073;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px}.tree-node-selected>.tree-node-content .tree-node-text[data-v-cb507100]{color:var(--info-color-suppl)}.tree-node-special[data-v-cb507100]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px}.empty-state[data-v-cb507100]{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:flex;flex-direction:column;align-items:center;padding:40px 20px;text-align:center;color:#999}.empty-icon[data-v-cb507100]{font-size:48px;margin-bottom:16px;opacity:.6}.empty-text[data-v-cb507100]{font-size:14px;line-height:1.5}.tree-lines-layer[data-v-cb507100]{position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;z-index:0}.tree-vertical-line[data-v-cb507100]{position:absolute;width:1px;border-left:1px dashed rgba(0,0,0,.15)}.tree-node[data-v-cb507100]{position:relative;z-index:1}.tree-node-content[data-v-cb507100]:after{content:"";position:absolute;top:16px;height:1px;width:12px;border-top:1px dashed rgba(0,0,0,.15);pointer-events:none;left:calc(var(--tree-level, 0) * 20px + 8px)}.tree-node-content[style*="--tree-level: 0"][data-v-cb507100]:after{display:none}.tree-node-label[data-v-cb507100] mark,.tree-node-text[data-v-cb507100] mark,.tree-node-special[data-v-cb507100] mark{background-color:#ffd666;color:#000;padding:0 2px;border-radius:2px;font-weight:600}.tree-node-selected .tree-node-label[data-v-cb507100] mark,.tree-node-selected .tree-node-text[data-v-cb507100] mark,.tree-node-selected .tree-node-special[data-v-cb507100] mark{background-color:#ffa940;color:#fff}.tree-node-content[data-v-cb507100]{cursor:grab;-webkit-user-select:none;-moz-user-select:none;user-select:none}.tree-node-content[data-v-cb507100]:active{cursor:grabbing}.tree-node-content>*[data-v-cb507100]{-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.drag-over-before[data-v-cb507100]{border-top:2px solid #18a058}.drag-over-after[data-v-cb507100]{border-bottom:2px solid #18a058}.drag-over-inner[data-v-cb507100]{background-color:#18a0581a!important;border:1px dashed #18a058}.drag-over-forbid[data-v-cb507100]{background-color:#d030501a!important;border:1px dashed #d03050;cursor:not-allowed}.tree-node-multi-selected>.tree-node-content[data-v-cb507100]{background-color:#18a0581f;box-shadow:inset 0 0 0 1px #18a05866;border-radius:4px}.tree-node-selected.tree-node-multi-selected>.tree-node-content[data-v-cb507100]{background-color:#4098fc26;box-shadow:inset 0 0 0 1.5px #18a058}.tree-node-multi-selected>.tree-node-content .tree-node-label[data-v-cb507100]{color:#18a058;font-weight:600}.tree-header-area[data-v-cb507100]{padding:8px 10px 6px;flex-shrink:0;background:#fff;border-bottom:1px solid #f0f0f0;margin-bottom:4px;z-index:10}.tree-search-row[data-v-cb507100]{display:flex;align-items:center;gap:6px}.tree-search-row .n-input[data-v-cb507100]{flex:1;min-width:0}.ctrl-hint[data-v-cb507100]{display:flex;align-items:center;gap:2px;font-size:10px;color:#bbb;white-space:nowrap;flex-shrink:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.batch-toolbar[data-v-cb507100]{display:flex;align-items:center;justify-content:space-between;padding:8px 2px 2px;font-size:12px;overflow:hidden}.batch-toolbar-info[data-v-cb507100]{display:flex;align-items:center;gap:4px;color:#18a058;font-weight:500;font-size:12px}.batch-toolbar-info b[data-v-cb507100]{font-size:13px}.batch-toolbar-actions[data-v-cb507100]{display:flex;align-items:center;gap:4px;color:#999;font-size:11px}.batch-clear-link[data-v-cb507100]{cursor:pointer;color:#999;transition:color .15s;padding:2px 5px;border-radius:4px}.batch-clear-link[data-v-cb507100]:hover{color:#555;background:#0000000d}.batch-divider[data-v-cb507100]{color:#eee;font-size:11px;margin:0 2px}.batch-delete-link[data-v-cb507100]{display:inline-flex;align-items:center;gap:3px;cursor:pointer;color:#d03050;font-weight:600;padding:2px 6px;border-radius:4px;transition:all .15s;background:#d030500d}.batch-delete-link[data-v-cb507100]:hover{background:#d030501f;color:#c02040}.batch-toolbar-enter-active[data-v-cb507100],.batch-toolbar-leave-active[data-v-cb507100]{transition:all .2s ease;max-height:40px;opacity:1}.batch-toolbar-enter-from[data-v-cb507100],.batch-toolbar-leave-to[data-v-cb507100]{max-height:0;opacity:0;padding-top:0;padding-bottom:0}.kbd-key[data-v-cb507100]{display:inline-block;padding:0 4px;font-size:10px;font-family:monospace;background:#f0f0f0;border:1px solid #ccc;border-bottom-width:2px;border-radius:3px;color:#555;line-height:1.5;font-style:normal}:root,:host{--w-e-textarea-bg-color: #fff;--w-e-textarea-color: #333;--w-e-textarea-border-color: #ccc;--w-e-textarea-slight-border-color: #e8e8e8;--w-e-textarea-slight-color: #d4d4d4;--w-e-textarea-slight-bg-color: #f5f2f0;--w-e-textarea-selected-border-color: #B4D5FF;--w-e-textarea-handler-bg-color: #4290f7;--w-e-toolbar-color: #595959;--w-e-toolbar-bg-color: #fff;--w-e-toolbar-active-color: #333;--w-e-toolbar-active-bg-color: #f1f1f1;--w-e-toolbar-disabled-color: #999;--w-e-toolbar-border-color: #e8e8e8;--w-e-modal-button-bg-color: #fafafa;--w-e-modal-button-border-color: #d9d9d9}.w-e-text-container *,.w-e-toolbar *{box-sizing:border-box;margin:0;outline:none;padding:0}.w-e-text-container blockquote,.w-e-text-container li,.w-e-text-container p,.w-e-text-container td,.w-e-text-container th,.w-e-toolbar *{line-height:1.5}.w-e-text-container{background-color:var(--w-e-textarea-bg-color);color:var(--w-e-textarea-color);height:100%;position:relative}.w-e-text-container .w-e-scroll{-webkit-overflow-scrolling:touch;height:100%}.w-e-text-container [data-slate-editor]{word-wrap:break-word;border-top:1px solid transparent;min-height:100%;outline:0;padding:0 10px;white-space:pre-wrap}.w-e-text-container [data-slate-editor] p{margin:15px 0}.w-e-text-container [data-slate-editor] h1,.w-e-text-container [data-slate-editor] h2,.w-e-text-container [data-slate-editor] h3,.w-e-text-container [data-slate-editor] h4,.w-e-text-container [data-slate-editor] h5{margin:20px 0}.w-e-text-container [data-slate-editor] img{cursor:default;display:inline!important;max-width:100%;min-height:20px;min-width:20px}.w-e-text-container [data-slate-editor] span{text-indent:0}.w-e-text-container [data-slate-editor] [data-selected=true]{box-shadow:0 0 0 2px var(--w-e-textarea-selected-border-color)}.w-e-text-placeholder{font-style:italic;left:10px;top:17px;width:90%}.w-e-max-length-info,.w-e-text-placeholder{color:var(--w-e-textarea-slight-color);pointer-events:none;position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none}.w-e-max-length-info{bottom:.5em;right:1em}.w-e-bar{background-color:var(--w-e-toolbar-bg-color);color:var(--w-e-toolbar-color);font-size:14px;padding:0 5px}.w-e-bar svg{fill:var(--w-e-toolbar-color);height:14px;width:14px}.w-e-bar-show{display:flex}.w-e-bar-hidden{display:none}.w-e-hover-bar{border:1px solid var(--w-e-toolbar-border-color);border-radius:3px;box-shadow:0 2px 5px #0000001f;position:absolute}.w-e-toolbar{flex-wrap:wrap;position:relative}.w-e-bar-divider{background-color:var(--w-e-toolbar-border-color);display:inline-flex;height:40px;margin:0 5px;width:1px}.w-e-bar-item{display:flex;height:40px;padding:4px;position:relative;text-align:center}.w-e-bar-item,.w-e-bar-item button{align-items:center;justify-content:center}.w-e-bar-item button{background:transparent;border:none;color:var(--w-e-toolbar-color);cursor:pointer;display:inline-flex;height:32px;overflow:hidden;padding:0 8px;white-space:nowrap}.w-e-bar-item button:hover{background-color:var(--w-e-toolbar-active-bg-color);color:var(--w-e-toolbar-active-color)}.w-e-bar-item button .title{margin-left:5px}.w-e-bar-item .active{background-color:var(--w-e-toolbar-active-bg-color);color:var(--w-e-toolbar-active-color)}.w-e-bar-item .disabled{color:var(--w-e-toolbar-disabled-color);cursor:not-allowed}.w-e-bar-item .disabled svg{fill:var(--w-e-toolbar-disabled-color)}.w-e-bar-item .disabled:hover{background-color:var(--w-e-toolbar-bg-color);color:var(--w-e-toolbar-disabled-color)}.w-e-bar-item .disabled:hover svg{fill:var(--w-e-toolbar-disabled-color)}.w-e-menu-tooltip-v5:before{background-color:var(--w-e-toolbar-active-color);border-radius:5px;color:var(--w-e-toolbar-bg-color);content:attr(data-tooltip);font-size:.75em;opacity:0;padding:5px 10px;position:absolute;text-align:center;top:40px;transition:opacity .6s;visibility:hidden;white-space:pre;z-index:1}.w-e-menu-tooltip-v5:after{border:5px solid transparent;border-bottom:5px solid var(--w-e-toolbar-active-color);content:"";opacity:0;position:absolute;top:30px;transition:opacity .6s;visibility:hidden}.w-e-menu-tooltip-v5:hover:after,.w-e-menu-tooltip-v5:hover:before{opacity:1;visibility:visible}.w-e-menu-tooltip-v5.tooltip-right:before{left:100%;top:10px}.w-e-menu-tooltip-v5.tooltip-right:after{border-bottom-color:transparent;border-left-color:transparent;border-right-color:var(--w-e-toolbar-active-color);border-top-color:transparent;left:100%;margin-left:-10px;top:16px}.w-e-bar-item-group .w-e-bar-item-menus-container{background-color:var(--w-e-toolbar-bg-color);border:1px solid var(--w-e-toolbar-border-color);border-radius:3px;box-shadow:0 2px 10px #0000001f;display:none;left:0;margin-top:40px;position:absolute;top:0;z-index:1}.w-e-bar-item-group:hover .w-e-bar-item-menus-container{display:block}.w-e-select-list{background-color:var(--w-e-toolbar-bg-color);border:1px solid var(--w-e-toolbar-border-color);border-radius:3px;box-shadow:0 2px 10px #0000001f;left:0;margin-top:40px;max-height:350px;min-width:100px;overflow-y:auto;position:absolute;top:0;z-index:1}.w-e-select-list ul{line-height:1;list-style:none}.w-e-select-list ul .selected{background-color:var(--w-e-toolbar-active-bg-color)}.w-e-select-list ul li{cursor:pointer;padding:7px 0 7px 25px;position:relative;text-align:left;white-space:nowrap}.w-e-select-list ul li:hover{background-color:var(--w-e-toolbar-active-bg-color)}.w-e-select-list ul li svg{left:0;margin-left:5px;margin-top:-7px;position:absolute;top:50%}.w-e-bar-bottom .w-e-select-list{bottom:0;margin-bottom:40px;margin-top:0;top:inherit}.w-e-drop-panel{background-color:var(--w-e-toolbar-bg-color);border:1px solid var(--w-e-toolbar-border-color);border-radius:3px;box-shadow:0 2px 10px #0000001f;margin-top:40px;min-width:200px;padding:10px;position:absolute;top:0;z-index:1}.w-e-bar-bottom .w-e-drop-panel{bottom:0;margin-bottom:40px;margin-top:0;top:inherit}.w-e-modal{background-color:var(--w-e-toolbar-bg-color);border:1px solid var(--w-e-toolbar-border-color);border-radius:3px;box-shadow:0 2px 10px #0000001f;color:var(--w-e-toolbar-color);font-size:14px;min-height:40px;min-width:100px;padding:20px 15px 0;position:absolute;text-align:left;z-index:1}.w-e-modal .btn-close{cursor:pointer;line-height:1;padding:5px;position:absolute;right:8px;top:7px}.w-e-modal .btn-close svg{fill:var(--w-e-toolbar-color);height:10px;width:10px}.w-e-modal .babel-container{display:block;margin-bottom:15px}.w-e-modal .babel-container span{display:block;margin-bottom:10px}.w-e-modal .button-container{margin-bottom:15px}.w-e-modal button{background-color:var(--w-e-modal-button-bg-color);border:1px solid var(--w-e-modal-button-border-color);border-radius:4px;color:var(--w-e-toolbar-color);cursor:pointer;font-weight:400;height:32px;padding:4.5px 15px;text-align:center;touch-action:manipulation;transition:all .3s cubic-bezier(.645,.045,.355,1);-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:nowrap}.w-e-modal input[type=number],.w-e-modal input[type=text],.w-e-modal textarea{font-feature-settings:"tnum";background-color:var(--w-e-toolbar-bg-color);border:1px solid var(--w-e-modal-button-border-color);border-radius:4px;color:var(--w-e-toolbar-color);font-variant:tabular-nums;padding:4.5px 11px;transition:all .3s;width:100%}.w-e-modal textarea{min-height:60px}body .w-e-modal,body .w-e-modal *{box-sizing:border-box}.w-e-progress-bar{background-color:var(--w-e-textarea-handler-bg-color);height:1px;position:absolute;transition:width .3s;width:0}.w-e-full-screen-container{bottom:0!important;display:flex!important;flex-direction:column!important;height:100%!important;left:0!important;margin:0!important;padding:0!important;position:fixed;right:0!important;top:0!important;width:100%!important}.w-e-full-screen-container [data-w-e-textarea=true]{flex:1!important}.w-e-text-container [data-slate-editor] code{background-color:var(--w-e-textarea-slight-bg-color);border-radius:3px;font-family:monospace;padding:3px}.w-e-panel-content-color{list-style:none;text-align:left;width:230px}.w-e-panel-content-color li{border:1px solid var(--w-e-toolbar-bg-color);border-radius:3px;cursor:pointer;display:inline-block;padding:2px}.w-e-panel-content-color li:hover{border-color:var(--w-e-toolbar-color)}.w-e-panel-content-color li .color-block{border:1px solid var(--w-e-toolbar-border-color);border-radius:3px;height:17px;width:17px}.w-e-panel-content-color .active{border-color:var(--w-e-toolbar-color)}.w-e-panel-content-color .clear{line-height:1.5;margin-bottom:5px;width:100%}.w-e-panel-content-color .clear svg{height:16px;margin-bottom:-4px;width:16px}.w-e-text-container [data-slate-editor] blockquote{background-color:var(--w-e-textarea-slight-bg-color);border-left:8px solid var(--w-e-textarea-selected-border-color);display:block;font-size:100%;line-height:1.5;margin:10px 0;padding:10px}.w-e-panel-content-emotion{font-size:20px;list-style:none;text-align:left;width:300px}.w-e-panel-content-emotion li{border-radius:3px;cursor:pointer;display:inline-block;padding:0 5px}.w-e-panel-content-emotion li:hover{background-color:var(--w-e-textarea-slight-bg-color)}.w-e-textarea-divider{border-radius:3px;margin:20px auto;padding:20px}.w-e-textarea-divider hr{background-color:var(--w-e-textarea-border-color);border:0;display:block;height:1px}.w-e-text-container [data-slate-editor] pre>code{background-color:var(--w-e-textarea-slight-bg-color);border:1px solid var(--w-e-textarea-slight-border-color);border-radius:4px;display:block;font-size:14px;padding:10px;text-indent:0}.w-e-text-container [data-slate-editor] .w-e-image-container{display:inline-block;margin:0 3px}.w-e-text-container [data-slate-editor] .w-e-image-container:hover{box-shadow:0 0 0 2px var(--w-e-textarea-selected-border-color)}.w-e-text-container [data-slate-editor] .w-e-selected-image-container{overflow:hidden;position:relative}.w-e-text-container [data-slate-editor] .w-e-selected-image-container .w-e-image-dragger{background-color:var(--w-e-textarea-handler-bg-color);height:7px;position:absolute;width:7px}.w-e-text-container [data-slate-editor] .w-e-selected-image-container .left-top{cursor:nwse-resize;left:0;top:0}.w-e-text-container [data-slate-editor] .w-e-selected-image-container .right-top{cursor:nesw-resize;right:0;top:0}.w-e-text-container [data-slate-editor] .w-e-selected-image-container .left-bottom{bottom:0;cursor:nesw-resize;left:0}.w-e-text-container [data-slate-editor] .w-e-selected-image-container .right-bottom{bottom:0;cursor:nwse-resize;right:0}.w-e-text-container [data-slate-editor] .w-e-selected-image-container:hover,.w-e-text-container [contenteditable=false] .w-e-image-container:hover{box-shadow:none}.w-e-text-container [data-slate-editor] .table-container{border:1px dashed var(--w-e-textarea-border-color);border-radius:5px;margin-top:10px;overflow-x:auto;padding:10px;width:100%}.w-e-text-container [data-slate-editor] table{border-collapse:collapse}.w-e-text-container [data-slate-editor] table td,.w-e-text-container [data-slate-editor] table th{border:1px solid var(--w-e-textarea-border-color);line-height:1.5;min-width:30px;padding:3px 5px;text-align:left}.w-e-text-container [data-slate-editor] table th{background-color:var(--w-e-textarea-slight-bg-color);font-weight:700;text-align:center}.w-e-panel-content-table{background-color:var(--w-e-toolbar-bg-color)}.w-e-panel-content-table table{border-collapse:collapse}.w-e-panel-content-table td{border:1px solid var(--w-e-toolbar-border-color);cursor:pointer;height:15px;padding:3px 5px;width:20px}.w-e-panel-content-table td.active{background-color:var(--w-e-toolbar-active-bg-color)}.w-e-textarea-video-container{background-image:linear-gradient(45deg,#eee 25%,transparent 0,transparent 75%,#eee 0,#eee),linear-gradient(45deg,#eee 25%,#fff 0,#fff 75%,#eee 0,#eee);background-position:0 0,10px 10px;background-size:20px 20px;border:1px dashed var(--w-e-textarea-border-color);border-radius:5px;margin:10px auto 0;padding:10px 0;text-align:center}.w-e-text-container [data-slate-editor] pre>code{word-wrap:normal;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;-webkit-hyphens:none;hyphens:none;line-height:1.5;margin:.5em 0;overflow:auto;padding:1em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;text-align:left;text-shadow:0 1px #fff;white-space:pre;word-break:normal;word-spacing:normal}.w-e-text-container [data-slate-editor] pre>code .token.cdata,.w-e-text-container [data-slate-editor] pre>code .token.comment,.w-e-text-container [data-slate-editor] pre>code .token.doctype,.w-e-text-container [data-slate-editor] pre>code .token.prolog{color:#708090}.w-e-text-container [data-slate-editor] pre>code .token.punctuation{color:#999}.w-e-text-container [data-slate-editor] pre>code .token.namespace{opacity:.7}.w-e-text-container [data-slate-editor] pre>code .token.boolean,.w-e-text-container [data-slate-editor] pre>code .token.constant,.w-e-text-container [data-slate-editor] pre>code .token.deleted,.w-e-text-container [data-slate-editor] pre>code .token.number,.w-e-text-container [data-slate-editor] pre>code .token.property,.w-e-text-container [data-slate-editor] pre>code .token.symbol,.w-e-text-container [data-slate-editor] pre>code .token.tag{color:#905}.w-e-text-container [data-slate-editor] pre>code .token.attr-name,.w-e-text-container [data-slate-editor] pre>code .token.builtin,.w-e-text-container [data-slate-editor] pre>code .token.char,.w-e-text-container [data-slate-editor] pre>code .token.inserted,.w-e-text-container [data-slate-editor] pre>code .token.selector,.w-e-text-container [data-slate-editor] pre>code .token.string{color:#690}.w-e-text-container [data-slate-editor] pre>code .language-css .token.string,.w-e-text-container [data-slate-editor] pre>code .style .token.string,.w-e-text-container [data-slate-editor] pre>code .token.entity,.w-e-text-container [data-slate-editor] pre>code .token.operator,.w-e-text-container [data-slate-editor] pre>code .token.url{color:#9a6e3a}.w-e-text-container [data-slate-editor] pre>code .token.atrule,.w-e-text-container [data-slate-editor] pre>code .token.attr-value,.w-e-text-container [data-slate-editor] pre>code .token.keyword{color:#07a}.w-e-text-container [data-slate-editor] pre>code .token.class-name,.w-e-text-container [data-slate-editor] pre>code .token.function{color:#dd4a68}.w-e-text-container [data-slate-editor] pre>code .token.important,.w-e-text-container [data-slate-editor] pre>code .token.regex,.w-e-text-container [data-slate-editor] pre>code .token.variable{color:#e90}.w-e-text-container [data-slate-editor] pre>code .token.bold,.w-e-text-container [data-slate-editor] pre>code .token.important{font-weight:700}.w-e-text-container [data-slate-editor] pre>code .token.italic{font-style:italic}.w-e-text-container [data-slate-editor] pre>code .token.entity{cursor:help}[data-modify-type=removed]{background-color:var(--error-color-suppl)}[data-modify-type=added]{background-color:var(--success-color-suppl)}[data-modify-type=blank]{display:none!important}[data-modify-type=placeholder]{position:relative;color:var(--progress-rail-color)}[data-modify-type=placeholder]:before{content:"";position:absolute;top:0;left:0;width:100%;height:100%;background-color:var(--progress-rail-color)}[data-modify-type=placeholder] [data-modify-type=placeholder] .modify-arrow-left,[data-modify-type=placeholder] [data-modify-type=placeholder] .modify-arrow-right{display:none}.compareContainer .w-e-text-container [data-slate-editor]{padding:0 30px}.modify-arrow-right,.modify-arrow-left{height:100%;position:absolute;top:50%;transform:translateY(-50%);display:flex;justify-content:center;align-items:center;font-size:16px;cursor:pointer;color:var(--w-e-textarea-color);z-index:100;background-color:var(--border-color)}.modify-arrow-right:before,.modify-arrow-left:before{content:"";position:absolute;top:0;left:0;width:100%;height:100%;border:1px dashed var(--w-e-textarea-color)}.modify-arrow-right{right:-25px}.modify-arrow-left{left:-25px}.choose-row[data-v-ac55e72e]{width:100%;position:absolute;z-index:99;opacity:.6}.view-iframe[data-v-10493071]{width:100%;min-height:70vh;border:none;background:#fff}.rule-preview[data-v-07dbb7ba]{margin-top:12px;padding:12px 16px;background:#1a1a2e;border-radius:6px;font-family:JetBrains Mono,Fira Code,Cascadia Code,Consolas,monospace;font-size:13px;line-height:1.7;white-space:pre-wrap;word-break:break-word;color:#cdd6f4;border:1px solid #313244}[data-v-07dbb7ba] .rule-elem{color:#89b4fa;font-weight:500}[data-v-07dbb7ba] .rule-occ{color:#a6e3a1;font-weight:600}[data-v-07dbb7ba] .rule-pipe,[data-v-07dbb7ba] .rule-punc:not(:empty){color:#f38ba8}[data-v-07dbb7ba] .rule-comma{color:#f5c2e7}[data-v-07dbb7ba] .rule-pcdata{color:#fab387;font-style:italic}[data-v-07dbb7ba] .rule-pcdata-hint{color:#6c7086;font-size:11px;margin-left:4px}.node-tree-popover-wrapper[data-v-7a37e7a9]{position:fixed;z-index:2000;background:#fff;border:1px solid #e0e0e0;border-radius:6px;box-shadow:0 3px 12px #00000026;min-width:320px;max-width:380px;max-height:600px;overflow-y:auto}.node-tree-popover-wrapper .node-tree-popover-card[data-v-7a37e7a9]{display:flex;flex-direction:column;height:100%}.node-tree-popover-wrapper .node-tree-popover-card .node-tree-popover-header[data-v-7a37e7a9]{display:flex;align-items:center;justify-content:space-between;padding:12px;border-bottom:1px solid #e0e0e0;font-weight:500;min-height:44px}.node-tree-popover-wrapper .node-tree-popover-card .popover-header[data-v-7a37e7a9]{display:flex;align-items:center;justify-content:space-between;padding:12px;border-bottom:1px solid #e0e0e0;font-weight:500}.node-tree-popover-body[data-v-7a37e7a9]{display:flex;flex-direction:column;gap:12px;padding:12px;min-width:350px;max-height:500px;overflow-y:auto}.node-tree-popover-body .current-node-info[data-v-7a37e7a9]{padding:10px;background:linear-gradient(135deg,#f5f7fa,#c3cfe2);border-radius:6px;border-left:3px solid #1890ff}.node-tree-popover-body .current-node-info .info-row[data-v-7a37e7a9]{display:flex;gap:8px;margin-bottom:6px;font-size:12px}.node-tree-popover-body .current-node-info .info-row[data-v-7a37e7a9]:last-child{margin-bottom:0}.node-tree-popover-body .current-node-info .info-row .label[data-v-7a37e7a9]{font-weight:600;min-width:70px;color:#333}.node-tree-popover-body .current-node-info .info-row .value[data-v-7a37e7a9]{flex:1;color:#555;word-break:break-all;font-family:Monaco,Menlo,monospace;font-size:11px}.node-tree-popover-body .node-structure-tree .tree-title[data-v-7a37e7a9]{font-size:13px;font-weight:600;color:#333;margin-bottom:8px;padding-bottom:6px;border-bottom:2px solid #1890ff}.node-tree-popover-body .node-structure-tree .tree-content[data-v-7a37e7a9]{display:flex;flex-direction:column;gap:0;max-height:280px;overflow-y:auto;border:1px solid #d9d9d9;border-radius:6px;background-color:#fff}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item[data-v-7a37e7a9]{display:flex;align-items:center;gap:0;padding:4px 6px;font-size:12px;transition:all .2s;border-bottom:1px solid #f0f0f0;border-left:3px solid #e8e8e8;position:relative}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item[data-v-7a37e7a9]:last-child{border-bottom:none}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item[data-v-7a37e7a9]:hover{background-color:#fafafa;border-left-color:#d4d4d4}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item.is-parent[data-v-7a37e7a9]{background-color:#f5f7fa;border-left-color:#94a3b8;color:#334155;font-weight:500}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item.is-current[data-v-7a37e7a9]{background-color:#dbeafe;border-left-color:#0284c7;font-weight:500}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item.is-current.is-parent[data-v-7a37e7a9]{background-color:#dbeafe;border-left-color:#0284c7;color:#262626}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item .expand-btn[data-v-7a37e7a9]{min-width:16px;width:16px;height:16px;padding:0;display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-right:2px}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item .expand-placeholder[data-v-7a37e7a9]{width:16px;height:16px;flex-shrink:0;margin-right:2px}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item .node-label[data-v-7a37e7a9]{flex:1;text-align:left;padding:2px 4px;height:auto;display:flex;align-items:center;gap:4px;cursor:pointer;border-radius:3px;transition:all .15s;justify-content:flex-start}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item .node-label[data-v-7a37e7a9]:hover{background-color:#0ea5e914}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item .node-label.is-current-node[data-v-7a37e7a9]{font-weight:500;color:#0ea5e9}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item .node-label.is-selected-node[data-v-7a37e7a9]{font-weight:600;color:#0284c7}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item .node-label .node-type[data-v-7a37e7a9]{font-weight:600;color:#0284c7;min-width:45px;padding:2px 6px;background-color:#0284c71a;border-radius:3px;font-size:11px;white-space:nowrap;text-align:center;flex-shrink:0}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item .node-label .node-preview[data-v-7a37e7a9]{color:#64748b;font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:80px;font-style:italic}.node-tree-popover-body .node-structure-tree .tree-content .tree-node-item .child-count[data-v-7a37e7a9]{font-size:10px;font-weight:600;color:#fff;background-color:#1890ff;padding:2px 6px;border-radius:10px;min-width:20px;text-align:center;flex-shrink:0}.node-tree-popover-body .operation-buttons[data-v-7a37e7a9]{display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px}.node-tree-popover-body .operation-buttons[data-v-7a37e7a9] .n-button{font-size:12px;height:32px}.w-e-text-container .w-e-scroll{padding-bottom:30vh}[data-w-e-type=HTML-TABLE] .chooseEntry{background-color:var(--avatar-color)}[data-w-e-type=HTML-TABLE] [data-w-e-type=HTML-TBODY].bg-warningColorSuppl .chooseEntry{background-color:var(--warning-color)}[data-w-e-type=HTML-TABLE] [data-w-e-type=HTML-THEAD] [data-w-e-type=ENTRY].chooseEntry{background-color:var(--avatar-color)}[data-w-e-type=HTML-TABLE] [data-w-e-type=HTML-THEAD].bg-warningColorSuppl [data-w-e-type=ENTRY]{background-color:var(--warning-color)}[data-w-e-type=HTML-TABLE] [data-w-e-type=HTML-THEAD].bg-warningColorSuppl [data-w-e-type=ENTRY].chooseEntry{background-color:var(--avatar-color)}[data-w-e-type=HTML-TABLE] [data-w-e-type=HTML-TBODY] [data-w-e-type=ENTRY]:not([DISABLED=TRUE]).chooseEntry{background-color:var(--avatar-color)!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ROW].bg-warningColorSuppl [data-w-e-type=ENTRY]{background-color:var(--warning-color)}[data-w-e-type=HTML-TABLE] [data-w-e-type=ROW].bg-warningColorSuppl [data-w-e-type=ENTRY].chooseEntry{background-color:var(--avatar-color)!important}[data-w-e-type=HTML-TABLE] [data-w-e-type=ENTRY].bg-warningColorSuppl{background-color:var(--warning-color)}.highlightColumn{background-color:var(--warning-color)!important}.highlightColumn .bg-warningColorSuppl{background-color:var(--warning-color)}.split-wrapper{position:relative}.left-pane{position:relative;transition:width .3s ease}.sidebar-toggle{position:absolute;top:50%;transform:translate(-50%,-50%);width:34px;height:34px;border-radius:999px;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:50;box-shadow:0 4px 12px #0000001f;background:var(--card-color);border:1px solid var(--border-color);color:var(--primary-color);transition:background-color .2s ease,border-color .2s ease,transform .2s ease}.sidebar-toggle:hover{border-color:var(--primary-color);background-color:#1890ff14}.sidebar-toggle.collapsed{transform:translateY(-50%)}
import{f as a,A as e,h as l,o as t,x as s,n as r,w as o,y as n,B as u,F as i,j as m,C as c,D as p,E as d}from"./index-BGNM-WBG.js";import{_ as h,a as f}from"./DataTable-CEJURamz.js";import{_ as b}from"./Button-ByITIhW2.js";import"./use-rtl-iN3przWb.js";import"./Scrollbar-DmIJvlAu.js";const y=a({__name:"theme",setup(a){const y=e(),j=()=>{y.changeTheme()},k=[{title:"变量名",key:"label"},{title:"变量值",key:"value"},{title:"颜色",key:"color",render:a=>m("div",{style:{width:"30px",height:"30px",backgroundColor:a.value}})},{title:"值",key:"value",render:a=>m("span",{},y.theme?c[a.label]:p[a.label])}],x=l(()=>Object.entries(d.colors).map(([a,e])=>({label:a,value:e})));return(a,e)=>{const l=b,m=h,c=f;return t(),s(i,null,[r(m,null,{default:o(()=>[r(l,{type:"primary",onClick:j},{default:o(()=>[n("主题切换")]),_:1})]),_:1}),r(c,{columns:k,data:u(x),"flex-height":"",class:"h-full"},null,8,["data"])],64)}}});export{y as default};
import{b_ as t,b7 as n,h as r,J as s,bC as e,H as a,a4 as o,bW as u}from"./index-BGNM-WBG.js";function i(i,l,c){if(!l)return;const f=n(),d=r(()=>{const{value:t}=l;if(!t)return;const n=t[i];return n||void 0}),v=s(e,null),b=()=>{o(()=>{const{value:n}=c,r=`${n}${i}Rtl`;if(function(n,r){if(void 0===n)return!1;if(r){const{context:{ids:t}}=r;return t.has(n)}return null!==t(n)}(r,f))return;const{value:s}=d;s&&s.style.mount({id:r,head:!0,anchorMetaName:u,props:{bPrefix:n?`.${n}-`:void 0},ssr:f,parent:null==v?void 0:v.styleMountTarget})})};return f?b():a(b),d}export{i as u};
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1724118561720" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4707" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M922.85749971 610.25000029C845.74999971 509.75 730.62500029 406.54999971 698.89249971 381.42500029 676.25 363.5 654.46250029 354.5 632.69749971 354.5a95.30250029 95.30250029 0 0 0-61.65749971 25.12500029c-3.62999971 3.59250029-9.0675 8.0775-15.4125 13.46249971-45.3375 37.69499971-186.795 166.0275-190.4175 169.61249971a58.94250029 58.94250029 0 0 1-39.89999971 16.155 69.21749971 69.21749971 0 0 1-43.50000058-20.63999971A1055.0475 1055.0475 0 0 1 166.625 448.73c-34.45499971-38.5875-63.4725-72.69000029-63.4725-72.69000029s-6.345 12.56249971-9.97499971 22.4325a17.02500029 17.02500029 0 0 0 0.90749971 14.36250058C113.12749971 439.75999971 199.25 541.16749971 317.15 642.57499971A100.26749971 100.26749971 0 0 0 383.33750029 669.5a95.30250029 95.30250029 0 0 0 61.65749971-25.12500029c3.62999971-3.59250029 9.0675-8.0775 15.4125-13.46249971 45.3375-37.69499971 131.48250029-115.76999971 171.37500029-152.56500029 9.0675-8.0775 15.4125-13.46249971 18.135-16.155a58.94250029 58.94250029 0 0 1 39.89999971-16.155 74.295 74.295 0 0 1 43.49999971 20.64000058c25.38749971 21.53999971 66.195 59.24999971 115.155 109.49999942 35.36250029 37.69499971 63.4725 71.7975 63.4725 72.69000029 0 0 6.345-13.46249971 9.97500058-22.4325a16.12500029 16.12500029 0 0 0 0.93749942-16.18499971z" fill="#8DC21F" p-id="4708"></path><path d="M469.61 235.0475c-39.8025 28.95000029-202.635 180.0225-234.29999971 216.20999971a550.50000029 550.50000029 0 0 0 56.99999971 53.40000058 19.8 19.8 0 0 0 28.95000029-3.61500029c62.42249971-63.32249971 190.88250029-176.40749971 208.06499971-189.97499971 47.9475-40.70999971 104.94-44.325 155.59499971-6.33000058 124.83749971 94.05 236.10750029 241.50750029 255.105 268.65 0 0 2.71500029-13.5675 4.5-25.32749942a28.5975 28.5975 0 0 0-3.61499942-20.80500029c-117.60000029-169.1775-242.42249971-274.11000029-290.37000058-300.34500029-54.27749971-28.04249971-117.60000029-38.0025-180.92999971 8.13750029z" fill="#0084CF" p-id="4709"></path><path d="M337.50500029 147.2975c-80.4825 65.13000029-184.5 161.02500029-210.75000029 189.97499971a706.5 706.5 0 0 0 50.66250029 54.27750058 11.12249971 11.12249971 0 0 0 17.19-1.80750058c79.605-76.89750029 165.54750029-155.60250029 216.2025-194.50499942 84.13499971-65.1375 183.645-71.46749971 280.44 5.42999971a1192.66499971 1192.66499971 0 0 1 114.89249971 104.94 1622.99999971 1622.99999971 0 0 1 141.12 180 206.505 206.505 0 0 0-4.5-37.99500029 57.83249971 57.83249971 0 0 0-5.42999971-14.47499971 857.025 857.025 0 0 0-100.43250029-145.59000029C728.3375 160.865 660.49250029 122.86999971 616.16750029 99.35 557.36 69.49999971 444.28249971 60.4475 337.50500029 147.2975z" fill="#0084CF" p-id="4710"></path><path d="M554.6375 782.36c39.8025-28.95000029 202.64249971-180 234.29999971-216.20999971a615.465 615.465 0 0 0-56.99999971-54.27750058 19.8 19.8 0 0 0-28.95000029 3.61500029c-62.3925 63.32249971-190.85249971 176.40749971-208.04249971 189.97499971-47.9475 40.70999971-104.94 44.325-156.50250029 6.33000058C213.605 617.71249971 103.23500029 470.25500029 84.23749971 442.21249971c0 0-2.71500029 13.5675-4.5 25.32750029a28.5975 28.5975 0 0 0 3.6 20.8125c117.60000029 169.16249971 242.4375 274.11000029 290.385 300.35999971 54.27749971 29.82750029 117.6075 39.75000029 180.91500029-6.35249971z" fill="#0084CF" p-id="4711"></path><path d="M686.75000029 870.10250029c80.51249971-65.1375 184.545-161.02500029 210.74999942-189.97500058a706.5 706.5 0 0 0-50.66249942-54.27749971 11.12249971 11.12249971 0 0 0-17.19 1.80749971C750.05 704.555 664.10749971 783.26 613.4525 822.1625c-84.13499971 65.1375-183.645 71.46749971-280.44-5.42999971a1192.67250029 1192.67250029 0 0 1-114.89249971-104.9625A1622.99999971 1622.99999971 0 0 1 77.00000029 531.77000029a214.43249971 214.43249971 0 0 0 4.5 37.99499942 57.83249971 57.83249971 0 0 0 5.45249971 14.50500029 857.025 857.025 0 0 0 100.4175 145.64999971c108.55500029 126.60000029 176.4 164.61 220.725 188.1 58.80750029 29.88749971 171.88499971 38.93249971 278.65500029-47.91749942z" fill="#0084CF" p-id="4712"></path></svg>
\ No newline at end of file
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Cache-Control" content="no-cache, no-store">
<script type="module" crossorigin src="./assets/index-BGNM-WBG.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-CKDo6wGH.css">
</head>
<body>
<div id="app"></div>
</body>
</html>
\ No newline at end of file
armo-xm项目重构-前端
<template>
<n-config-provider
:theme="appStore.isDark ? darkTheme : null"
:theme-overrides="appStore.isDark ? darkThemeOverrides : lightThemeOverrides"
:locale="zhCN"
:date-locale="dateZhCN"
>
<n-loading-bar-provider>
<n-message-provider>
<n-notification-provider>
<n-dialog-provider>
<!-- 全局内容 -->
<div class="h-screen w-full bg-body overflow-hidden transition-colors duration-300">
<router-view></router-view>
</div>
<!-- 全局阻塞式加载蒙层 (极简版) -->
<transition name="loading-fade">
<div v-if="appStore.loading" class="global-loading-overlay">
<n-spin size="large">
<template #description>
<span class="loading-text">{{ appStore.loadingText }}</span>
</template>
</n-spin>
</div>
</transition>
</n-dialog-provider>
</n-notification-provider>
</n-message-provider>
</n-loading-bar-provider>
</n-config-provider>
</template>
<script setup lang="ts">
import { zhCN, dateZhCN, darkTheme, type GlobalThemeOverrides } from 'naive-ui'
import { useAppStore } from '@/store/app/index'
import { lightThemeConfig, darkThemeConfig } from '@/configs/tailwind.ui.config'
const appStore = useAppStore()
// 根据 borderRadius 值生成 Naive UI 的 borderRadius 字符串
const getBorderRadius = () => `${appStore.borderRadius * 16}px`
const getFontSize = () => `${appStore.fontSize}px`
// 计算 Naive UI 的主题覆写(响应式 computed)
const lightThemeOverrides = computed<GlobalThemeOverrides>(() => ({
common: {
...lightThemeConfig,
primaryColor: appStore.primaryColor,
primaryColorHover: appStore.primaryColor + 'cc',
primaryColorPressed: appStore.primaryColor + '99',
primaryColorSuppl: appStore.primaryColor,
borderRadius: getBorderRadius(),
borderRadiusSmall: getBorderRadius(),
fontSize: getFontSize(),
fontSizeMedium: getFontSize(),
fontSizeLarge: `${appStore.fontSize + 2}px`,
fontSizeSmall: `${appStore.fontSize - 2}px`,
fontSizeTiny: `${appStore.fontSize - 2}px`
},
Layout: {
siderToggleBarColor: 'rgba(0, 0, 0, 0.05)',
siderToggleBarColorHover: appStore.primaryColor + '33'
}
}))
const darkThemeOverrides = computed<GlobalThemeOverrides>(() => ({
common: {
...darkThemeConfig,
primaryColor: appStore.primaryColor,
primaryColorHover: appStore.primaryColor + 'cc',
primaryColorPressed: appStore.primaryColor + '99',
primaryColorSuppl: appStore.primaryColor,
borderRadius: getBorderRadius(),
borderRadiusSmall: getBorderRadius(),
fontSize: getFontSize(),
fontSizeMedium: getFontSize(),
fontSizeLarge: `${appStore.fontSize + 2}px`,
fontSizeSmall: `${appStore.fontSize - 2}px`,
fontSizeTiny: `${appStore.fontSize - 2}px`
},
Layout: {
siderToggleBarColor: 'rgba(255, 255, 255, 0.1)',
siderToggleBarColorHover: appStore.primaryColor + '40'
}
}))
// 同步主题变量到 CSS 根节点,供 Tailwind 使用
const updateCssVariables = (config: any) => {
const root = document.documentElement
Object.entries(config).forEach(([key, value]) => {
if (typeof value === 'string') {
root.style.setProperty(`--${key}`, value)
}
})
// 始终用 appStore 的动态值覆盖
root.style.setProperty('--primary-color', appStore.primaryColor)
const semanticMap: Record<string, string> = {
'--primary-color': appStore.primaryColor,
'--primary-color-hover': config.primaryColorHover,
'--primary-color-pressed': config.primaryColorPressed,
'--success-color': config.successColor,
'--success-color-hover': config.successColorHover,
'--success-color-pressed': config.successColorPressed,
'--warning-color': config.warningColor,
'--warning-color-hover': config.warningColorHover,
'--warning-color-pressed': config.warningColorPressed,
'--error-color': config.errorColor,
'--error-color-hover': config.errorColorHover,
'--error-color-pressed': config.errorColorPressed,
'--body-color': config.bodyColor,
'--card-color': config.cardColor,
'--divider-color': config.dividerColor,
'--border-color': config.borderColor
}
Object.entries(semanticMap).forEach(([key, value]) => {
if (value) root.style.setProperty(key, value)
})
}
// 应用字体大小到 body
const applyFontSize = (size: number) => {
document.documentElement.style.fontSize = `${size}px`
}
// 应用色弱/灰色模式
const applyFilterModes = () => {
document.body.classList.toggle('color-weak', appStore.colorWeak)
document.body.classList.toggle('gray-mode', appStore.grayMode)
}
// 监听主题(暗/亮)变化
watch(
() => appStore.isDark,
(isDark) => {
if (isDark) {
document.documentElement.classList.add('dark')
updateCssVariables(darkThemeConfig)
} else {
document.documentElement.classList.remove('dark')
updateCssVariables(lightThemeConfig)
}
},
{ immediate: true }
)
// 监听主题色变化
watch(
() => appStore.primaryColor,
(color) => {
document.documentElement.style.setProperty('--primary-color', color)
}
)
// 监听字体大小变化
watch(
() => appStore.fontSize,
(size) => {
applyFontSize(size)
},
{ immediate: true }
)
// 监听色弱/灰色模式变化
watch(
[() => appStore.colorWeak, () => appStore.grayMode],
() => {
applyFilterModes()
},
{ immediate: true }
)
</script>
<style>
body {
margin: 0;
padding: 0;
}
.bg-body {
background-color: var(--body-color);
}
/* 色弱模式 */
body.color-weak {
filter: invert(80%) hue-rotate(180deg);
}
/* 灰色模式 */
body.gray-mode {
filter: grayscale(100%);
}
.global-loading-overlay {
position: fixed;
inset: 0;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(4px);
}
.loading-text {
margin-top: 12px;
font-size: 14px;
color: var(--primary-color);
letter-spacing: 1px;
}
.loading-fade-enter-active,
.loading-fade-leave-active {
transition: opacity 0.3s ease;
}
.loading-fade-enter-from,
.loading-fade-leave-to {
opacity: 0;
}
</style>
<style scoped></style>
import { createAlova } from 'alova'
import VueHook from 'alova/vue'
import adapterFetch from 'alova/fetch'
// 移除了局部 createDiscreteApi 的创建,统一使用 window.$message 和 window.$loadingBar (见规则 80/81)
export interface RequestConfig {
/** 控制是否开启全局加载动画,默认为 false,支持传入字符串显示自定义加载文案 */
showLoading?: boolean | string
/** 自定义从 data 中获取错误信息的字段名 (优先级最高) */
msgField?: string
[key: string]: any
}
// 封装创建 Alova 实例 of 函数,方便支持多个域名
const createService = (baseURL: string) => {
const alovaInst = createAlova({
baseURL,
statesHook: VueHook,
requestAdapter: adapterFetch(),
beforeRequest(method) {
method.config.headers['X-Requested-With'] = 'XMLHttpRequest'
method.config.credentials = 'include'
// 底层加入控制全局加载动画的参数处理
const showLoading = method.meta?.showLoading
if (showLoading) {
window.$loadingBar?.start()
// 开启阻塞式蒙层加载逻辑
window.$loading?.start(typeof showLoading === 'string' ? showLoading : '加载中...')
}
},
responded: {
onSuccess: async (response, method) => {
if (method.meta?.showLoading) {
window.$loadingBar?.finish()
window.$loading?.finish()
}
if (response.status >= 400) {
const text = await response.text()
console.error(`Request failed with status ${response.status}: ${text}`)
window.$message.error(`请求失败: ${response.status} ${response.statusText}`)
throw new Error(response.statusText)
}
const contentType = response.headers.get('content-type') || ''
const isJson = contentType.includes('application/json')
let json: ResponseData | null = null
if (isJson) {
json = (await response.json()) as ResponseData
if (json) {
if (json.code === 200 || json.code === '200' || json.code === 0 || json.code === '0' || json.success === true) {
json.code = 200
return json
}
}
} else {
const isBinary =
method.meta?.isDownload ||
contentType.includes('image/') ||
contentType.includes('application/pdf') ||
contentType.includes('text/') ||
contentType.includes('application/vnd.ms-excel') ||
contentType.includes('application/vnd.openxmlformats-officedocument') ||
contentType.includes('application/octet-stream') ||
contentType.includes('application/zip')
if (isBinary) {
return response
}
}
if (!json) {
json = (await response.json()) as ResponseData
}
if (json) {
if (json.code === 200 || json.code === '200' || json.code === 0 || json.code === '0' || json.success === true) {
json.code = 200
}
}
// 此处可根据后台全局的格式状态码做拦截或直接返回
if (json && json.code !== 200) {
// 1. 定义识别“技术性垃圾 ID”的逻辑
const isTechnicalId = (s: any) =>
!s ||
typeof s !== 'string' ||
s.trim() === '' ||
s.startsWith('ERRMSG.') || // 过滤标识符
s.startsWith('#{') || // 过滤占位符
/^[A-Z0-9_.]+$/.test(s) || // 过滤全大写/点号连接的代码名
(json && s === String(json.code)) // 过滤纯状态码
// 2. 初始回退信息
let errorMsg = json.msg || '请求发生错误'
// 如果在 RequestConfig 中指定了字段名,则以此为主 (最高优先级)
const customField = method.meta?.msgField
if (customField && json.data && typeof json.data === 'object' && json.data[customField]) {
errorMsg = json.data[customField]
} else {
// 3. 构建候选字段池 (按质量优先级排序)
const { default: i18n } = await import('@/locales')
const isZh = String(i18n.global.locale.value || i18n.global.locale)
.toLowerCase()
.includes('zh')
// 收集所有候选,优先处理 data 内部字段
const dataObj = json.data && typeof json.data === 'object' ? json.data : {}
const candidates = [dataObj.message, dataObj.remark, dataObj.msg, dataObj.i18nText, dataObj.msgData]
// 注入外层候选字段
candidates.push(json.msg)
candidates.push(json.msgData)
// 4. 智能筛选逻辑
for (let cand of candidates) {
if (!cand) continue
// 处理数组类型的错误信息(常见于 msgData)
if (Array.isArray(cand)) {
cand = cand.length > 0 ? cand[0] : null
}
if (!cand) continue
if (!isTechnicalId(cand)) {
// 如果是中文环境,进一步检查是否有中文字符,提升准确度
if (isZh && /[\u4e00-\u9fa5]/.test(cand!)) {
errorMsg = cand!
break
}
// 如果是非中文环境或未匹配到中文,但这个候选词不是技术 ID,我们也选它
if (!isZh || !errorMsg || isTechnicalId(errorMsg)) {
errorMsg = cand!
// 如果在非中文环境下找到了 i18nText,直接结束
if (!isZh && cand === json.data?.i18nText) break
}
}
}
}
// 【核心修复】将智能识别出的信息回写到响应对象中,确保业务层获取到的也是处理后的文案
json.msg = errorMsg
if (json.code === 100 || json.code === '100') {
window.$message.error(errorMsg)
window.location.hash = '/login'
throw new Error('未登录')
}
window.$message.error(errorMsg)
// 视业务约定是否抛出异常来中断后续 promise 处理
// throw new Error(errorMsg)
}
return json!
},
onError: (err, method) => {
if (method.meta?.showLoading) {
window.$loadingBar?.error()
window.$loading?.finish()
}
window.$message.error(err.message || '网络请求失败')
throw err
}
}
})
return {
get<T = any>(url: string, config?: RequestConfig) {
const { showLoading = false, msgField, ...rest } = config || {}
return alovaInst.Get<ResponseData<T>>(url, { ...rest, meta: { ...rest.meta, showLoading, msgField } }).send(true)
},
/** 提交数据 (默认使用 application/x-www-form-urlencoded) */
post<T = any>(url: string, data?: any, config?: RequestConfig) {
const { showLoading = false, msgField, ...rest } = config || {}
const isFormData = data instanceof FormData
const headers = { ...rest.headers }
if (!isFormData) {
headers['Content-Type'] = 'application/x-www-form-urlencoded'
}
let body = data
if (!isFormData) {
// 处理参数中的 null/undefined 为空字符串,防止 URLSearchParams 转为 "null" 字符串
if (data && typeof data === 'object' && !(data instanceof URLSearchParams)) {
const processedData: any = {}
Object.keys(data).forEach((key) => {
const val = data[key]
processedData[key] = val === null || val === undefined ? '' : val
})
body = new URLSearchParams(processedData).toString()
} else if (typeof data === 'object') {
body = new URLSearchParams(data).toString()
}
}
return alovaInst
.Post<ResponseData<T>>(url, body, {
...rest,
headers,
meta: { ...rest.meta, showLoading, msgField }
})
.send(true)
},
put<T = any>(url: string, data?: any, config?: RequestConfig) {
const { showLoading = false, msgField, ...rest } = config || {}
return alovaInst.Put<ResponseData<T>>(url, data, { ...rest, meta: { ...rest.meta, showLoading, msgField } }).send(true)
},
delete<T = any>(url: string, config?: RequestConfig) {
const { showLoading = false, msgField, ...rest } = config || {}
return alovaInst.Delete<ResponseData<T>>(url, { ...rest, meta: { ...rest.meta, showLoading, msgField } }).send(true)
},
/** 下载文件 (支持流式下载及自动两步验证模式) */
async download(url: string, data?: any, fileName?: string, config?: RequestConfig) {
const { showLoading = true, ...rest } = config || {}
const getProcessedBody = (dataObj: any) => {
if (dataObj instanceof FormData) return dataObj
const searchParams = new URLSearchParams()
if (dataObj && typeof dataObj === 'object') {
Object.keys(dataObj).forEach((key) => {
const val = dataObj[key]
searchParams.append(key, val === null || val === undefined ? '' : val)
})
}
return searchParams.toString()
}
const isFormData = data instanceof FormData
const headers = { ...rest.headers }
if (!isFormData) {
headers['Content-Type'] = 'application/x-www-form-urlencoded'
}
try {
// 第一步:发送请求(可能是预检查 JSON,也可能是直接文件流)
const method = alovaInst.Post(url, getProcessedBody(data), {
...rest,
headers,
meta: { ...rest.meta, showLoading, isDownload: true }
})
const result = await method.send()
// 情况 A:返回的是 Response 对象(即已识别出的二进制流)
if (result instanceof Response) {
const blob = await result.blob()
this._triggerDownload(blob, fileName, result)
return true
}
// 情况 B:返回的是 JSON 对象(预检查成功)
const resData = result as any
if (resData && typeof resData === 'object' && (resData.code === 200 || resData.code === 0 || resData.code === '200')) {
// 如果已经是带 down=Y 的请求返回了 JSON(可能是某些特殊接口),则不再重试
if (data?.down === 'Y') return true
// 自动执行第二步:携带 down=Y 获取正式文件流
const retryData = isFormData ? data : { ...data, down: 'Y' }
if (isFormData) {
;(retryData as FormData).append('down', 'Y')
}
const downloadMethod = alovaInst.Post(url, getProcessedBody(retryData), {
...rest,
headers,
meta: { ...rest.meta, showLoading: false, isDownload: true }
})
const dlRes = await downloadMethod.send()
if (dlRes instanceof Response) {
const blob = await dlRes.blob()
this._triggerDownload(blob, fileName, dlRes)
return true
}
}
return false
} catch (err) {
console.error('Download error:', err)
return false
}
},
/** 内部辅助:触发浏览器下载 */
_triggerDownload(blob: Blob, fileName?: string, response?: Response) {
let parsedFileName = fileName
if (!parsedFileName && response) {
const disposition = response.headers.get('content-disposition') || response.headers.get('Content-Disposition')
if (disposition) {
// 优先匹配 filename* (可能包含 UTF-8 编码格式)
const filenameStarRegex = /filename\*=utf-8''([^;\n]*)/i
const starMatches = filenameStarRegex.exec(disposition)
if (starMatches && starMatches[1]) {
try {
parsedFileName = decodeURIComponent(starMatches[1])
} catch (e) {
// Ignore
}
} else {
// 兜底匹配普通 filename
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
const matches = filenameRegex.exec(disposition)
if (matches && matches[1]) {
parsedFileName = matches[1].replace(/['"]/g, '')
try {
parsedFileName = decodeURIComponent(parsedFileName)
} catch (e) {
// Ignore
}
// 核心修复:如果文件名是 ISO-8859-1 编码的 UTF-8 字节串(常见于 Java 后端导出),则进行转换
if (parsedFileName && !/[^\x00-\xff]/.test(parsedFileName)) {
try {
const bytes = new Uint8Array(parsedFileName.split('').map((c) => c.charCodeAt(0)))
parsedFileName = new TextDecoder('utf-8').decode(bytes)
} catch (e) {
// Ignore
}
}
}
}
}
}
const downloadUrl = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = downloadUrl
a.download = parsedFileName || `export_${new Date().getTime()}.xlsx`
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(downloadUrl)
document.body.removeChild(a)
},
// 对外暴露 alova 实例,以便使用 useRequest 或者其他 hook
alova: alovaInst
}
}
const getBaseURL = () => {
// 开发环境使用相对路径以便配合 vite.config.ts 的代理服务
if (import.meta.env.DEV) {
return '/api'
}
// 生产/打包环境下,若配置了 VITE_APP_PROXY_URL,则将接口基准地址指向对应地址的 /api 路径
const proxyUrl = import.meta.env.VITE_APP_PROXY_URL
if (proxyUrl) {
const cleanUrl = proxyUrl.trim().replace(/\/$/, '')
return cleanUrl.endsWith('/api') ? cleanUrl : `${cleanUrl}/api`
}
return '/api'
}
// 当前主要的域名服务
export const service = createService(getBaseURL())
// 后续支持多个域名的示例(需要调用另一域名的接口时,直接导出并使用即可)
export const serviceDomain2 = createService('/apiDomain2')
// 兼容老代码中直接引用 alovaInstance 的方式
export const alovaInstance = service.alova
<JOBCARD><SMJC-HEADER/><CEP CHAPNBR="28" CHG="U" CONFLTR="A" CONFNBR="00" FUNC="710" KEY="EN28240071080300" PGBLKNBR="05" REVDATE="20240501" SECTNBR="24" SEQ="803" SUBJNBR="00"><EFFECT EFFRG="001999"/><TITLEC>Operational Check of LP Shut Off Valves including Operation by Individual Motors</TITLEC><TITLE>Operational Check of LP Shut Off Valves including Operation by Individual Motors</TITLE><WARNING><PARAC>PUT THE SAFETY DEVICES AND THE WARNING NOTICES IN POSITION BEFORE YOU START A TASK ON OR NEAR:</PARAC><PARA>PUT THE SAFETY DEVICES AND THE WARNING NOTICES IN POSITION BEFORE YOU START A TASK ON OR NEAR:</PARA><UNLIST BULLTYPE="SYSTEM"><UNLITEM><PARAC>THE FLIGHT CONTROLS</PARAC><PARA>THE FLIGHT CONTROLS</PARA></UNLITEM><UNLITEM><PARAC>THE FLIGHT CONTROL SURFACES</PARAC><PARA>THE FLIGHT CONTROL SURFACES</PARA></UNLITEM><UNLITEM><PARAC>THE LANDING GEAR AND THE RELATED DOORS</PARAC><PARA>THE LANDING GEAR AND THE RELATED DOORS</PARA></UNLITEM><UNLITEM><PARAC>COMPONENTS THAT MOVE.</PARAC><PARA>COMPONENTS THAT MOVE.</PARA></UNLITEM></UNLIST><PARAC>MOVEMENT OF COMPONENTS CAN KILL OR CAUSE INJURY TO PERSONS AND/OR CAN CAUSE DAMAGE TO THE EQUIPMENT.</PARAC><PARA>MOVEMENT OF COMPONENTS CAN KILL OR CAUSE INJURY TO PERSONS AND/OR CAN CAUSE DAMAGE TO THE EQUIPMENT.</PARA></WARNING><ASSODATA><ZONELST><ZONE>210</ZONE><ZONE>522</ZONE><ZONE>622</ZONE></ZONELST><EINLST><EINDATA><EFFECT EFFRG="001999"/><EIN TYPE="EXACT">12-QM</EIN></EINDATA><EINDATA><EFFECT EFFRG="001999"/><EIN TYPE="EXACT">13-QM</EIN></EINDATA></EINLST></ASSODATA><TFMATR><PRETOPIC><TITLEC>Reason for the Job</TITLEC><TITLE>Reason for the Job</TITLE><PARAC><REFEXT REFLOC="282400-02" REFMAN="MPD">Refer to the MPD TASK: 282400-02</REFEXT></PARAC><PARA><REFEXT REFLOC="282400-02" REFMAN="MPD">Refer to the MPD TASK: 282400-02</REFEXT></PARA><PARAC>OPERATIONAL CHECK OF LP SHUT OFF VALVES INCLUDING OPERATION BY INDIVIDUAL MOTORS</PARAC><PARA>OPERATIONAL CHECK OF LP SHUT OFF VALVES INCLUDING OPERATION BY INDIVIDUAL MOTORS</PARA></PRETOPIC><PRETOPIC><TITLEC>Job Set-up Information</TITLEC><TITLE>Job Set-up Information</TITLE><LIST1><L1ITEM><PARAC>Fixtures, Tools, Test and Support Equipment</PARAC><PARA>Fixtures, Tools, Test and Support Equipment</PARA><TABLE><TGROUP ALIGN="LEFT" CHAR="" CHAROFF="50" COLS="3"><COLSPEC COLNAME="COL1" COLWIDTH="22*"/><COLSPEC COLNAME="COL2" COLWIDTH="4*"/><COLSPEC COLNAME="COL3" COLWIDTH="53*"/><SPANSPEC ALIGN="CENTER" NAMEEND="COL3" NAMEST="COL1" SPANNAME="WHOLE"/><THEAD VALIGN="BOTTOM"><ROW><ENTRY COLNAME="COL1"><PARAC>REFERENCE</PARAC><PARA>REFERENCE</PARA></ENTRY><ENTRY COLNAME="COL2"><PARAC>QTY</PARAC><PARA>QTY</PARA></ENTRY><ENTRY COLNAME="COL3"><PARAC>DESIGNATION</PARAC><PARA>DESIGNATION</PARA></ENTRY></ROW></THEAD><TBODY VALIGN="TOP"><ROW><ENTRY COLNAME="COL1"><PARAC>No specific</PARAC><PARA>No specific</PARA></ENTRY><ENTRY COLNAME="COL2"><PARAC>AR</PARAC><PARA>AR</PARA></ENTRY><ENTRY COLNAME="COL3"><PARAC><STDNAME>ACCESS PLATFORM 4M (13 FT)-ADJUSTABLE</STDNAME></PARAC><PARA><STDNAME>ACCESS PLATFORM 4M (13 FT)-ADJUSTABLE</STDNAME></PARA></ENTRY></ROW><ROW><ENTRY COLNAME="COL1"><PARAC>No specific</PARAC><PARA>No specific</PARA></ENTRY><ENTRY COLNAME="COL2"><PARAC>AR</PARAC><PARA>AR</PARA></ENTRY><ENTRY COLNAME="COL3"><PARAC><STDNAME>SAFETY BARRIER(S)</STDNAME></PARAC><PARA><STDNAME>SAFETY BARRIER(S)</STDNAME></PARA></ENTRY></ROW><ROW><ENTRY COLNAME="COL1"><PARAC>No specific</PARAC><PARA>No specific</PARA></ENTRY><ENTRY COLNAME="COL2"><PARAC>AR</PARAC><PARA>AR</PARA></ENTRY><ENTRY COLNAME="COL3"><PARAC><STDNAME>SAFETY CLIP - CIRCUIT BREAKER</STDNAME></PARAC><PARA><STDNAME>SAFETY CLIP - CIRCUIT BREAKER</STDNAME></PARA></ENTRY></ROW><ROW><ENTRY COLNAME="COL1"><PARAC>No specific</PARAC><PARA>No specific</PARA></ENTRY><ENTRY COLNAME="COL2"><PARAC>AR</PARAC><PARA>AR</PARA></ENTRY><ENTRY COLNAME="COL3"><PARAC><STDNAME>WARNING NOTICE(S)</STDNAME></PARAC><PARA><STDNAME>WARNING NOTICE(S)</STDNAME></PARA></ENTRY></ROW></TBODY></TGROUP></TABLE></L1ITEM><L1ITEM><PARAC>Work Zones and Access Panels</PARAC><PARA>Work Zones and Access Panels</PARA><TABLE><TGROUP ALIGN="LEFT" CHAR="" CHAROFF="50" COLS="2"><COLSPEC COLNAME="COL1" COLWIDTH="26*"/><COLSPEC COLNAME="COL2" COLWIDTH="53*"/><SPANSPEC ALIGN="CENTER" NAMEEND="COL2" NAMEST="COL1" SPANNAME="WHOLE"/><THEAD VALIGN="BOTTOM"><ROW><ENTRY COLNAME="COL1"><PARAC>ZONE/ACCESS</PARAC><PARA>ZONE/ACCESS</PARA></ENTRY><ENTRY COLNAME="COL2"><PARAC>ZONE DESCRIPTION</PARAC><PARA>ZONE DESCRIPTION</PARA></ENTRY></ROW></THEAD><TBODY VALIGN="TOP"><ROW><ENTRY COLNAME="COL1"><PARAC><ZONE>210</ZONE></PARAC><PARA><ZONE>210</ZONE></PARA></ENTRY><ENTRY COLNAME="COL2"><PARAC>CKPT,FWD COMPT BHD TO FLT COMPT BULKHEAD</PARAC><PARA>CKPT,FWD COMPT BHD TO FLT COMPT BULKHEAD</PARA></ENTRY></ROW><ROW><ENTRY COLNAME="COL1"><PARAC><ZONE>522</ZONE></PARAC><PARA><ZONE>522</ZONE></PARA></ENTRY><ENTRY COLNAME="COL2"><PARAC>OUTBOARD FIXED L.E STRUCTURE</PARAC><PARA>OUTBOARD FIXED L.E STRUCTURE</PARA></ENTRY></ROW><ROW><ENTRY COLNAME="COL1"><PARAC><ZONE>622</ZONE></PARAC><PARA><ZONE>622</ZONE></PARA></ENTRY><ENTRY COLNAME="COL2"><PARAC>OUTBOARD FIXED L.E STRUCTURE</PARAC><PARA>OUTBOARD FIXED L.E STRUCTURE</PARA></ENTRY></ROW><ROW><ENTRY SPANNAME="WHOLE"><PARAC>FOR<EIN TYPE="EXACT">10-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 2)</PARAC><PARA>FOR<EIN TYPE="EXACT">10-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 2)</PARA></ENTRY></ROW><ROW><ENTRY COLNAME="COL1"><PARAC><PAN PANTYPE="accpan">622AT</PAN></PARAC><PARA><PAN PANTYPE="accpan">622AT</PAN></PARA></ENTRY></ROW><ROW><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/><ENTRY COLNAME="COL1"><PARAC><PAN PANTYPE="accpan">622AB</PAN></PARAC><PARA><PAN PANTYPE="accpan">622AB</PAN></PARA></ENTRY></ROW><ROW><EFFECT EFFRG="151200 251300"/><ENTRY COLNAME="COL1"><PARAC><PAN PANTYPE="accpan">622XB</PAN></PARAC><PARA><PAN PANTYPE="accpan">622XB</PAN></PARA></ENTRY></ROW><ROW><ENTRY SPANNAME="WHOLE"><PARAC>FOR<EIN TYPE="EXACT">9-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 1)</PARAC><PARA>FOR<EIN TYPE="EXACT">9-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 1)</PARA></ENTRY></ROW><ROW><ENTRY COLNAME="COL1"><PARAC><PAN PANTYPE="accpan">522AT</PAN></PARAC><PARA><PAN PANTYPE="accpan">522AT</PAN></PARA></ENTRY></ROW><ROW><EFFECT EFFRG="151200 251300"/><ENTRY COLNAME="COL1"><PARAC><PAN PANTYPE="accpan">522XB</PAN></PARAC><PARA><PAN PANTYPE="accpan">522XB</PAN></PARA></ENTRY></ROW><ROW><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/><ENTRY COLNAME="COL1"><PARAC><PAN PANTYPE="accpan">522AB</PAN></PARAC><PARA><PAN PANTYPE="accpan">522AB</PAN></PARA></ENTRY></ROW></TBODY></TGROUP></TABLE></L1ITEM></LIST1></PRETOPIC></TFMATR><TOPIC><TITLEC>Job Set-up</TITLEC><TITLE>Job Set-up</TITLE><SUBTASK CHAPNBR="28" CHG="U" CONFLTR="A" CONFNBR="00" FUNC="941" KEY="EN28240094106600001" PGBLKNBR="05" REVDATE="20230501" SECTNBR="24" SEQ="066" SUBJNBR="00"><EFFECT EFFRG="001999"/><LIST1><L1ITEM><PARAC>Safety Precautions</PARAC><PARA>Safety Precautions</PARA><LIST2><L2ITEM><WARNING><PARAC>OBEY THE FUEL SAFETY PROCEDURES. THIS CAN PREVENT INJURY AND DAMAGE.</PARAC><PARA>OBEY THE FUEL SAFETY PROCEDURES. THIS CAN PREVENT INJURY AND DAMAGE.</PARA></WARNING><PARAC>You must obey the fuel safety procedures when you do work on the fuel system<REFBLOCK>28-00-00-910-001<REFINT REFID="EN28000091000100"><EFFECT EFFRG="001999"/>28-00-00-910-001-A</REFINT></REFBLOCK>.</PARAC><PARA>You must obey the fuel safety procedures when you do work on the fuel system<REFBLOCK>28-00-00-910-001<REFINT REFID="EN28000091000100"><EFFECT EFFRG="001999"/>28-00-00-910-001-A</REFINT></REFBLOCK>.</PARA></L2ITEM><L2ITEM><PARAC>As necessary, use the applicable SAFETY BARRIER(S), specified by the operator's instructions and your local regulations.</PARAC><PARA>As necessary, use the applicable SAFETY BARRIER(S), specified by the operator's instructions and your local regulations.</PARA></L2ITEM></LIST2></L1ITEM></LIST1><SIGNOFF CK-LEVEL="B"/></SUBTASK><SUBTASK CHAPNBR="28" CHG="U" CONFLTR="A" CONFNBR="00" FUNC="860" KEY="EN28240086008000001" PGBLKNBR="05" REVDATE="20231101" SECTNBR="24" SEQ="080" SUBJNBR="00"><EFFECT EFFRG="001999"/><LIST1><L1ITEM><PARAC>Aircraft Maintenance Configuration</PARAC><PARA>Aircraft Maintenance Configuration</PARA><LIST2><L2ITEM><PARAC>On panel 20VU, make sure that ENG 1 and ENG 2 FIRE pushbutton switches are in closed and guarded position.</PARAC><PARA>On panel 20VU, make sure that ENG 1 and ENG 2 FIRE pushbutton switches are in closed and guarded position.</PARA></L2ITEM><L2ITEM><PARAC>On panel 115VU, make sure that ENG/MASTER 1 and ENG/MASTER 2 switches are in OFF position.</PARAC><PARA>On panel 115VU, make sure that ENG/MASTER 1 and ENG/MASTER 2 switches are in OFF position.</PARA></L2ITEM><L2ITEM><PARAC>Put the<STDNAME>WARNING NOTICE(S)</STDNAME>in position on panel 20VU to tell persons not to operate ENG 1 and ENG 2 FIRE pushbutton switches.</PARAC><PARA>Put the<STDNAME>WARNING NOTICE(S)</STDNAME>in position on panel 20VU to tell persons not to operate ENG 1 and ENG 2 FIRE pushbutton switches.</PARA></L2ITEM><L2ITEM><PARAC>Put the<STDNAME>WARNING NOTICE(S)</STDNAME>in position on panel 115VU to tell persons not to operate ENG/MASTER 1 and ENG/MASTER 2 switches.</PARAC><PARA>Put the<STDNAME>WARNING NOTICE(S)</STDNAME>in position on panel 115VU to tell persons not to operate ENG/MASTER 1 and ENG/MASTER 2 switches.</PARA></L2ITEM><L2ITEM><PARAC>Fully extend the slats<REFBLOCK>27-80-00-866-004<REFINT REFID="EN27800086600400"><EFFECT EFFRG="001999"/>27-80-00-866-004-A</REFINT></REFBLOCK>.</PARAC><PARA>Fully extend the slats<REFBLOCK>27-80-00-866-004<REFINT REFID="EN27800086600400"><EFFECT EFFRG="001999"/>27-80-00-866-004-A</REFINT></REFBLOCK>.</PARA></L2ITEM><L2ITEM><PARAC>Make sure that the<TED><TOOLNBR>98D27803000000</TOOLNBR><TOOLNAME>LOCKING TOOL-FLAP SLAT LEVER,INT. POSITION</TOOLNAME></TED>is installed on the slat/flap control lever.</PARAC><PARA>Make sure that the<TED><TOOLNBR>98D27803000000</TOOLNBR><TOOLNAME>LOCKING TOOL-FLAP SLAT LEVER,INT. POSITION</TOOLNAME></TED>is installed on the slat/flap control lever.</PARA></L2ITEM><L2ITEM><PARAC>Put the<STDNAME>WARNING NOTICE(S)</STDNAME>in position to tell persons not to operate the slats.</PARAC><PARA>Put the<STDNAME>WARNING NOTICE(S)</STDNAME>in position to tell persons not to operate the slats.</PARA></L2ITEM><L2ITEM><PARAC>Make sure that the APU does not operate and there is no bleed air.</PARAC><PARA>Make sure that the APU does not operate and there is no bleed air.</PARA></L2ITEM><L2ITEM><PARAC>Energize the aircraft electrical circuits</PARAC><PARA>Energize the aircraft electrical circuits</PARA><PARAC><REFBLOCK>24-41-00-861-002<REFINT REFID="EN24410086100200"><EFFECT EFFRG="001999"/>24-41-00-861-002-A</REFINT><REFINT REFID="EN24410086100201"><EFFECT EFFRG="001999"/>24-41-00-861-002-A-01</REFINT><REFINT REFID="EN24410086100202"><EFFECT EFFRG="001999"/>24-41-00-861-002-A-02</REFINT></REFBLOCK>.</PARAC><PARA><REFBLOCK>24-41-00-861-002<REFINT REFID="EN24410086100200"><EFFECT EFFRG="001999"/>24-41-00-861-002-A</REFINT><REFINT REFID="EN24410086100201"><EFFECT EFFRG="001999"/>24-41-00-861-002-A-01</REFINT><REFINT REFID="EN24410086100202"><EFFECT EFFRG="001999"/>24-41-00-861-002-A-02</REFINT></REFBLOCK>.</PARA></L2ITEM><L2ITEM><PARAC>Do the EIS start procedure (upper ECAM DU and lower ECAM DU only)</PARAC><PARA>Do the EIS start procedure (upper ECAM DU and lower ECAM DU only)</PARA><PARAC><REFBLOCK>31-60-00-860-001<REFINT REFID="EN31600086000100"><EFFECT EFFRG="001999"/>31-60-00-860-001-A</REFINT></REFBLOCK>.</PARAC><PARA><REFBLOCK>31-60-00-860-001<REFINT REFID="EN31600086000100"><EFFECT EFFRG="001999"/>31-60-00-860-001-A</REFINT></REFBLOCK>.</PARA></L2ITEM><L2ITEM><PARAC>On ECAM Control Panel (CP) 11VU, push the FUEL pushbutton switches to get the FUEL page on the ECAM lower DU.</PARAC><PARA>On ECAM Control Panel (CP) 11VU, push the FUEL pushbutton switches to get the FUEL page on the ECAM lower DU.</PARA></L2ITEM><L2ITEM><PARAC>On the FUEL page, make sure that the cross-feed valve indication shows green cross-line (valve closed).</PARAC><PARA>On the FUEL page, make sure that the cross-feed valve indication shows green cross-line (valve closed).</PARA></L2ITEM></LIST2></L1ITEM></LIST1><SIGNOFF CK-LEVEL="B"/></SUBTASK><SUBTASK CHAPNBR="28" CHG="U" CONFLTR="A" CONFNBR="00" FUNC="865" KEY="EN28240086508500001" PGBLKNBR="05" REVDATE="20230201" SECTNBR="24" SEQ="085" SUBJNBR="00"><EFFECT EFFRG="001999"/><LIST1><L1ITEM><PARAC>Make sure that this (these) circuit breaker(s) is (are) closed:</PARAC><PARA>Make sure that this (these) circuit breaker(s) is (are) closed:</PARA><CBLST ACTION="verif-close" CHKSUM="E4670E60"><CBSUBLST><CBDATA><EFFECT EFFRG="001999"/><CB CBTYPE="elmec">2-QG</CB><CBNAME>FUEL/LP VALVE/MOT1/ENG2</CBNAME><PAN PANTYPE="elec">49VU</PAN><CBLOC>A09</CBLOC></CBDATA><CBDATA><EFFECT EFFRG="001999"/><CB CBTYPE="elmec">1-QG</CB><CBNAME>FUEL/LP VALVE/MOT1/ENG1</CBNAME><PAN PANTYPE="elec">49VU</PAN><CBLOC>A08</CBLOC></CBDATA><CBDATA><EFFECT EFFRG="001999"/><CB CBTYPE="elmec">4-QG</CB><CBNAME>FUEL/LP VALVE/MOT2/ENG2</CBNAME><PAN PANTYPE="elec">121VU</PAN><CBLOC>M26</CBLOC></CBDATA><CBDATA><EFFECT EFFRG="001999"/><CB CBTYPE="elmec">3-QG</CB><CBNAME>FUEL/LP VALVE/MOT2/ENG1</CBNAME><PAN PANTYPE="elec">121VU</PAN><CBLOC>M25</CBLOC></CBDATA></CBSUBLST></CBLST></L1ITEM></LIST1><SIGNOFF CK-LEVEL="B"/></SUBTASK><SUBTASK CHAPNBR="28" CHG="U" CONFLTR="A" CONFNBR="00" FUNC="865" KEY="EN28240086508800001" PGBLKNBR="05" REVDATE="20240201" SECTNBR="24" SEQ="088" SUBJNBR="00"><EFFECT EFFRG="001999"/><LIST1><L1ITEM><PARAC>Open, safety and tag the circuit breaker(s) that follow(s). Use the SAFETY CLIP - CIRCUIT BREAKER as necessary.</PARAC><PARA>Open, safety and tag the circuit breaker(s) that follow(s). Use the SAFETY CLIP - CIRCUIT BREAKER as necessary.</PARA><CBLST ACTION="open" CHKSUM="55F7A769"><CBSUBLST><CBDATA><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126200"/><CB CBTYPE="elmec">5-CV</CB><CBNAME>FLIGHT CONTROLS/SLT/CTL AND MONG/SYS1</CBNAME><PAN PANTYPE="elec">49VU</PAN><CBLOC>B06</CBLOC></CBDATA><CBDATA><EFFECT EFFRG="251300"/><CB CBTYPE="elmec">5-CV</CB><CBNAME>FLIGHT CONTROLS/SLT/CTL AND MONG/SYS1</CBNAME><PAN PANTYPE="elec">49VU</PAN><CBLOC>B01</CBLOC></CBDATA><CBDATA><EFFECT EFFRG="001999"/><CB CBTYPE="elmec">7-CV</CB><CBNAME>FLIGHT CONTROLS/SLT/CTL/SYS2</CBNAME><PAN PANTYPE="elec">121VU</PAN><CBLOC>R21</CBLOC></CBDATA></CBSUBLST></CBLST></L1ITEM></LIST1><SIGNOFF CK-LEVEL="B"/></SUBTASK><SUBTASK CHAPNBR="28" CHG="U" CONFLTR="A" CONFNBR="00" FUNC="010" KEY="EN28240001006100001" PGBLKNBR="05" REVDATE="20220501" SECTNBR="24" SEQ="061" SUBJNBR="00"><EFFECT EFFRG="151200 251300"/><LIST1><L1ITEM><PARAC>Get Access</PARAC><PARA>Get Access</PARA><LIST2><L2ITEM><PARAC>Put the<STDNAME>ACCESS PLATFORM 4M (13 FT)-ADJUSTABLE</STDNAME>in position below the applicable zone 522(622).</PARAC><PARA>Put the<STDNAME>ACCESS PLATFORM 4M (13 FT)-ADJUSTABLE</STDNAME>in position below the applicable zone 522(622).</PARA></L2ITEM><L2ITEM><PARAC>Remove the applicable access panel:</PARAC><PARA>Remove the applicable access panel:</PARA><LIST3><L3ITEM><PARAC>FOR<EIN TYPE="EXACT">9-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 1)</PARAC><PARA>FOR<EIN TYPE="EXACT">9-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 1)</PARA><PARAC>Remove<PAN PANTYPE="accpan">522AT</PAN><REFBLOCK>57-41-37-000-003<REFINT REFID="EN57413700000300"><EFFECT EFFRG="151200 251300"/>57-41-37-000-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">522XB</PAN><REFBLOCK>57-41-37-000-004<REFINT REFID="EN57413700000400"><EFFECT EFFRG="151200 251300"/>57-41-37-000-004-A</REFINT></REFBLOCK>.</PARAC><PARA>Remove<PAN PANTYPE="accpan">522AT</PAN><REFBLOCK>57-41-37-000-003<REFINT REFID="EN57413700000300"><EFFECT EFFRG="151200 251300"/>57-41-37-000-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">522XB</PAN><REFBLOCK>57-41-37-000-004<REFINT REFID="EN57413700000400"><EFFECT EFFRG="151200 251300"/>57-41-37-000-004-A</REFINT></REFBLOCK>.</PARA></L3ITEM><L3ITEM><PARAC>FOR<EIN TYPE="EXACT">10-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 2)</PARAC><PARA>FOR<EIN TYPE="EXACT">10-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 2)</PARA><PARAC>Remove<PAN PANTYPE="accpan">622AT</PAN><REFBLOCK>57-41-37-000-003<REFINT REFID="EN57413700000300"><EFFECT EFFRG="151200 251300"/>57-41-37-000-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">622XB</PAN><REFBLOCK>57-41-37-000-004<REFINT REFID="EN57413700000400"><EFFECT EFFRG="151200 251300"/>57-41-37-000-004-A</REFINT></REFBLOCK>.</PARAC><PARA>Remove<PAN PANTYPE="accpan">622AT</PAN><REFBLOCK>57-41-37-000-003<REFINT REFID="EN57413700000300"><EFFECT EFFRG="151200 251300"/>57-41-37-000-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">622XB</PAN><REFBLOCK>57-41-37-000-004<REFINT REFID="EN57413700000400"><EFFECT EFFRG="151200 251300"/>57-41-37-000-004-A</REFINT></REFBLOCK>.</PARA></L3ITEM></LIST3></L2ITEM></LIST2></L1ITEM></LIST1><SIGNOFF CK-LEVEL="B"/></SUBTASK><SUBTASK CHAPNBR="28" CHG="U" CONFLTR="B" CONFNBR="00" FUNC="010" KEY="EN28240001006100002" PGBLKNBR="05" REVDATE="20240201" SECTNBR="24" SEQ="061" SUBJNBR="00"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/><LIST1><L1ITEM><PARAC>Get Access</PARAC><PARA>Get Access</PARA><LIST2><L2ITEM><PARAC>Put the<STDNAME>ACCESS PLATFORM 4M (13 FT)-ADJUSTABLE</STDNAME>in position below applicable zone 522 (622).</PARAC><PARA>Put the<STDNAME>ACCESS PLATFORM 4M (13 FT)-ADJUSTABLE</STDNAME>in position below applicable zone 522 (622).</PARA></L2ITEM><L2ITEM><PARAC>Remove the applicable access panel:</PARAC><PARA>Remove the applicable access panel:</PARA><LIST3><L3ITEM><PARAC>FOR<EIN TYPE="EXACT">9-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 1)</PARAC><PARA>FOR<EIN TYPE="EXACT">9-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 1)</PARA><PARAC>Remove<PAN PANTYPE="accpan">522AT</PAN><REFBLOCK>57-41-37-000-003<REFINT REFID="EN57413700000300"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-000-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">522AB</PAN><REFBLOCK>57-41-37-000-004<REFINT REFID="EN57413700000400"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-000-004-A</REFINT></REFBLOCK>.</PARAC><PARA>Remove<PAN PANTYPE="accpan">522AT</PAN><REFBLOCK>57-41-37-000-003<REFINT REFID="EN57413700000300"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-000-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">522AB</PAN><REFBLOCK>57-41-37-000-004<REFINT REFID="EN57413700000400"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-000-004-A</REFINT></REFBLOCK>.</PARA></L3ITEM><L3ITEM><PARAC>FOR<EIN TYPE="EXACT">10-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 2)</PARAC><PARA>FOR<EIN TYPE="EXACT">10-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 2)</PARA><PARAC>Remove<PAN PANTYPE="accpan">622AT</PAN><REFBLOCK>57-41-37-000-003<REFINT REFID="EN57413700000300"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-000-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">622AB</PAN><REFBLOCK>57-41-37-000-004<REFINT REFID="EN57413700000400"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-000-004-A</REFINT></REFBLOCK>.</PARAC><PARA>Remove<PAN PANTYPE="accpan">622AT</PAN><REFBLOCK>57-41-37-000-003<REFINT REFID="EN57413700000300"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-000-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">622AB</PAN><REFBLOCK>57-41-37-000-004<REFINT REFID="EN57413700000400"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-000-004-A</REFINT></REFBLOCK>.</PARA></L3ITEM></LIST3></L2ITEM></LIST2></L1ITEM></LIST1><SIGNOFF CK-LEVEL="B"/></SUBTASK></TOPIC><TOPIC><TITLEC>Procedure</TITLEC><TITLE>Procedure</TITLE><GRPHCREF REFID="EN28240099100200002" SHOWNOW="0"><EFFECT EFFRG="001003 009011 015020 022044"/>Component Location - Cockpit</GRPHCREF><GRPHCREF REFID="EN28240099100200004" SHOWNOW="0"><EFFECT EFFRG="046099 101114 116116 120124 126200 251300"/>Component Location - Cockpit</GRPHCREF><SUBTASK CHAPNBR="28" CHG="U" CONFLTR="A" CONFNBR="00" FUNC="710" KEY="EN28240071005800001" PGBLKNBR="05" REVDATE="20211101" SECTNBR="24" SEQ="058" SUBJNBR="00"><EFFECT EFFRG="001999"/><LIST1><L1ITEM><PARAC>Operational Check of LP Shut Off Valves including Operation by Individual Motors</PARAC><PARA>Operational Check of LP Shut Off Valves including Operation by Individual Motors</PARA><LIST2><L2ITEM><PARAC>In the test that follows, on each operation of the LP valve:</PARAC><PARA>In the test that follows, on each operation of the LP valve:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Make sure that the see/feel indicator operates smoothly from the valve open (valve closed) position to the valve closed (valve open) position.</PARAC><PARA>Make sure that the see/feel indicator operates smoothly from the valve open (valve closed) position to the valve closed (valve open) position.</PARA></UNLITEM><UNLITEM><PARAC>Make sure that the see/feel indicator marks are accurately aligned with the valve open position.</PARAC><PARA>Make sure that the see/feel indicator marks are accurately aligned with the valve open position.</PARA></UNLITEM><UNLITEM><PARAC>Make sure that the see/feel indicator marks are at 90 deg (90 deg) to each other with the valve closed position.</PARAC><PARA>Make sure that the see/feel indicator marks are at 90 deg (90 deg) to each other with the valve closed position.</PARA></UNLITEM></UNLIST></L2ITEM><L2ITEM><PARAC>For valve 12QM, do this test:</PARAC><PARA>For valve 12QM, do this test:</PARA><TABLE><TGROUP ALIGN="LEFT" CHAR="" CHAROFF="50" COLS="2"><COLSPEC COLNAME="COL1" COLWIDTH="3.9*"/><COLSPEC COLNAME="COL2" COLWIDTH="4.0*"/><SPANSPEC ALIGN="CENTER" NAMEEND="COL2" NAMEST="COL1" SPANNAME="WHOLE"/><THEAD VALIGN="BOTTOM"><ROW><ENTRY ALIGN="CENTER" COLNAME="COL1" VALIGN="TOP"><PARAC>ACTION</PARAC><PARA>ACTION</PARA></ENTRY><ENTRY ALIGN="CENTER" COLNAME="COL2" VALIGN="TOP"><PARAC>RESULT</PARAC><PARA>RESULT</PARA></ENTRY></ROW></THEAD><TBODY VALIGN="TOP"><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>1.On panel 121VU:</PARAC><PARA>1.On panel 121VU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Open, safety and tag circuit breaker 3QG.</PARAC><PARA>Open, safety and tag circuit breaker 3QG.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>ENG 1 LP valve MOTOR 2 SUPPLY and CONTROL are isolated.</PARAC><PARA>ENG 1 LP valve MOTOR 2 SUPPLY and CONTROL are isolated.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC></PARAC><PARA></PARA></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC>On the ECAM lower DU:</PARAC><PARA>On the ECAM lower DU:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The LEFT LP valve indication shows that the valve is closed (amber cross-line).</PARAC><PARA>The LEFT LP valve indication shows that the valve is closed (amber cross-line).</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>2.On LP-valve actuator 9QG:</PARAC><PARA>2.On LP-valve actuator 9QG:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Examine the position of the mechanical see/feel indicator.</PARAC><PARA>Examine the position of the mechanical see/feel indicator.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The indicator shows that the valve is closed.</PARAC><PARA>The indicator shows that the valve is closed.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>3.On panel 115VU:</PARAC><PARA>3.On panel 115VU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Set ENG/MASTER 1 switch to the ON position.</PARAC><PARA>Set ENG/MASTER 1 switch to the ON position.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC>On the ECAM lower DU:</PARAC><PARA>On the ECAM lower DU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The LEFT LP valve indication shows that the valve is open (green in-line).</PARAC><PARA>The LEFT LP valve indication shows that the valve is open (green in-line).</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>4.On LP-valve actuator 9QG:</PARAC><PARA>4.On LP-valve actuator 9QG:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Listen to the actuator motor and measure the time that it operates for as the LP valve opens.</PARAC><PARA>Listen to the actuator motor and measure the time that it operates for as the LP valve opens.</PARA></UNLITEM><UNLITEM><PARAC>Examine the position of the mechanical see/feel indicator.</PARAC><PARA>Examine the position of the mechanical see/feel indicator.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The actuator motor must not operate for more than 5 seconds.</PARAC><PARA>The actuator motor must not operate for more than 5 seconds.</PARA></UNLITEM><UNLITEM><PARAC>The indicator shows that the valve is open.</PARAC><PARA>The indicator shows that the valve is open.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>5.On panel 115VU:</PARAC><PARA>5.On panel 115VU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Set ENG/MASTER 1 switch to the OFF position.</PARAC><PARA>Set ENG/MASTER 1 switch to the OFF position.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC>On the ECAM lower DU:</PARAC><PARA>On the ECAM lower DU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The LEFT LP valve indication shows that the valve is closed (amber cross-line).</PARAC><PARA>The LEFT LP valve indication shows that the valve is closed (amber cross-line).</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>6.On LP-valve actuator 9QG:</PARAC><PARA>6.On LP-valve actuator 9QG:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Listen to the actuator motor and measure the time that it operates for as the LP valve closes.</PARAC><PARA>Listen to the actuator motor and measure the time that it operates for as the LP valve closes.</PARA></UNLITEM><UNLITEM><PARAC>Examine the position of the mechanical see/feel indicator.</PARAC><PARA>Examine the position of the mechanical see/feel indicator.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The actuator motor must not operate for more than 5 seconds.</PARAC><PARA>The actuator motor must not operate for more than 5 seconds.</PARA></UNLITEM><UNLITEM><PARAC>The indicator shows that the valve is closed.</PARAC><PARA>The indicator shows that the valve is closed.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>7.On panel 121VU:</PARAC><PARA>7.On panel 121VU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Remove the safety clip and the tag and close circuit breaker 3QG.</PARAC><PARA>Remove the safety clip and the tag and close circuit breaker 3QG.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>ENG 1 LP valve MOTOR 2 SUPPLY and CONTROL are energized.</PARAC><PARA>ENG 1 LP valve MOTOR 2 SUPPLY and CONTROL are energized.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>8.On panel 49VU:</PARAC><PARA>8.On panel 49VU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Open, safety and tag circuit breaker 1QG.</PARAC><PARA>Open, safety and tag circuit breaker 1QG.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>ENG 1 LP valve MOTOR 1 SUPPLY and CONTROL are isolated.</PARAC><PARA>ENG 1 LP valve MOTOR 1 SUPPLY and CONTROL are isolated.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC></PARAC><PARA></PARA></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC>On the ECAM lower DU:</PARAC><PARA>On the ECAM lower DU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The LEFT LP valve indication shows that the valve is closed (amber cross-line).</PARAC><PARA>The LEFT LP valve indication shows that the valve is closed (amber cross-line).</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>9.On LP-valve actuator 9QG:</PARAC><PARA>9.On LP-valve actuator 9QG:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Examine the position of the mechanical see/feel indicator.</PARAC><PARA>Examine the position of the mechanical see/feel indicator.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The indicator shows that the valve is closed.</PARAC><PARA>The indicator shows that the valve is closed.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>10.On panel 115VU:</PARAC><PARA>10.On panel 115VU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Set ENG/MASTER 1 switch to the ON position.</PARAC><PARA>Set ENG/MASTER 1 switch to the ON position.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC>On the ECAM lower DU:</PARAC><PARA>On the ECAM lower DU:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The LEFT LP valve indication shows that the valve is open (green in-line).</PARAC><PARA>The LEFT LP valve indication shows that the valve is open (green in-line).</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>11.On LP-valve actuator 9QG:</PARAC><PARA>11.On LP-valve actuator 9QG:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Listen to the actuator motor and measure the time that it operates for as the LP valve opens.</PARAC><PARA>Listen to the actuator motor and measure the time that it operates for as the LP valve opens.</PARA></UNLITEM><UNLITEM><PARAC>Examine the position of the mechanical see/feel indicator.</PARAC><PARA>Examine the position of the mechanical see/feel indicator.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The actuator motor must not operate for more than 5 seconds.</PARAC><PARA>The actuator motor must not operate for more than 5 seconds.</PARA></UNLITEM><UNLITEM><PARAC>The indicator shows that the valve is open.</PARAC><PARA>The indicator shows that the valve is open.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>12.On panel 115VU:</PARAC><PARA>12.On panel 115VU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Set ENG/MASTER 1 switch to the OFF position.</PARAC><PARA>Set ENG/MASTER 1 switch to the OFF position.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC>On the ECAM lower DU:</PARAC><PARA>On the ECAM lower DU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The LEFT LP valve indication shows that the valve is closed (amber cross-line).</PARAC><PARA>The LEFT LP valve indication shows that the valve is closed (amber cross-line).</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>13.On LP-valve actuator 9QG:</PARAC><PARA>13.On LP-valve actuator 9QG:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Listen to the actuator motor and measure the time that it operates for as the LP valve closes.</PARAC><PARA>Listen to the actuator motor and measure the time that it operates for as the LP valve closes.</PARA></UNLITEM><UNLITEM><PARAC>Examine the position of the mechanical see/feel indicator.</PARAC><PARA>Examine the position of the mechanical see/feel indicator.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The actuator motor must not operate for more than 5 seconds.</PARAC><PARA>The actuator motor must not operate for more than 5 seconds.</PARA></UNLITEM><UNLITEM><PARAC>The indicator shows that the valve is closed.</PARAC><PARA>The indicator shows that the valve is closed.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>14.On panel 49VU:</PARAC><PARA>14.On panel 49VU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Remove the safety clip and the tag and close circuit breaker 1QG.</PARAC><PARA>Remove the safety clip and the tag and close circuit breaker 1QG.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>ENG 1 LP valve MOTOR 1 SUPPLY and CONTROL are energized.</PARAC><PARA>ENG 1 LP valve MOTOR 1 SUPPLY and CONTROL are energized.</PARA></UNLITEM></UNLIST></ENTRY></ROW></TBODY></TGROUP></TABLE></L2ITEM><L2ITEM><PARAC>For valve 13QM, do this test:</PARAC><PARA>For valve 13QM, do this test:</PARA><TABLE><TGROUP ALIGN="LEFT" CHAR="" CHAROFF="50" COLS="2"><COLSPEC COLNAME="COL1" COLWIDTH="3.9*"/><COLSPEC COLNAME="COL2" COLWIDTH="4.0*"/><SPANSPEC ALIGN="CENTER" NAMEEND="COL2" NAMEST="COL1" SPANNAME="WHOLE"/><THEAD VALIGN="BOTTOM"><ROW><ENTRY ALIGN="CENTER" COLNAME="COL1" VALIGN="TOP"><PARAC>ACTION</PARAC><PARA>ACTION</PARA></ENTRY><ENTRY ALIGN="CENTER" COLNAME="COL2" VALIGN="TOP"><PARAC>RESULT</PARAC><PARA>RESULT</PARA></ENTRY></ROW></THEAD><TBODY VALIGN="TOP"><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>1.On panel 121VU:</PARAC><PARA>1.On panel 121VU:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Open, safety and tag circuit breaker 4QG.</PARAC><PARA>Open, safety and tag circuit breaker 4QG.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>ENG 2 LP valve MOTOR 2 SUPPLY and CONTROL are isolated.</PARAC><PARA>ENG 2 LP valve MOTOR 2 SUPPLY and CONTROL are isolated.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC></PARAC><PARA></PARA></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC>On the ECAM lower DU:</PARAC><PARA>On the ECAM lower DU:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The RIGHT LP valve indication shows that the valve is closed (amber cross-line).</PARAC><PARA>The RIGHT LP valve indication shows that the valve is closed (amber cross-line).</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>2.On LP-valve actuator 10QG:</PARAC><PARA>2.On LP-valve actuator 10QG:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Examine the position of the mechanical see/feel indicator.</PARAC><PARA>Examine the position of the mechanical see/feel indicator.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The indicator shows that the valve is closed.</PARAC><PARA>The indicator shows that the valve is closed.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>3.On panel 115VU:</PARAC><PARA>3.On panel 115VU:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Set ENG/MASTER 2 switch to the ON position.</PARAC><PARA>Set ENG/MASTER 2 switch to the ON position.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC>On the ECAM lower DU:</PARAC><PARA>On the ECAM lower DU:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The RIGHT LP valve indication shows that the valve is open (green in-line).</PARAC><PARA>The RIGHT LP valve indication shows that the valve is open (green in-line).</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>4.On LP-valve actuator 10QG:</PARAC><PARA>4.On LP-valve actuator 10QG:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Listen to the actuator motor and measure the time that it operates for as the LP valve opens.</PARAC><PARA>Listen to the actuator motor and measure the time that it operates for as the LP valve opens.</PARA></UNLITEM><UNLITEM><PARAC>Examine the position of the mechanical see/feel indicator.</PARAC><PARA>Examine the position of the mechanical see/feel indicator.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The actuator motor must not operate for more than 5 seconds.</PARAC><PARA>The actuator motor must not operate for more than 5 seconds.</PARA></UNLITEM><UNLITEM><PARAC>The indicator shows that the valve is open.</PARAC><PARA>The indicator shows that the valve is open.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>5.On panel 115VU:</PARAC><PARA>5.On panel 115VU:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Set ENG/MASTER 2 switch to the OFF position.</PARAC><PARA>Set ENG/MASTER 2 switch to the OFF position.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC>On the ECAM lower DU:</PARAC><PARA>On the ECAM lower DU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The RIGHT LP valve indication shows that the valve is closed (amber cross-line).</PARAC><PARA>The RIGHT LP valve indication shows that the valve is closed (amber cross-line).</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>6.On LP-valve actuator 10QG:</PARAC><PARA>6.On LP-valve actuator 10QG:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Listen to the actuator motor and measure the time that it operates for as the LP valve closes.</PARAC><PARA>Listen to the actuator motor and measure the time that it operates for as the LP valve closes.</PARA></UNLITEM><UNLITEM><PARAC>Examine the position of the mechanical see/feel indicator.</PARAC><PARA>Examine the position of the mechanical see/feel indicator.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The actuator motor must not operate for more than 5 seconds.</PARAC><PARA>The actuator motor must not operate for more than 5 seconds.</PARA></UNLITEM><UNLITEM><PARAC>The indicator shows that the valve is closed.</PARAC><PARA>The indicator shows that the valve is closed.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>7.On panel 121VU:</PARAC><PARA>7.On panel 121VU:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Remove the safety clip and the tag and close circuit breaker 4QG.</PARAC><PARA>Remove the safety clip and the tag and close circuit breaker 4QG.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>ENG 2 LP valve MOTOR 2 SUPPLY and CONTROL are energized.</PARAC><PARA>ENG 2 LP valve MOTOR 2 SUPPLY and CONTROL are energized.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>8.On panel 49VU:</PARAC><PARA>8.On panel 49VU:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Open, safety and tag circuit breaker 2QG.</PARAC><PARA>Open, safety and tag circuit breaker 2QG.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>ENG 2 LP valve MOTOR 1 SUPPLY and CONTROL are isolated.</PARAC><PARA>ENG 2 LP valve MOTOR 1 SUPPLY and CONTROL are isolated.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC></PARAC><PARA></PARA></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC>On the ECAM lower DU:</PARAC><PARA>On the ECAM lower DU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The RIGHT LP valve indication shows that the valve is closed (amber cross-line).</PARAC><PARA>The RIGHT LP valve indication shows that the valve is closed (amber cross-line).</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>9.On LP-valve actuator 10QG:</PARAC><PARA>9.On LP-valve actuator 10QG:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Examine the position of the mechanical see/feel indicator.</PARAC><PARA>Examine the position of the mechanical see/feel indicator.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The indicator shows that the valve is closed.</PARAC><PARA>The indicator shows that the valve is closed.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>10.On panel 115VU:</PARAC><PARA>10.On panel 115VU:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Set ENG/MASTER 2 switch to the ON position.</PARAC><PARA>Set ENG/MASTER 2 switch to the ON position.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC>On the ECAM lower DU:</PARAC><PARA>On the ECAM lower DU:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The RIGHT LP valve indication shows that the valve is open (green in-line).</PARAC><PARA>The RIGHT LP valve indication shows that the valve is open (green in-line).</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>11.On LP-valve actuator 10QG:</PARAC><PARA>11.On LP-valve actuator 10QG:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Listen to the actuator motor and measure the time that it operates for as the LP valve opens.</PARAC><PARA>Listen to the actuator motor and measure the time that it operates for as the LP valve opens.</PARA></UNLITEM><UNLITEM><PARAC>Examine the position of the mechanical see/feel indicator.</PARAC><PARA>Examine the position of the mechanical see/feel indicator.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The actuator motor must not operate for more than 5 seconds.</PARAC><PARA>The actuator motor must not operate for more than 5 seconds.</PARA></UNLITEM><UNLITEM><PARAC>The indicator shows that the valve is open.</PARAC><PARA>The indicator shows that the valve is open.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>12.On panel 115VU:</PARAC><PARA>12.On panel 115VU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Set ENG/MASTER 2 switch to the OFF position.</PARAC><PARA>Set ENG/MASTER 2 switch to the OFF position.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC>On the ECAM lower DU:</PARAC><PARA>On the ECAM lower DU:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The RIGHT LP valve indication shows that the valve is closed (amber cross-line).</PARAC><PARA>The RIGHT LP valve indication shows that the valve is closed (amber cross-line).</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>13.On LP-valve actuator 10QG:</PARAC><PARA>13.On LP-valve actuator 10QG:</PARA><PARAC/><PARA/><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Listen to the actuator motor and measure the time that it operates for as the LP valve closes.</PARAC><PARA>Listen to the actuator motor and measure the time that it operates for as the LP valve closes.</PARA></UNLITEM><UNLITEM><PARAC>Examine the position of the mechanical see/feel indicator.</PARAC><PARA>Examine the position of the mechanical see/feel indicator.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>The actuator motor must not operate for more than 5 seconds.</PARAC><PARA>The actuator motor must not operate for more than 5 seconds.</PARA></UNLITEM><UNLITEM><PARAC>The indicator shows that the valve is closed.</PARAC><PARA>The indicator shows that the valve is closed.</PARA></UNLITEM></UNLIST></ENTRY></ROW><ROW><ENTRY COLNAME="COL1" VALIGN="TOP"><PARAC>14.On panel 49VU:</PARAC><PARA>14.On panel 49VU:</PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>Remove the safety clip and the tag and close circuit breaker 2QG.</PARAC><PARA>Remove the safety clip and the tag and close circuit breaker 2QG.</PARA></UNLITEM></UNLIST></ENTRY><ENTRY COLNAME="COL2" VALIGN="TOP"><PARAC></PARAC><PARA></PARA><UNLIST BULLTYPE="BULLET"><UNLITEM><PARAC>ENG 2 LP valve MOTOR 1 SUPPLY and CONTROL are energized.</PARAC><PARA>ENG 2 LP valve MOTOR 1 SUPPLY and CONTROL are energized.</PARA></UNLITEM></UNLIST></ENTRY></ROW></TBODY></TGROUP></TABLE></L2ITEM></LIST2></L1ITEM></LIST1><SIGNOFF CK-LEVEL="B"/></SUBTASK></TOPIC><TOPIC><TITLEC>Close-up</TITLEC><TITLE>Close-up</TITLE><SUBTASK CHAPNBR="28" CHG="U" CONFLTR="A" CONFNBR="00" FUNC="410" KEY="EN28240041006300001" PGBLKNBR="05" REVDATE="20220501" SECTNBR="24" SEQ="063" SUBJNBR="00"><EFFECT EFFRG="151200 251300"/><LIST1><L1ITEM><PARAC>Close Access</PARAC><PARA>Close Access</PARA><LIST2><L2ITEM><PARAC>Install the applicable access panel:</PARAC><PARA>Install the applicable access panel:</PARA><LIST3><L3ITEM><PARAC>FOR<EIN TYPE="EXACT">9-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 1)</PARAC><PARA>FOR<EIN TYPE="EXACT">9-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 1)</PARA><PARAC>Install<PAN PANTYPE="accpan">522AT</PAN><REFBLOCK>57-41-37-400-003<REFINT REFID="EN57413740000300"><EFFECT EFFRG="151200 251300"/>57-41-37-400-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">522XB</PAN><REFBLOCK>57-41-37-400-002<REFINT REFID="EN57413740000200"><EFFECT EFFRG="151200 251300"/>57-41-37-400-002-A</REFINT></REFBLOCK>.</PARAC><PARA>Install<PAN PANTYPE="accpan">522AT</PAN><REFBLOCK>57-41-37-400-003<REFINT REFID="EN57413740000300"><EFFECT EFFRG="151200 251300"/>57-41-37-400-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">522XB</PAN><REFBLOCK>57-41-37-400-002<REFINT REFID="EN57413740000200"><EFFECT EFFRG="151200 251300"/>57-41-37-400-002-A</REFINT></REFBLOCK>.</PARA></L3ITEM><L3ITEM><PARAC>FOR<EIN TYPE="EXACT">10-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 2)</PARAC><PARA>FOR<EIN TYPE="EXACT">10-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 2)</PARA><PARAC>Install<PAN PANTYPE="accpan">622AT</PAN><REFBLOCK>57-41-37-400-003<REFINT REFID="EN57413740000300"><EFFECT EFFRG="151200 251300"/>57-41-37-400-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">622XB</PAN><REFBLOCK>57-41-37-400-002<REFINT REFID="EN57413740000200"><EFFECT EFFRG="151200 251300"/>57-41-37-400-002-A</REFINT></REFBLOCK>.</PARAC><PARA>Install<PAN PANTYPE="accpan">622AT</PAN><REFBLOCK>57-41-37-400-003<REFINT REFID="EN57413740000300"><EFFECT EFFRG="151200 251300"/>57-41-37-400-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">622XB</PAN><REFBLOCK>57-41-37-400-002<REFINT REFID="EN57413740000200"><EFFECT EFFRG="151200 251300"/>57-41-37-400-002-A</REFINT></REFBLOCK>.</PARA></L3ITEM></LIST3></L2ITEM></LIST2></L1ITEM></LIST1><SIGNOFF CK-LEVEL="B"/></SUBTASK><SUBTASK CHAPNBR="28" CHG="U" CONFLTR="B" CONFNBR="00" FUNC="410" KEY="EN28240041006300002" PGBLKNBR="05" REVDATE="20240201" SECTNBR="24" SEQ="063" SUBJNBR="00"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/><LIST1><L1ITEM><PARAC>Close Access</PARAC><PARA>Close Access</PARA><LIST2><L2ITEM><PARAC>Make sure that the work area is clean and clear of tools and other items.</PARAC><PARA>Make sure that the work area is clean and clear of tools and other items.</PARA></L2ITEM><L2ITEM><PARAC>Install the applicable access panel:</PARAC><PARA>Install the applicable access panel:</PARA><LIST3><L3ITEM><PARAC>FOR<EIN TYPE="EXACT">9-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 1)</PARAC><PARA>FOR<EIN TYPE="EXACT">9-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 1)</PARA><PARAC>Install<PAN PANTYPE="accpan">522AT</PAN><REFBLOCK>57-41-37-400-003<REFINT REFID="EN57413740000300"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-400-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">522AB</PAN><REFBLOCK>57-41-37-400-002<REFINT REFID="EN57413740000200"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-400-002-A</REFINT></REFBLOCK>.</PARAC><PARA>Install<PAN PANTYPE="accpan">522AT</PAN><REFBLOCK>57-41-37-400-003<REFINT REFID="EN57413740000300"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-400-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">522AB</PAN><REFBLOCK>57-41-37-400-002<REFINT REFID="EN57413740000200"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-400-002-A</REFINT></REFBLOCK>.</PARA></L3ITEM><L3ITEM><PARAC>FOR<EIN TYPE="EXACT">10-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 2)</PARAC><PARA>FOR<EIN TYPE="EXACT">10-QG</EIN>(ACTUATOR-LP FUEL VALVE, ENG 2)</PARA><PARAC>Install<PAN PANTYPE="accpan">622AT</PAN><REFBLOCK>57-41-37-400-003<REFINT REFID="EN57413740000300"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-400-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">622AB</PAN><REFBLOCK>57-41-37-400-002<REFINT REFID="EN57413740000200"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-400-002-A</REFINT></REFBLOCK>.</PARAC><PARA>Install<PAN PANTYPE="accpan">622AT</PAN><REFBLOCK>57-41-37-400-003<REFINT REFID="EN57413740000300"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-400-003-A</REFINT></REFBLOCK>or<PAN PANTYPE="accpan">622AB</PAN><REFBLOCK>57-41-37-400-002<REFINT REFID="EN57413740000200"><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126150"/>57-41-37-400-002-A</REFINT></REFBLOCK>.</PARA></L3ITEM></LIST3></L2ITEM></LIST2></L1ITEM></LIST1><SIGNOFF CK-LEVEL="B"/></SUBTASK><SUBTASK CHAPNBR="28" CHG="U" CONFLTR="A" CONFNBR="00" FUNC="865" KEY="EN28240086508900001" PGBLKNBR="05" REVDATE="20240201" SECTNBR="24" SEQ="089" SUBJNBR="00"><EFFECT EFFRG="001999"/><LIST1><L1ITEM><PARAC>Remove the SAFETY CLIP - CIRCUIT BREAKER and the tag(s) and close this (these) circuit breaker(s):</PARAC><PARA>Remove the SAFETY CLIP - CIRCUIT BREAKER and the tag(s) and close this (these) circuit breaker(s):</PARA><CBLST ACTION="close" CHKSUM="55F7A769"><CBSUBLST><CBDATA><EFFECT EFFRG="001003 009011 015020 022044 046099 101114 116116 120124 126200"/><CB CBTYPE="elmec">5-CV</CB><CBNAME>FLIGHT CONTROLS/SLT/CTL AND MONG/SYS1</CBNAME><PAN PANTYPE="elec">49VU</PAN><CBLOC>B06</CBLOC></CBDATA><CBDATA><EFFECT EFFRG="251300"/><CB CBTYPE="elmec">5-CV</CB><CBNAME>FLIGHT CONTROLS/SLT/CTL AND MONG/SYS1</CBNAME><PAN PANTYPE="elec">49VU</PAN><CBLOC>B01</CBLOC></CBDATA><CBDATA><EFFECT EFFRG="001999"/><CB CBTYPE="elmec">7-CV</CB><CBNAME>FLIGHT CONTROLS/SLT/CTL/SYS2</CBNAME><PAN PANTYPE="elec">121VU</PAN><CBLOC>R21</CBLOC></CBDATA></CBSUBLST></CBLST></L1ITEM></LIST1><SIGNOFF CK-LEVEL="B"/></SUBTASK><SUBTASK CHAPNBR="28" CHG="U" CONFLTR="A" CONFNBR="00" FUNC="860" KEY="EN28240086008100001" PGBLKNBR="05" REVDATE="20190201" SECTNBR="24" SEQ="081" SUBJNBR="00"><EFFECT EFFRG="001999"/><LIST1><L1ITEM><PARAC>Aircraft Maintenance Configuration</PARAC><PARA>Aircraft Maintenance Configuration</PARA><LIST2><L2ITEM><PARAC>Fully retract the slats<REFBLOCK>27-80-00-866-005<REFINT REFID="EN27800086600500"><EFFECT EFFRG="001999"/>27-80-00-866-005-A</REFINT></REFBLOCK>.</PARAC><PARA>Fully retract the slats<REFBLOCK>27-80-00-866-005<REFINT REFID="EN27800086600500"><EFFECT EFFRG="001999"/>27-80-00-866-005-A</REFINT></REFBLOCK>.</PARA></L2ITEM><L2ITEM><PARAC>On the ECAM lower DU, make sure that the LP valves are closed.</PARAC><PARA>On the ECAM lower DU, make sure that the LP valves are closed.</PARA></L2ITEM><L2ITEM><PARAC>Do the EIS stop procedure<REFBLOCK>31-60-00-860-002<REFINT REFID="EN31600086000200"><EFFECT EFFRG="001999"/>31-60-00-860-002-A</REFINT></REFBLOCK>.</PARAC><PARA>Do the EIS stop procedure<REFBLOCK>31-60-00-860-002<REFINT REFID="EN31600086000200"><EFFECT EFFRG="001999"/>31-60-00-860-002-A</REFINT></REFBLOCK>.</PARA></L2ITEM><L2ITEM><PARAC>De-energize the aircraft electrical circuits<REFBLOCK>24-41-00-862-002<REFINT REFID="EN24410086200200"><EFFECT EFFRG="001999"/>24-41-00-862-002-A</REFINT><REFINT REFID="EN24410086200201"><EFFECT EFFRG="001999"/>24-41-00-862-002-A-01</REFINT><REFINT REFID="EN24410086200202"><EFFECT EFFRG="001999"/>24-41-00-862-002-A-02</REFINT></REFBLOCK>.</PARAC><PARA>De-energize the aircraft electrical circuits<REFBLOCK>24-41-00-862-002<REFINT REFID="EN24410086200200"><EFFECT EFFRG="001999"/>24-41-00-862-002-A</REFINT><REFINT REFID="EN24410086200201"><EFFECT EFFRG="001999"/>24-41-00-862-002-A-01</REFINT><REFINT REFID="EN24410086200202"><EFFECT EFFRG="001999"/>24-41-00-862-002-A-02</REFINT></REFBLOCK>.</PARA></L2ITEM></LIST2></L1ITEM></LIST1><SIGNOFF CK-LEVEL="B"/></SUBTASK><SUBTASK CHAPNBR="28" CHG="U" CONFLTR="A" CONFNBR="00" FUNC="942" KEY="EN28240094206400001" PGBLKNBR="05" REVDATE="20190201" SECTNBR="24" SEQ="064" SUBJNBR="00"><EFFECT EFFRG="001999"/><LIST1><L1ITEM><PARAC>Removal of Equipment</PARAC><PARA>Removal of Equipment</PARA><LIST2><L2ITEM><PARAC>Remove the SAFETY BARRIERS.</PARAC><PARA>Remove the SAFETY BARRIERS.</PARA></L2ITEM><L2ITEM><PARAC>Remove the access platform(s).</PARAC><PARA>Remove the access platform(s).</PARA></L2ITEM><L2ITEM><PARAC>Remove the WARNING NOTICE(S).</PARAC><PARA>Remove the WARNING NOTICE(S).</PARA></L2ITEM><L2ITEM><PARAC>Remove the ground support and maintenance equipment, the special and standard tools and all other items.</PARAC><PARA>Remove the ground support and maintenance equipment, the special and standard tools and all other items.</PARA></L2ITEM></LIST2></L1ITEM></LIST1><SIGNOFF CK-LEVEL="B"/></SUBTASK></TOPIC><GRAPHIC CHAPNBR="28" CHG="U" CONFLTR="B" CONFNBR="00" FUNC="991" KEY="EN28240099100200002" PGBLKNBR="05" REVDATE="20240501" SECTNBR="24" SEQ="00200" SUBJNBR="00"><EFFECT EFFRG="001003 009011 015020 022044"/><TITLE>Component Location - Cockpit</TITLE><SHEET CFNBR="C_N_MM_282400_5_AEM0_01_00" CHG="U" GNBR="n_mm_282400_5_aem0_01_00" IMGAREA="AP" KEY="EN28240099100200002_AEM001" REVDATE="20240501" SHEETNBR="1"><EFFECT EFFRG="001003 009011 015020 022044"/><TITLE>Component Location - Cockpit</TITLE></SHEET></GRAPHIC><GRAPHIC CHAPNBR="28" CHG="U" CONFLTR="D" CONFNBR="00" FUNC="991" KEY="EN28240099100200004" PGBLKNBR="05" REVDATE="20240501" SECTNBR="24" SEQ="00200" SUBJNBR="00"><EFFECT EFFRG="046099 101114 116116 120124 126200 251300"/><TITLE>Component Location - Cockpit</TITLE><SHEET CFNBR="C_N_MM_282400_5_SAM0_01_00" CHG="U" GNBR="n_mm_282400_5_sam0_01_00" IMGAREA="AP" KEY="EN28240099100200004_SAM001" REVDATE="20240501" SHEETNBR="1"><EFFECT EFFRG="046099 101114 116116 120124 126200 251300"/><TITLE>Component Location - Cockpit</TITLE></SHEET></GRAPHIC></CEP></JOBCARD>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
\ No newline at end of file
<template>
<n-button v-if="isVisible" v-bind="$attrs">
<!-- 插槽转发 -->
<template v-for="slotName in Object.keys($slots)" :key="slotName" #[slotName]="slotProps">
<slot :name="slotName" v-bind="slotProps || {}" />
</template>
</n-button>
</template>
<script setup lang="ts">
/**
* 全局通用权限按钮组件
* 对齐 SHTML 的 auth 指令逻辑,无权限不渲染
*/
interface Props {
/** 权限标识 (支持字符串或数组) */
auth?: string | string[]
/** 显隐控制 (对齐 SHTML 逻辑) */
display?: boolean
}
const props = withDefaults(defineProps<Props>(), {
display: true
})
const isVisible = computed(() => {
// 1. 如果 display 为 false,直接不显示
if (!props.display) {
return false
}
// 2. 如果没有定义 auth 属性,则默认展示
if (!props.auth || (Array.isArray(props.auth) && props.auth.length === 0)) {
return true
}
// 3. 校验权限
return true
})
</script>
<template>
<n-checkbox-group v-model:value="computedValue" v-bind="$attrs" @update:value="handleUpdateValue">
<n-grid v-if="gridCols" :cols="gridCols" :x-gap="20" :y-gap="10">
<n-gi v-for="opt in computedOptions" :key="String(opt.value)">
<n-checkbox :value="opt.value" :disabled="opt.disabled">
{{ opt.label }}
</n-checkbox>
</n-gi>
</n-grid>
<n-space v-else :size="spaceSize">
<n-checkbox v-for="opt in computedOptions" :key="String(opt.value)" :value="opt.value" :disabled="opt.disabled">
{{ opt.label }}
</n-checkbox>
</n-space>
</n-checkbox-group>
</template>
<script setup lang="ts">
import type { PropType } from 'vue'
const props = defineProps({
/** 绑定的值 (v-model) */
value: {
type: [Array, String] as PropType<any[] | string | null | undefined>,
default: () => []
},
modelValue: {
type: [Array, String] as PropType<any[] | string | null | undefined>,
default: () => []
},
/** 静态选项 */
options: {
type: Array as PropType<any[]>,
default: null
},
/** 严格模式:默认不开启(不开启时自动转字符串比对,不区分 1 和 '1') */
strict: {
type: Boolean,
default: false
},
/** 选项之间的间距 (n-space size) */
spaceSize: {
type: [String, Number, Array] as PropType<number | 'small' | 'medium' | 'large' | [number, number]>,
default: 'medium'
},
/** 网格布局列数,如果不传则使用 n-space */
gridCols: {
type: [Number, String] as PropType<number | string>,
default: 0
}
})
const emit = defineEmits(['update:value', 'update:modelValue', 'change'])
/**
* 值的标准化处理
*/
const normalizeValue = (v: any) => {
if (v === null || v === undefined || v === '') return null
return props.strict ? v : String(v)
}
const computedValue = computed<any>({
get: () => {
const val = props.modelValue ?? props.value ?? []
// 兼容性处理:如果传入的是非数组(如字符串或 null),则尝试转换为数组
let arrayVal: any[] = []
if (Array.isArray(val)) {
arrayVal = val
} else if (typeof val === 'string' && val !== '') {
arrayVal = val.split(',')
}
return arrayVal.map(normalizeValue).filter((v) => v !== null)
},
set: (val: any) => {
emit('update:value', val)
emit('update:modelValue', val)
}
})
const handleUpdateValue = (val: any) => {
emit('change', val)
}
const computedOptions = computed(() => {
let rawOptions: any[] = []
if (props.options) {
rawOptions = props.options
}
return rawOptions.map((opt: any) => {
const label = opt.TEXT || opt.name || opt.label || opt.REAL_NAME || opt.MENU_NAME || opt.title
const rawVal = opt.VALUE !== undefined ? opt.VALUE : (opt.id ?? opt.value ?? opt.PKID)
const value = normalizeValue(rawVal)
return {
...opt,
label,
value
}
})
})
</script>
<style scoped></style>
<template>
<n-date-picker
v-model:value="internalValue"
:type="type"
clearable
:shortcuts="showShortcuts ? (isRange ? RANGE_SHORTCUTS : undefined) : undefined"
class="w-full"
v-bind="$attrs"
@update:value="handleUpdate"
/>
</template>
<script setup lang="ts">
import dayjs from 'dayjs'
/**
* 统一通用的日期/时间选择组件 (CommonDatePicker)
*
* 强制规范:
* 1. 业务层统一使用字符串交互,避免 Date 对象或时间戳处理。
* 2. 范围选择建议绑定 v-model:start 和 v-model:end。
* 3. 单选建议绑定 v-model:value。
* 4. 自动补全 00:00:00/23:59:59 以符合后端查询习惯(仅在 range 模式下默认开启)。
*/
// v-model 绑定
const modelValue = defineModel<string | number | null | undefined>()
const customValue = defineModel<string | number | null | undefined>('value')
const start = defineModel<string | number | null | undefined>('start')
const end = defineModel<string | number | null | undefined>('end')
const props = withDefaults(
defineProps<{
/** 选择类型,参考 Naive UI: date, datetime, daterange, datetimerange, month, year, quarter */
type?: 'date' | 'datetime' | 'daterange' | 'datetimerange' | 'month' | 'year' | 'quarter'
/** 日期部分的格式化模板,默认 YYYY-MM-DD */
format?: string
/** 是否包含时分秒补全 (仅对 range 模式有效) */
withTimePadding?: boolean
/** 是否显示快捷面板 */
showShortcuts?: boolean
}>(),
{
type: 'date',
withTimePadding: true,
showShortcuts: true
}
)
// 是否为 range 模式
const isRange = computed(() => props.type.includes('range'))
// 内部驱动 n-date-picker 的时间戳状态
const internalValue = ref<number | [number, number] | null>(null)
/** 默认快捷面板配置 */
const RANGE_SHORTCUTS = {
今天: () => [dayjs().startOf('day').valueOf(), dayjs().endOf('day').valueOf()] as [number, number],
昨天: () => [dayjs().subtract(1, 'day').startOf('day').valueOf(), dayjs().subtract(1, 'day').endOf('day').valueOf()] as [number, number],
最近7: () => [dayjs().subtract(6, 'day').startOf('day').valueOf(), dayjs().endOf('day').valueOf()] as [number, number],
本月: () => [dayjs().startOf('month').valueOf(), dayjs().endOf('month').valueOf()] as [number, number],
最近30: () => [dayjs().subtract(29, 'day').startOf('day').valueOf(), dayjs().endOf('day').valueOf()] as [number, number]
}
/**
* 同步:从外部字符串同步到内部时间戳
*/
const instance = getCurrentInstance()
const hasValueBind = computed(() => {
const props = instance?.vnode.props || {}
return 'value' in props || 'onUpdate:value' in props
})
watch(
[modelValue, customValue, start, end],
() => {
if (isRange.value) {
if (start.value && end.value) {
const s = dayjs(start.value).valueOf()
const e = dayjs(end.value).valueOf()
if (!isNaN(s) && !isNaN(e)) {
if (!Array.isArray(internalValue.value) || (internalValue.value as any)[0] !== s || (internalValue.value as any)[1] !== e) {
internalValue.value = [s, e]
}
} else {
internalValue.value = null
}
} else if (!start.value && !end.value) {
internalValue.value = null
}
} else {
const activeVal = hasValueBind.value ? customValue.value : modelValue.value
if (activeVal) {
const v = dayjs(activeVal).valueOf()
if (!isNaN(v)) {
if (internalValue.value !== v) {
internalValue.value = v as any
}
} else {
internalValue.value = null
}
} else {
internalValue.value = null
}
}
},
{ immediate: true }
)
/**
* 处理更新:从内部时间戳格式化至外部字符串
*/
const handleUpdate = (val: number | [number, number] | null) => {
const fmt = props.format || (props.type.includes('time') ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD')
if (!val) {
if (isRange.value) {
start.value = null
end.value = null
} else {
modelValue.value = null
customValue.value = null
}
return
}
if (isRange.value && Array.isArray(val)) {
const [s, e] = val
if (props.withTimePadding && !props.type.includes('time')) {
// 非 datetime 模式下,自动补全 00:00:00 和 23:59:59
start.value = dayjs(s).format(fmt) + ' 00:00:00'
end.value = dayjs(e).format(fmt) + ' 23:59:59'
} else {
start.value = dayjs(s).format(fmt)
end.value = dayjs(e).format(fmt)
}
} else if (!Array.isArray(val)) {
const formatted = dayjs(val as number).format(fmt)
modelValue.value = formatted
customValue.value = formatted
}
}
</script>
<template>
<CommonModal
v-model="show"
title=""
:width="420"
:closable="!loading"
:show-confirm="false"
:show-cancel="false"
:segmented="false"
:header-style="{ display: 'none' }"
:footer-style="{ display: 'none' }"
content-style="padding: 0; background: transparent;"
class="download-progress-modal"
>
<div
class="relative overflow-hidden rounded-2xl bg-fill-1/80 backdrop-blur-xl p-4 border border-color1/20"
>
<!-- 装饰性背景光效 -->
<div class="absolute -top-24 -right-24 w-48 h-48 bg-primary/10 rounded-full blur-3xl animate-pulse"></div>
<div class="absolute -bottom-24 -left-24 w-48 h-48 bg-primary/5 rounded-full blur-3xl animate-pulse" style="animation-delay: 1s"></div>
<div class="relative z-10">
<div class="flex items-center justify-between mb-6">
<div class="flex flex-col">
<n-text class="text-lg font-bold tracking-tight text-color1">正在处理文件</n-text>
<n-text depth="3" class="text-xs mt-0.5 opacity-60">
{{ progress === 100 ? '已准备就绪' : '请勿关闭当前窗口' }}
</n-text>
</div>
<div class="flex items-center justify-center w-12 h-12 rounded-xl bg-primary/10 text-primary">
<div class="relative">
<DownloadOutline class="text-2xl animate-bounce" v-if="progress < 100" />
<CheckmarkCircleOutline class="text-2xl text-success" v-else />
</div>
</div>
</div>
<div class="space-y-3">
<div class="flex justify-between items-end px-0.5">
<n-text class="text-xs font-mono font-medium opacity-50">{{ fileName }}</n-text>
<n-text class="text-2xl font-black italic tracking-tighter text-primary">{{ Math.floor(progress) }}%</n-text>
</div>
<div class="h-3 w-full bg-fill-3 rounded-full overflow-hidden p-[2px]">
<div
class="h-full rounded-full transition-all duration-300 ease-out relative"
:style="{
width: `${progress}%`,
background: themeVars.primaryColor,
boxShadow: `0 0 15px ${themeVars.primaryColor}40`
}"
>
<!-- 进度条高光流光效果 -->
<div
class="absolute inset-0 w-full h-full bg-gradient-to-r from-transparent via-white/30 to-transparent skew-x-[-20deg] animate-shimmer"
></div>
</div>
</div>
</div>
</div>
</div>
</CommonModal>
</template>
<script setup lang="ts">
import { DownloadOutline, CheckmarkCircleOutline } from '@vicons/ionicons5'
interface DownloadOptions {
api?: string
params?: any
fileName?: string
title?: string
callback?: (success: boolean) => void
}
const themeVars = useThemeVars()
const show = ref(false)
const loading = ref(false)
const progress = ref(0)
const title = ref('文件下载')
const fileName = ref('')
let progressTimer: any = null
/**
* 打开下载弹窗并立即开始下载
* @param options 下载配置
*/
const open = async (options: DownloadOptions) => {
title.value = options.title || '下载模板'
fileName.value = options.fileName || '正在准备下载文件...'
progress.value = 0
loading.value = true
show.value = true
// 开启模拟进度条定时器
progressTimer = setInterval(() => {
if (progress.value < 90) {
progress.value += Math.floor(Math.random() * 5) + 2
} else if (progress.value < 98) {
progress.value += 0.5
}
}, 150)
const { api = '/v1/plugins/ATTACHMENT_DOWN', params = {}, fileName: name } = options
try {
const success = await service.download(api, params, name, { showLoading: false })
if (success) {
progress.value = 100
window.$message.success('下载任务已完成')
// 延迟关闭,让用户看到 100% 成功状态
setTimeout(() => {
show.value = false
options.callback?.(true)
}, 800)
} else {
show.value = false
options.callback?.(false)
}
} catch (err) {
console.error('Download failed', err)
progress.value = 0
window.$message.error('下载失败,请重试')
show.value = false
options.callback?.(false)
} finally {
loading.value = false
if (progressTimer) {
clearInterval(progressTimer)
progressTimer = null
}
}
}
defineExpose({ open })
</script>
<style scoped>
@keyframes shimmer {
0% {
transform: translateX(-100%) skewX(-20deg);
}
100% {
transform: translateX(200%) skewX(-20deg);
}
}
.animate-shimmer {
animation: shimmer 2s infinite linear;
}
:deep(.download-progress-modal.n-modal) {
background: transparent !important;
}
/* 兼容 Naive UI card preset 的内部样式覆盖 */
:deep(.download-progress-modal .n-card-header),
:deep(.download-progress-modal .n-card__footer) {
display: none !important;
}
:deep(.download-progress-modal .n-card__content) {
padding: 0 !important;
}
</style>
<template>
<CommonModal v-model="show" title="导出确认" style="width: 500px">
<div class="p-4 py-6">
<n-form-item label="选择导出范围" label-placement="left">
<n-radio-group v-model:value="exportMode" name="exportMode">
<n-space>
<n-radio value="page">当前页数据</n-radio>
<n-radio value="all">
全量数据
<n-text depth="3" class="text-xs ml-1">(最多 2,147,483,647 条)</n-text>
</n-radio>
</n-space>
</n-radio-group>
</n-form-item>
<n-alert v-if="exportMode === 'all'" title="提示" type="info" class="mt-4">全量导出可能会耗时较长,具体取决于系统数据总量。</n-alert>
<n-progress v-if="loading" type="line" :percentage="exportProgress" :indicator-placement="'inside'" processing class="mt-4" />
</div>
<template #footer>
<n-space justify="end">
<CommonButton @click="show = false" :disabled="loading">取消</CommonButton>
<CommonButton type="primary" :loading="loading" @click="handleConfirm">开始导出</CommonButton>
</n-space>
</template>
</CommonModal>
</template>
<script setup lang="ts">
import { service } from '@/api/index'
const show = ref(false)
const loading = ref(false)
const exportProgress = ref(0)
const exportMode = ref<'page' | 'all'>('page')
let progressTimer: any = null
// 导出上下文
const context = ref<{
fileName: string
functionCode: string
params: any
} | null>(null)
/**
* 打开导出选择弹窗
* @param options 导出配置
*/
const open = (options: { fileName: string; functionCode: string; params: any }) => {
context.value = options
exportMode.value = 'page' // 默认当前页
show.value = true
}
/** 执行下载 */
const handleConfirm = async () => {
if (!context.value) return
loading.value = true
exportProgress.value = 0
// 开启模拟进度条定时器
progressTimer = setInterval(() => {
if (exportProgress.value < 90) {
exportProgress.value += Math.floor(Math.random() * 3) + 1
} else if (exportProgress.value < 98) {
exportProgress.value += 0.3
}
}, 200)
const { fileName, functionCode, params } = context.value
const exportParams = {
...params,
functionCode,
fileName,
page: exportMode.value === 'all' ? 1 : params.page || 1,
rows: exportMode.value === 'all' ? 2147483647 : params.rows || 15
}
try {
const success = await service.download('/excel/export', exportParams, `${fileName}_${new Date().getTime()}.xlsx`, {
showLoading: false
})
if (success) {
exportProgress.value = 100
window.$message.success('导出任务已启动,请查看浏览器下载')
// 延迟关闭,让用户看到 100% 进度
setTimeout(() => {
show.value = false
}, 500)
}
} catch (err) {
console.error('Export failed', err)
exportProgress.value = 0
} finally {
loading.value = false
if (progressTimer) {
clearInterval(progressTimer)
progressTimer = null
}
}
}
defineExpose({ open })
</script>
<template>
<CommonModal v-model="showModal" :title="title" style="width: 500px">
<div class="p-6">
<n-upload v-model:file-list="fileList" :default-upload="false" :max="1" action="#" @before-upload="beforeUpload" @remove="handleRemove">
<n-upload-dragger v-if="fileList.length === 0">
<div class="mb-3">
<n-icon size="48" :depth="3">
<CloudUploadOutline />
</n-icon>
</div>
<n-text style="font-size: 16px">点击或将文件拖拽到这里上传</n-text>
<n-p depth="3" style="margin: 8px 0 0 0">仅支持 .xlsx 格式文件</n-p>
</n-upload-dragger>
</n-upload>
<n-progress
v-if="uploading || importResult?.success"
type="line"
:percentage="uploadProgress"
:indicator-placement="'inside'"
processing
class="mt-4"
/>
<n-alert v-if="importResult" class="mt-4" title="导入结果" :type="importResult.success ? 'success' : 'error'">
{{ importResult.message }}
</n-alert>
</div>
<template #footer>
<div class="flex justify-between items-center w-full">
<CommonButton v-if="showDownload" type="primary" secondary @click="handleDownloadTemplate">
<template #icon>
<n-icon><DownloadOutline /></n-icon>
</template>
下载导入模板
</CommonButton>
<div v-else></div>
<n-space>
<CommonButton @click="showModal = false">取消</CommonButton>
<CommonButton
type="primary"
:loading="uploading"
:disabled="!importResult?.success && fileList.length === 0"
@click="handleConfirm"
>
{{ importResult?.success ? '完成' : '确定' }}
</CommonButton>
</n-space>
</div>
</template>
</CommonModal>
</template>
<script setup lang="ts">
import { CloudUploadOutline, DownloadOutline } from '@vicons/ionicons5'
import type { UploadFileInfo } from 'naive-ui'
import { service } from '@/api/index'
interface ImportOptions {
title?: string
api: string
/** 模板名称/标识 */
templateName?: string
/** 模板下载接口 (可选,不传则使用默认下载逻辑) */
templateApi?: string
templateTitle?: string
data?: any
downloadParams?: any
onSuccess?: () => void
fileKey?: string
}
const showModal = ref(false)
const uploading = ref(false)
const uploadProgress = ref(0)
const fileList = ref<UploadFileInfo[]>([])
const importResult = ref<{ success: boolean; message: string } | null>(null)
const context = ref<ImportOptions | null>(null)
let progressTimer: any = null
const title = computed(() => context.value?.title || '批量导入')
const showDownload = computed(() => !!(context.value?.templateName || context.value?.templateApi))
/**
* 打开导入弹窗
* @param options 导入配置项
*/
const open = (options: ImportOptions) => {
context.value = options
showModal.value = true
importResult.value = null
fileList.value = []
}
const beforeUpload = (data: { file: UploadFileInfo; fileList: UploadFileInfo[] }) => {
const file = data.file.file
if (file) {
const isExcel = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.name.endsWith('.xlsx')
if (!isExcel) {
window.$message.error('只能上传 .xlsx 格式的文件')
return false
}
}
return true
}
const handleRemove = () => {
importResult.value = null
}
const handleConfirm = async () => {
const ctx = context.value
// 如果已经成功导入,再次点击确定则直接关闭弹窗
if (importResult.value?.success) {
showModal.value = false
return
}
if (fileList.value.length === 0 || !ctx?.api) return
uploading.value = true
uploadProgress.value = 0
importResult.value = null
// 开启模拟进度条定时器
progressTimer = setInterval(() => {
if (uploadProgress.value < 90) {
uploadProgress.value += Math.floor(Math.random() * 5) + 1
} else if (uploadProgress.value < 98) {
uploadProgress.value += 0.5
}
}, 200)
try {
const formData = new FormData()
const file = fileList.value[0]?.file
if (file) {
const key = ctx.fileKey || 'file'
formData.append(key, file)
}
// 合并业务参数
const extraData = ctx.data || {}
Object.keys(extraData).forEach((key) => {
formData.append(key, extraData[key])
})
// 禁用全局 showLoading,改用本地进度条
const res = await service.post(ctx.api, formData, { showLoading: false })
if (res.code === 200) {
window.$message.success('导入成功')
uploadProgress.value = 100
// 如果返回的是技术代码,转换一下显示效果
const msg = res.msg && res.msg.startsWith('ERRMSG.') ? '导入操作已成功完成' : res.msg || '导入成功'
importResult.value = {
success: true,
message: msg
}
fileList.value = [] // 成功后清除文件列表,防止重复点确定
ctx.onSuccess?.()
emit('success')
setTimeout(() => {
showModal.value = false
}, 1000)
} else {
uploadProgress.value = 0
// 拦截器通常会自动处理非 200 的报错,但这里我们可能需要显示结果到区域
importResult.value = {
success: false,
message: res.msg || '导入失败'
}
}
} catch (e: any) {
console.error('Upload failed', e)
uploadProgress.value = 0
importResult.value = {
success: false,
message: e.message || '上传异常'
}
} finally {
uploading.value = false
if (progressTimer) {
clearInterval(progressTimer)
progressTimer = null
}
}
}
const handleDownloadTemplate = () => {
if (!context.value?.templateName && !context.value?.templateApi) return
const params = {
...(context.value.templateName ? { filename: context.value.templateName } : {}),
...(context.value.downloadParams || {})
}
const downloadApi = context.value.templateApi || '/v1/plugins/ATTACHMENT_DOWN'
openDownloadModal({
api: downloadApi,
params,
fileName: context.value.templateTitle || '导入模板.xlsx',
title: '下载导入模板'
})
}
const emit = defineEmits(['success'])
defineExpose({
open
})
</script>
<template>
<n-input-number
v-model:value="internalValue"
:disabled="disabled"
:min="min"
:max="max"
:step="step"
:precision="precision"
:clearable="clearable"
:show-button="showButton"
v-bind="$attrs"
@update:value="handleUpdateValue"
>
<template v-for="(_, name) in $slots" #[name]="slotProps">
<slot :name="name" v-bind="slotProps || {}"></slot>
</template>
</n-input-number>
</template>
<script setup lang="ts">
const props = defineProps({
/** 绑定的值 (v-model) */
value: {
type: [String, Number, Boolean] as PropType<string | number | boolean | null | undefined>,
default: null
},
modelValue: {
type: [String, Number, Boolean] as PropType<string | number | boolean | null | undefined>,
default: null
},
/** 是否显示加减按钮 */
showButton: {
type: Boolean,
default: false
},
/** 严格模式:默认不开启(不开启时自动转字符串比对,不区分 1 和 '1') */
strict: {
type: Boolean,
default: false
},
/** 禁用状态 */
disabled: {
type: Boolean,
default: false
},
/** 最小值 */
min: {
type: Number,
default: undefined
},
/** 最大值 */
max: {
type: Number,
default: undefined
},
/** 步长 */
step: {
type: Number,
default: undefined
},
/** 精度 */
precision: {
type: Number,
default: undefined
},
/** 是否显示清除按钮 */
clearable: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['update:value', 'update:modelValue', 'change'])
/**
* 将外部值转换为内部数字值
*/
const toInternalValue = (v: any): number | null => {
if (v === null || v === undefined || v === '') return null
if (typeof v === 'boolean') return v ? 1 : 0
if (typeof v === 'string') {
const num = Number(v)
return isNaN(num) ? null : num
}
return typeof v === 'number' ? v : null
}
/**
* 将内部数字值转换为外部值
*/
const toExternalValue = (v: number | null): any => {
if (v === null || v === undefined) return null
return props.strict ? v : String(v)
}
const internalValue = ref<number | null>(toInternalValue(props.modelValue ?? props.value))
watch(
() => props.modelValue ?? props.value,
(newVal) => {
internalValue.value = toInternalValue(newVal)
}
)
const handleUpdateValue = (val: number | null) => {
const result = toExternalValue(val)
emit('update:value', result)
emit('update:modelValue', result)
emit('change', result)
}
</script>
<style scoped></style>
<template>
<n-modal
v-model:show="showValue"
preset="card"
:title="title"
:segmented="{ content: true, footer: 'soft' }"
:mask-closable="!loading"
:closable="!loading"
:draggable="{ bounds: 'none' }"
v-bind="$attrs"
:style="modalStyle"
class="app-modal"
header-class="!px-[20px] !py-[15px]"
content-class="!p-0"
footer-class="!m-0 !p-0"
>
<template #header-extra>
<slot name="header-extra"></slot>
</template>
<n-scrollbar :style="{ maxHeight: typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight }">
<n-spin :show="loading" :style="{ padding: typeof padding === 'number' ? `${padding}px` : padding }">
<slot></slot>
</n-spin>
</n-scrollbar>
<template #footer v-if="showFooter">
<n-space justify="end" align="center" class="p-[10px]">
<slot name="footer">
<slot name="footer-extra"></slot>
<CommonButton v-if="showCancel" @click="handleCancel" :disabled="loading">
{{ cancelText }}
</CommonButton>
<CommonButton v-if="showConfirm" type="primary" :loading="loading" @click="handleConfirm">
{{ confirmText }}
</CommonButton>
</slot>
</n-space>
</template>
</n-modal>
</template>
<script setup lang="ts">
interface Props {
modelValue: boolean
title?: string
loading?: boolean
confirmText?: string
cancelText?: string
showConfirm?: boolean
showCancel?: boolean
showFooter?: boolean
width?: string | number
maxHeight?: string | number
padding?: string | number
}
const props = withDefaults(defineProps<Props>(), {
title: '提示',
loading: false,
confirmText: '确定',
cancelText: '取消',
showConfirm: true,
showCancel: true,
showFooter: true,
width: '600px',
maxHeight: '75vh',
padding: '15px'
})
const emit = defineEmits(['update:modelValue', 'confirm', 'cancel'])
const modalStyle = computed(() => {
const w = props.width
return {
width: typeof w === 'number' ? `${w}px` : w
}
})
const showValue = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
const handleConfirm = () => {
emit('confirm')
}
const handleCancel = () => {
showValue.value = false
emit('cancel')
}
</script>
<style></style>
<style scoped></style>
<template>
<n-radio-group v-model:value="computedValue" v-bind="$attrs" @update:value="handleUpdateValue">
<n-space :size="spaceSize">
<template v-if="type === 'button'">
<n-radio-button v-for="opt in computedOptions" :key="String(opt.value)" :value="opt.value" :disabled="opt.disabled">
{{ opt.label }}
</n-radio-button>
</template>
<template v-else>
<n-radio v-for="opt in computedOptions" :key="String(opt.value)" :value="opt.value" :disabled="opt.disabled">
{{ opt.label }}
</n-radio>
</template>
</n-space>
</n-radio-group>
</template>
<script setup lang="ts">
import type { PropType } from 'vue'
const props = defineProps({
/** 绑定的值 (v-model) */
value: {
type: [String, Number, Boolean] as PropType<string | number | boolean | null | undefined>,
default: null
},
modelValue: {
type: [String, Number, Boolean] as PropType<string | number | boolean | null | undefined>,
default: null
},
/** 静态选项 */
options: {
type: Array as PropType<any[]>,
default: null
},
/** 显示类型: radio 或 button */
type: {
type: String as PropType<'radio' | 'button'>,
default: 'radio'
},
/** 严格模式:默认不开启(不开启时自动转字符串比对,不区分 1 和 '1') */
strict: {
type: Boolean,
default: false
},
/** 选项之间的间距 (n-space size) */
spaceSize: {
type: [String, Number, Array] as PropType<number | 'small' | 'medium' | 'large' | [number, number]>,
default: 'medium'
}
})
const emit = defineEmits(['update:value', 'update:modelValue', 'change'])
/**
* 值的标准化处理
*/
const normalizeValue = (v: any) => {
if (v === null || v === undefined || v === '') return null
// 如果是布尔值且非严格模式,转为字符串
if (typeof v === 'boolean' && !props.strict) return String(v)
return props.strict ? v : String(v)
}
const computedValue = computed<any>({
get: () => normalizeValue(props.modelValue ?? props.value),
set: (val: any) => {
// 如果是非严格模式,保持为 String 以匹配选项;如果需要数字,建议业务层自行处理或开启 strict
emit('update:value', val)
emit('update:modelValue', val)
}
})
const handleUpdateValue = (val: any) => {
emit('change', val)
}
const computedOptions = computed(() => {
let rawOptions: any[] = []
if (props.options) {
rawOptions = props.options
}
return rawOptions.map((opt: any) => {
const label = opt.TEXT || opt.name || opt.label || opt.REAL_NAME || opt.MENU_NAME || opt.title
const rawVal = opt.VALUE !== undefined ? opt.VALUE : (opt.id ?? opt.value ?? opt.PKID)
const value = normalizeValue(rawVal)
return {
...opt,
label,
value
}
})
})
</script>
<style scoped></style>
<template>
<n-select
:value="computedValue"
:options="computedOptions"
:loading="isLoading"
:clearable="true"
v-bind="$attrs"
:filterable="filterable"
:consistent-menu-width="consistentMenuWidth"
@update:value="handleUpdateValue"
/>
</template>
<script setup lang="ts">
import type { PropType } from 'vue'
const props = defineProps({
modelValue: {
type: [String, Number, Array] as PropType<string | number | any[] | null | undefined>,
default: null
},
// 接口 URL
api: {
type: String,
default: ''
},
// 接口参数
params: {
type: Object as PropType<Record<string, any>>,
default: () => ({})
},
beforeRequest: {
type: Function as PropType<(params: any) => any>,
default: undefined
},
afterResponse: {
type: Function as PropType<(data: any[]) => any[]>,
default: (data: any[]) => data
},
// 静态选项
options: {
type: Array as PropType<any[]>,
default: null
},
loading: {
type: Boolean,
default: false
},
// 严格模式:默认不开启(不开启时自动转字符串比对,不区分 1 和 '1')
strict: {
type: Boolean,
default: false
},
/**
* 是否保持菜单宽度与选择框一致
* @default false (不一致,自动适配内容宽度)
*/
consistentMenuWidth: {
type: Boolean,
default: false
},
/** 是否可搜索 */
filterable: {
type: Boolean,
default: true
}
})
const emit = defineEmits(['update:modelValue', 'change'])
const apiOptions = ref<any[]>([])
const internalLoading = ref(false)
const isLoading = computed(() => internalLoading.value || props.loading)
/**
* 值的标准化处理
*/
const normalizeValue = (v: any) => {
if (v === null || v === undefined || v === '') return null
return props.strict ? v : String(v)
}
const computedValue = computed<any>({
get: () => {
if (Array.isArray(props.modelValue)) {
return props.modelValue.map(normalizeValue).filter((v) => v !== null)
}
return normalizeValue(props.modelValue)
},
set: (val: any) => {
emit('update:modelValue', val)
}
})
const handleUpdateValue = (val: any, option: any) => {
emit('update:modelValue', val)
emit('change', val, option)
}
const computedOptions = computed(() => {
let rawOptions: any[] = []
if (props.options) {
rawOptions = props.options
} else if (props.api) {
rawOptions = apiOptions.value
}
return rawOptions.filter(Boolean).map((opt: any) => {
// 兼容不同的字段名 (针对 DICT、接口返回、用户传入等场景)
const label = opt.TEXT || opt.name || opt.label || opt.REAL_NAME || opt.MENU_NAME || opt.title
const value = normalizeValue(opt.VALUE !== undefined ? opt.VALUE : (opt.id ?? opt.value ?? opt.PKID))
return {
...opt,
label,
value
}
})
})
const fetchApiOptions = async () => {
if (!props.api) return
internalLoading.value = true
try {
let requestParams: any = { ...props.params }
if (props.beforeRequest) {
requestParams = props.beforeRequest(requestParams) ?? requestParams
}
const res = await requestListData(props.api, requestParams)
if (res.success) {
const list = Array.isArray(res.data) ? res.data : []
apiOptions.value = props.afterResponse ? props.afterResponse(list) : list
}
} catch (e) {
console.error('Fetch select options failed:', e)
apiOptions.value = []
} finally {
internalLoading.value = false
}
}
// 监听 API 或参数变化
watch(
() => props.api,
(newApi) => {
if (newApi) fetchApiOptions()
},
{ immediate: true }
)
watch(
() => JSON.stringify(props.params),
(newVal, oldVal) => {
if (newVal !== oldVal && props.api) {
fetchApiOptions()
}
}
)
</script>
<template>
<div class="common-split-container w-full h-full overflow-hidden">
<n-split :direction="direction" :default-size="defaultSize" :min="min" :max="max" class="h-full">
<template #1>
<div class="split-pane h-full flex flex-col overflow-hidden">
<slot name="left" v-if="direction === 'horizontal'" />
<slot name="top" v-else />
</div>
</template>
<template #2>
<div class="split-pane h-full flex flex-col overflow-hidden">
<slot name="right" v-if="direction === 'horizontal'" />
<slot name="bottom" v-else />
</div>
</template>
<template #resize-trigger>
<div class="resize-trigger-wrapper" :class="direction">
<div class="resize-line" :class="direction" />
</div>
</template>
</n-split>
</div>
</template>
<script setup lang="ts">
const themeVars = useThemeVars()
interface Props {
/** 分割方向,vertical 垂直分割,horizontal 水平分割 */
direction?: 'vertical' | 'horizontal'
/** 默认大小比例 */
defaultSize?: number
/** 最小比例 */
min?: number
/** 最大比例 */
max?: number
}
const props = withDefaults(defineProps<Props>(), {
direction: 'vertical',
defaultSize: 0.5,
min: 0.2,
max: 0.8
})
/**
* CommonSplitLayout - 主从表/分栏标准分割布局组件
* 用于全项目所有“上方主表 + 下方子表”或“左侧树/表 + 右侧表”的场景,提供可拖拽的分割杆。
*/
defineSlots<{
/** 上方容器插槽 (vertical) */
top?(): any
/** 下方容器插槽 (vertical) */
bottom?(): any
/** 左侧容器插槽 (horizontal) */
left?(): any
/** 右侧容器插槽 (horizontal) */
right?(): any
}>()
</script>
<style scoped>
.common-split-container {
padding: 0;
}
.split-pane {
box-sizing: border-box;
padding: 0;
}
.resize-trigger-wrapper {
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
background-color: transparent;
transition: background-color 0.3s;
}
.resize-trigger-wrapper.vertical {
height: 4px;
width: 100%;
cursor: row-resize;
}
.resize-trigger-wrapper.horizontal {
width: 4px;
height: 100%;
cursor: col-resize;
}
.resize-trigger-wrapper:hover {
background-color: v-bind('themeVars.primaryColor + "1A"');
}
.resize-trigger-wrapper:hover .resize-line {
background-color: v-bind('themeVars.primaryColor');
}
.resize-line {
background-color: v-bind('themeVars.borderColor');
transition: all 0.3s;
}
.resize-line.vertical {
width: 100%;
height: 1px;
}
.resize-line.horizontal {
height: 100%;
width: 1px;
}
/* 兼容主题变量 */
:deep(.n-split__resize-trigger) {
background-color: transparent;
}
/*
* 关键修复:CommonTable 默认 minHeight='350px',
* 在 split-pane 中强制撑高表格导致分页被 overflow:hidden 裁掉。
* 覆盖为 0 后,flex-1 能正确接管高度计算,分页始终可见。
*/
.split-pane :deep(.common-table-container) {
height: 100% !important;
}
.split-pane :deep(.custom-data-table) {
min-height: 0 !important;
}
</style>
<template>
<div
class="common-table-container flex-1 min-h-0 flex flex-col transition-all duration-300 h-full"
:style="{ backgroundColor: themeVars.cardColor }"
>
<n-h3 v-if="title" class="m-0 p-2">{{ title }}</n-h3>
<div :class="['flex-1 min-h-0 table-container', compact ? 'p-1' : 'p-3']">
<n-data-table
remote
ref="tableRef"
v-bind="$attrs"
:columns="mergedColumns"
:data="displayData"
:loading="displayLoading"
:pagination="false"
:row-key="props.rowKey"
:scroll-x="autoScrollX"
:checked-row-keys="checkedRowKeys"
:row-class-name="handleRowClassName"
:row-props="rowProps"
@update:page="onPageChange"
@update:page-size="onPageSizeChange"
@update:checked-row-keys="handleCheckedRowKeysChange"
@update:sorter="handleSorterChange"
size="small"
:flex-height="flexHeight"
:style="{ height: '100%', minHeight: typeof minHeight === 'number' ? `${minHeight}px` : minHeight }"
class="h-full custom-data-table"
/>
</div>
<!-- 自定义底部分页栏 -->
<div
v-if="showPagination"
class="common-table-pager flex items-center justify-end px-4 py-1.5 text-xs select-none space-x-4"
:style="{ backgroundColor: themeVars.actionColor, color: themeVars.textColor2 }"
>
<!-- 左侧:占位 -->
<div class="mr-auto flex items-center">
</div>
<div v-if="!compact" class="flex items-center space-x-3">
<span>显示 {{ pageStart }}{{ pageEnd }}, 共 {{ displayTotal }} 记录</span>
<span class="pager-divider">|</span>
<div class="flex items-center">
<span>每页</span>
<CommonSelect
:model-value="currentPageSize"
@update:model-value="onPageSizeChange"
:options="pageSizeOptions"
strict
size="small"
class="w-[75px] mx-1"
:consistent-menu-width="true"
:clearable="false"
/>
<span></span>
</div>
</div>
<div v-else class="text-xs">{{ displayTotal }}</div>
<div class="flex items-center space-x-2">
<!-- 首页 -->
<n-button v-if="!compact" size="small" quaternary :disabled="currentPage <= 1 || displayLoading" @click="goToPage(1)">
<template #icon>
<n-icon :size="14"><PlayBackOutline /></n-icon>
</template>
</n-button>
<!-- 上一页 -->
<n-button size="small" quaternary :disabled="currentPage <= 1 || displayLoading" @click="goToPage(currentPage - 1)">
<template #icon>
<n-icon :size="14"><ChevronBackOutline /></n-icon>
</template>
</n-button>
<span class="pager-divider">|</span>
<div class="flex items-center">
<span></span>
<CommonInputNumber
:value="currentPage"
@update:value="handlePageInput"
strict
size="small"
:show-button="false"
class="w-[55px] mx-1 text-center"
:min="1"
:max="totalPages"
:precision="0"
/>
<span>共 {{ totalPages }} 页</span>
</div>
<span class="pager-divider">|</span>
<!-- 下一页 -->
<n-button size="small" quaternary :disabled="currentPage >= totalPages || displayLoading" @click="goToPage(currentPage + 1)">
<template #icon>
<n-icon :size="14"><ChevronForwardOutline /></n-icon>
</template>
</n-button>
<!-- 尾页 -->
<n-button
v-if="!compact"
size="small"
quaternary
:disabled="currentPage >= totalPages || displayLoading"
@click="goToPage(totalPages)"
>
<template #icon>
<n-icon :size="14"><PlayForwardOutline /></n-icon>
</template>
</n-button>
<span v-if="!compact" class="pager-divider">|</span>
<!-- 刷新按钮 -->
<n-button v-if="!compact" size="small" quaternary @click="refreshData" :loading="displayLoading">
<template #icon>
<n-icon :size="14"><RefreshOutline /></n-icon>
</template>
</n-button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useAttrs } from 'vue'
import { useThemeVars } from 'naive-ui'
import type { DataTableColumns } from 'naive-ui'
import { PlayBackOutline, ChevronBackOutline, ChevronForwardOutline, PlayForwardOutline, RefreshOutline } from '@vicons/ionicons5'
const themeVars = useThemeVars()
const attrs = useAttrs()
interface Props {
title?: string
columns: DataTableColumns<any>
data?: any[]
loading?: boolean
total?: number
page?: number
pageSize?: number
rowKey?: (row: any) => string | number
showPagination?: boolean
/** 接口地址或自定义获取数据的方法 */
api?: string | ((params: any) => Promise<any>)
searchParams?: any
showIndex?: boolean
/** 请求方法:GET或POST,默认POST */
method?: 'GET' | 'POST'
/** 是否开启单行点击选中效果 */
choose?: boolean
/** 请求参数转化器 */
beforeRequest?: (params: any) => any
/** 响应结果转化器 */
afterResponse?: (data: any[]) => any[]
/** 是否在挂载时立即触发请求,默认为 true */
immediate?: boolean
/** 是否填充父容器高度,默认为 true */
flexHeight?: boolean
/** 是否紧凑模式 (用于下拉框等场景) */
compact?: boolean
/** 分页配置透传 */
paginationProps?: any
/** 最小高度,默认 250px */
minHeight?: string | number
/** 自定义行样式类 */
rowClassName?: string | ((row: any) => string)
/** 是否开启多选 */
multiple?: boolean
/** 外部传入的行属性 */
rowProps?: (row: any) => any
/** 是否开启远程排序,默认 false (本地排序) */
remoteSort?: boolean
}
const props = withDefaults(defineProps<Props>(), {
title: '',
loading: false,
total: 0,
page: 1,
pageSize: 15,
rowKey: (row: any) => {
if (row.PKID !== undefined && row.PKID !== null) return row.PKID
if (row.id !== undefined && row.id !== null) return row.id
if (row.pkid !== undefined && row.pkid !== null) return row.pkid
const keys = [
'REQUIRE_NO',
'PLAN_NO',
'TEMPLATE_ID',
'RULE_OBJNR',
'COMPANY_CODE',
'OPERATOR_CODE',
'MAINTENANCE_BASE_NO',
'WKPKGE_NO',
'MTTASK_NO',
'GR_DOCUMENT_NO',
'ITEM_NO',
'NO'
]
for (const k of keys) {
if (row[k] !== undefined && row[k] !== null && row[k] !== '') return row[k]
}
return row.PKID || row.id || JSON.stringify(row)
},
showPagination: true,
choose: false,
searchParams: () => ({}),
showIndex: true,
afterResponse: (data: any[]) => data,
immediate: true,
method: 'POST',
flexHeight: true,
compact: false,
paginationProps: () => ({}),
minHeight: '250px',
remoteSort: false
})
const emit = defineEmits([
'update:page',
'update:pageSize',
'refresh',
'success',
'select',
'row-click',
'update:checkedRowKeys',
'update:checkedRows'
])
// 选中的行 Key (v-model:checkedRowKeys)
const checkedRowKeys = defineModel<Array<string | number>>('checkedRowKeys', { default: () => [] })
// 选中的行数据对象 (v-model:checkedRows)
const checkedRows = defineModel<Array<Record<string, any>>>('checkedRows', { default: () => [] })
// 当前点击选中的行 (v-model:chooseRow)
const chooseRow = defineModel<Record<string, any> | null>('chooseRow', { default: null })
let isUpdating = false
/** 处理多选 Key 变化,同步更新 checkedRows 并分发事件 */
const handleCheckedRowKeysChange = (keys: Array<string | number>, rows: Array<Record<string, any>>) => {
isUpdating = true
checkedRowKeys.value = keys
checkedRows.value = rows
emit('update:checkedRowKeys', keys, rows)
emit('update:checkedRows', rows)
nextTick(() => {
isUpdating = false
})
}
/** 行属性配置:处理点击选中逻辑,并深度融合外部传入的行属性配置 */
const rowProps = (row: Record<string, any>) => {
const extProps = props.rowProps ? props.rowProps(row) : {}
return {
...extProps,
class: `${props.choose ? 'cursor-pointer' : ''} ${extProps.class || ''}`.trim(),
onClick: (event: MouseEvent) => {
// 执行外部的 onClick
if (extProps.onClick) {
extProps.onClick(event)
}
// 检查点击目标是否是交互元素(按钮、链接、输入框等)
const target = event.target as HTMLElement
const isInteractive = !!target.closest('button, a, input, [role="button"], .n-button')
if (!isInteractive) {
if (props.choose) {
chooseRow.value = row
}
emit('select', row)
emit('row-click', row)
}
}
}
}
/** 行样式配置:高亮当前点击选中的行并合并外部自定义样式类 */
const handleRowClassName = (row: Record<string, any>) => {
const classes: string[] = []
// 如果有外部定义的样式类
if (props.rowClassName) {
if (typeof props.rowClassName === 'function') {
const externalClass = props.rowClassName(row)
if (externalClass) classes.push(externalClass)
} else {
classes.push(props.rowClassName)
}
}
// 内部选中高亮逻辑
if (props.choose && chooseRow.value) {
const key = props.rowKey(row)
const selectedKey = props.rowKey(chooseRow.value)
if (key === selectedKey) classes.push('select-row')
}
return classes.join(' ')
}
// 内部状态,仅在提供 api 时使用
const internalData = ref<any[]>([])
const internalTotal = ref(0)
const internalLoading = ref(false)
const internalPage = ref(1)
const internalPageSize = ref(15)
// 当前排序状态
const currentSorter = ref<any>(null)
const handleSorterChange = (sorter: any) => {
currentSorter.value = sorter
if (props.remoteSort) {
internalPage.value = 1
fetchData()
} else {
if (!sorter || !sorter.order) {
fetchData()
} else {
applyLocalSort()
}
}
}
const applyLocalSort = () => {
if (!currentSorter.value || !currentSorter.value.order) return
const { columnKey, order } = currentSorter.value
const list = internalData.value
if (!Array.isArray(list) || list.length === 0) return
internalData.value = [...list].sort((a, b) => {
const valA = a[columnKey]
const valB = b[columnKey]
if (valA === valB) return 0
if (valA === null || valA === undefined) return 1
if (valB === null || valB === undefined) return -1
// 数字排序
if (typeof valA === 'number' && typeof valB === 'number') {
return order === 'ascend' ? valA - valB : valB - valA
}
// 字符串型数字排序
const numA = Number(valA)
const numB = Number(valB)
if (!isNaN(numA) && !isNaN(numB)) {
return order === 'ascend' ? numA - numB : numB - numA
}
// 字符串字母排序
const strA = String(valA)
const strB = String(valB)
return order === 'ascend' ? strA.localeCompare(strB, 'zh-CN', { numeric: true }) : strB.localeCompare(strA, 'zh-CN', { numeric: true })
})
}
const displayData = computed(() => (props.api ? internalData.value : props.data))
const displayTotal = computed(() => (props.api ? internalTotal.value : props.total))
const displayLoading = computed(() => (props.api ? internalLoading.value : props.loading))
const currentPage = computed(() => (props.api ? internalPage.value : props.page))
const currentPageSize = computed(() => (props.api ? internalPageSize.value : props.pageSize))
const paginationObj = computed(() => {
if (!props.showPagination) return false
return {
page: currentPage.value,
pageSize: currentPageSize.value,
itemCount: displayTotal.value,
showSizePicker: !props.compact,
pageSizes: [10, 15, 20, 50, 100],
prefix: props.compact ? undefined : ({ itemCount }: { itemCount?: number }) => `共 ${itemCount || 0} 条数据`,
'max-pages': 5,
...props.paginationProps
}
})
const mergedColumns = computed(() => {
if (!props.columns || !Array.isArray(props.columns)) return []
// 1. 将特殊列与普通列分开
const specialLeftCols: any[] = []
const specialRightCols: any[] = []
const normalCols: any[] = []
props.columns.forEach((col: any) => {
const isSpecialLeft = col.type === 'selection' || col.key === 'index' || col.type === 'index'
const isSpecialRight = col.key === 'actions' || col.key === 'action' || col.type === 'action' || String(col.title).includes('操作')
if (isSpecialLeft) {
specialLeftCols.push(col)
} else if (isSpecialRight) {
specialRightCols.push(col)
} else {
normalCols.push(col)
}
})
let cols = [...specialLeftCols, ...normalCols, ...specialRightCols]
// 3. 处理多选列逻辑
const hasSelection = cols.some((col: any) => col.type === 'selection')
if (props.multiple && !hasSelection) {
cols.unshift({
type: 'selection',
fixed: 'left',
width: 50
})
}
// 4. 处理序号列逻辑
const hasIndex = cols.some((col: any) => col.key === 'index' || col.type === 'index')
if (props.showIndex && !hasIndex) {
const indexColumn: any = {
title: '序号',
key: 'index',
width: 80,
align: 'center',
render: (_: any, index: number) => {
return (currentPage.value - 1) * currentPageSize.value + index + 1
}
}
const selectionIdx = cols.findIndex((col: any) => col.type === 'selection')
if (selectionIdx !== -1) {
cols.splice(selectionIdx + 1, 0, indexColumn)
} else {
cols.unshift(indexColumn)
}
}
// 5. 处理全局截断与渲染
return cols.map((col: any) => {
const isSpecial =
col.type === 'selection' ||
col.key === 'index' ||
col.key === 'action' ||
col.type === 'action' ||
String(col.title).includes('操作') ||
col.noEllipsis === true
if (!isSpecial) {
const originalRender = col.render
return {
...col,
ellipsis: false,
render: (row: any, index: number) => {
const content = originalRender ? originalRender(row, index) : row[col.key]
if (content === null || content === undefined || content === '') return ''
return content
}
}
}
return col
})
})
const autoScrollX = computed<string | number | undefined>(() => {
const scrollX = attrs['scroll-x'] as any
if (scrollX) {
return Number(scrollX) || scrollX
}
let total = 0
mergedColumns.value.forEach((col: any) => {
if (typeof col.width === 'number') {
total += col.width
} else if (typeof col.width === 'string' && col.width.endsWith('px')) {
total += parseInt(col.width)
} else {
total += 100 // Default width if not specified
}
})
return total
})
const fetchData = async () => {
if (!props.api) return
internalLoading.value = true
try {
let queryParams: any = {
...props.searchParams,
page: internalPage.value,
rows: internalPageSize.value
}
if (props.remoteSort && currentSorter.value && currentSorter.value.order) {
queryParams.sort = currentSorter.value.columnKey
queryParams.order = currentSorter.value.order === 'ascend' ? 'asc' : 'desc'
}
// 处理请求前参数转换
if (props.beforeRequest) {
queryParams = props.beforeRequest(queryParams) ?? queryParams
}
let res: any
if (typeof props.api === 'function') {
res = await props.api(queryParams)
} else {
res = await requestListData(props.api, queryParams, props.method)
}
if (res && (res.success || res.code === 200)) {
const responseData = Array.isArray(res.data) ? res.data : []
const list = props.afterResponse ? props.afterResponse(responseData) : responseData
// 自动回退逻辑:如果当前页没有数据且不在第一页,说明可能由于删除等原因导致当前页空,自动跳转到第一页
if (list.length === 0 && internalPage.value > 1) {
internalPage.value = 1
fetchData()
return
}
internalData.value = list
if (!props.remoteSort) {
applyLocalSort()
}
// 兼容后端返回 total 为 0 但 data 有数据的情况
internalTotal.value = res.total || (res.total === 0 && list.length > 0 ? list.length : res.total)
emit('success', res)
}
} catch (err) {
console.error('Fetch error:', err)
} finally {
internalLoading.value = false
}
}
const onPageChange = (page: number) => {
if (props.api) {
internalPage.value = page
fetchData()
} else {
emit('update:page', page)
emit('refresh')
}
}
const onPageSizeChange = (pageSize: number) => {
if (props.api) {
internalPageSize.value = pageSize
internalPage.value = 1
fetchData()
} else {
emit('update:pageSize', pageSize)
emit('update:page', 1)
emit('refresh')
}
}
const pageSizeOptions = [
{ label: '10', value: 10 },
{ label: '15', value: 15 },
{ label: '20', value: 20 },
{ label: '50', value: 50 },
{ label: '100', value: 100 }
]
const pageStart = computed(() => {
if (displayTotal.value === 0) return 0
return (currentPage.value - 1) * currentPageSize.value + 1
})
const pageEnd = computed(() => {
if (displayTotal.value === 0) return 0
return Math.min(currentPage.value * currentPageSize.value, displayTotal.value)
})
const totalPages = computed(() => {
if (displayTotal.value === 0) return 1
return Math.ceil(displayTotal.value / currentPageSize.value)
})
const goToPage = (page: number) => {
if (page < 1 || page > totalPages.value) return
onPageChange(page)
}
const handlePageInput = (value: number | null) => {
if (value === null) return
const pageNum = Math.max(1, Math.floor(Number(value)))
goToPage(pageNum)
}
const refreshData = () => {
if (props.api) {
fetchData()
} else {
emit('refresh')
}
}
// 监听搜索参数变化
watch(
() => props.searchParams,
(newVal, oldVal) => {
if (props.api) {
// 只有当查询参数真正发生变化(深比较)时,才重置到第一页并重新查询
// 这样可以避免:1. 点击查询但参数没变时跳回第一页 2. 联动更新时的意外跳转
const isChanged = JSON.stringify(newVal) !== JSON.stringify(oldVal)
if (isChanged) {
internalPage.value = 1
fetchData()
}
}
},
{ deep: true }
)
// 监听 API 地址变化,自动重置分页并刷新
watch(
() => props.api,
(newVal) => {
if (newVal) {
internalPage.value = 1
fetchData()
} else {
internalData.value = []
internalTotal.value = 0
}
}
)
// 监听外部对 checkedRows 的修改,同步更新 checkedRowKeys
watch(
() => checkedRows.value,
(newVal) => {
if (isUpdating) return
if (!newVal || newVal.length === 0) {
if (checkedRowKeys.value && checkedRowKeys.value.length !== 0) {
checkedRowKeys.value = []
}
} else {
const keys = newVal.map((row) => props.rowKey(row))
if (JSON.stringify(checkedRowKeys.value) !== JSON.stringify(keys)) {
checkedRowKeys.value = keys
}
}
},
{ deep: true }
)
// 监听外部对 checkedRowKeys 的修改,同步更新 checkedRows
watch(
() => checkedRowKeys.value,
(newVal) => {
if (isUpdating) return
if (!newVal || newVal.length === 0) {
if (checkedRows.value && checkedRows.value.length !== 0) {
checkedRows.value = []
}
} else {
const currentRows = checkedRows.value || []
const newRows = newVal.map((key) => {
const foundInChecked = currentRows.find((row) => props.rowKey(row) === key)
if (foundInChecked) return foundInChecked
const foundInData = displayData.value?.find((row) => props.rowKey(row) === key)
if (foundInData) return foundInData
// 构造防错回退对象,使用 Proxy 代理拦截所有属性访问并返回 key,
// 使得外部自定义的 rowKey 函数在执行时读取任意业务字段均能安全返回 key,规避死循环
try {
return new Proxy(
{ PKID: key },
{
get(target, prop) {
if (
typeof prop === 'symbol' ||
(typeof prop === 'string' && (prop.startsWith('__') || prop === 'constructor' || prop === 'toJSON'))
) {
return (target as any)[prop]
}
return key
}
}
)
} catch (e) {
return { PKID: key }
}
})
const currentKeys = currentRows.map((row) => props.rowKey(row))
if (JSON.stringify(currentKeys) !== JSON.stringify(newVal)) {
checkedRows.value = newRows
}
}
},
{ deep: true }
)
onMounted(() => {
if (props.api && props.immediate) {
fetchData()
}
})
// 暴露刷新方法
defineExpose({
refresh: () => {
if (props.api) {
fetchData()
} else {
emit('refresh')
}
},
reSearch: () => {
if (props.api) {
internalPage.value = 1
fetchData()
}
},
getCheckedRows: () => checkedRows.value,
getCheckedRowKeys: () => checkedRowKeys.value,
getSelectedRow: () => chooseRow.value,
getTableData: () => displayData.value,
currentPage,
currentPageSize
})
</script>
<style scoped>
.common-table-card {
height: 100%;
}
.table-container {
padding: 0;
}
:deep(.n-data-table .n-data-table-wrapper) {
border-radius: 8px;
}
/* Refine scrollbar appearance for theme compatibility */
:deep(.n-data-table .n-scrollbar-rail) {
background-color: transparent !important;
opacity: 1 !important;
}
:deep(.n-data-table .n-scrollbar-rail--horizontal) {
height: 8px !important;
bottom: 0px !important;
}
:deep(.n-data-table .n-scrollbar-rail--vertical) {
width: 8px !important;
right: 0px !important;
}
:deep(.n-data-table .n-scrollbar-rail > .n-scrollbar-rail__scrollbar) {
background-color: rgba(128, 128, 128, 0.25) !important;
transition: background-color 0.3s !important;
}
:deep(.n-data-table .n-scrollbar-rail:hover > .n-scrollbar-rail__scrollbar) {
background-color: rgba(128, 128, 128, 0.5) !important;
}
:deep(.n-data-table .n-pagination) {
margin-top: 12px;
padding: 8px 12px;
}
.custom-data-table :deep(.n-data-table-base-table-body) {
scrollbar-width: thin;
scrollbar-color: rgba(128, 128, 128, 0.3) transparent;
}
/* 两行截断:通过 -webkit-line-clamp 限制单元格内容显示行数 */
:deep(.n-data-table-td .n-data-table-td__content) {
display: -webkit-box !important;
-webkit-box-orient: vertical !important;
-webkit-line-clamp: 2 !important;
overflow: hidden !important;
white-space: normal !important;
word-break: break-all !important;
line-height: 1.6 !important;
}
/* 如果是按钮组或操作列,允许其正常展示(通常内部有不同的 DOM 结构)*/
:deep(.n-data-table-td.n-data-table-td--action .n-data-table-td__content) {
display: block !important;
max-height: none !important;
overflow: visible !important;
}
:deep(.n-data-table .n-data-table-tr.select-row > .n-data-table-td) {
/* 核心修复:固定列背景必须不透明,否则横向滚动时会看到被遮挡的内容。
通过设置层叠背景,底层使用不透明的 tableColor,上层使用半透明的选中色。*/
background-color: v-bind('themeVars.tableColor') !important;
background-image: linear-gradient(v-bind('themeVars.primaryColor + "1A"'), v-bind('themeVars.primaryColor + "1A"')) !important;
}
.common-table-pager :deep(.n-input-number .n-input__input-el) {
text-align: center;
}
.common-table-pager :deep(.n-button) {
padding: 0 4px;
}
.pager-divider {
color: v-bind('themeVars.borderColor');
}
</style>
<template>
<n-popover
v-model:show="showPopover"
trigger="click"
placement="bottom-start"
:width="typeof popoverWidth === 'number' ? popoverWidth : undefined"
:style="{ padding: 0 }"
@update:show="onPopoverShow"
>
<template #trigger>
<n-input
v-model:value="displayLabel"
:placeholder="placeholder"
:disabled="disabled"
:loading="loading"
clearable
readonly
@clear="onClear"
>
<template #suffix>
<n-icon class="cursor-pointer">
<chevron-down-outline />
</n-icon>
</template>
</n-input>
</template>
<div
class="p-3 flex flex-col min-w-0"
:style="{ width: typeof popoverWidth === 'number' ? `${popoverWidth}px` : popoverWidth, maxHeight: '550px' }"
>
<div v-if="showSearch" class="mb-2">
<n-input-group>
<n-input
ref="searchInputRef"
v-model:value="searchKeyword"
placeholder="输入关键字搜索"
size="small"
@keyup.enter="handleSearch"
/>
<n-button type="primary" size="small" @click="handleSearch">搜索</n-button>
</n-input-group>
</div>
<div class="flex-1 min-h-0">
<CommonTable
ref="tableRef"
:api="api"
:columns="columns"
:search-params="combinedParams"
:show-pagination="showPagination"
:pagination-props="{ maxPages: 5 }"
:flex-height="false"
compact
:immediate="false"
choose
v-model:choose-row="selectedRow"
:row-key="(row: any) => row[valueField]"
:max-height="320"
:show-index="showIndex"
:after-response="handleTableResponse"
@success="onTableSuccess"
@select="onRowSelect"
/>
</div>
</div>
</n-popover>
</template>
<script setup lang="ts">
import { debounce } from 'lodash-es'
import { ChevronDownOutline } from '@vicons/ionicons5'
import type { DataTableColumns } from 'naive-ui'
interface Props {
modelValue?: string | number | null
labelValue?: string
placeholder?: string
disabled?: boolean
loading?: boolean
api: string
params?: Record<string, any>
columns: DataTableColumns<any>
valueField?: string
labelField?: string
searchField?: string
popoverWidth?: number | string
showPagination?: boolean
immediate?: boolean
strict?: boolean
showSearch?: boolean
showIndex?: boolean
searchValue?: 'value' | 'label'
filterData?: boolean
}
const props = withDefaults(defineProps<Props>(), {
modelValue: null,
labelValue: '',
placeholder: '请选择',
disabled: false,
loading: false,
params: () => ({}),
valueField: 'ACCOUNT_ID',
labelField: 'USER_NAME',
searchField: 'q',
popoverWidth: 500,
showPagination: true,
immediate: true,
strict: false,
showSearch: true,
showIndex: false,
searchValue: 'label',
filterData: true
})
const emit = defineEmits(['update:modelValue', 'update:labelValue', 'change', 'select'])
const showPopover = ref(false)
const searchKeyword = ref('')
const throttledKeyword = ref('')
const tableRef = ref<any>(null)
const searchInputRef = ref<any>(null)
const selectedRow = ref<any>(null)
const displayLabel = ref(props.labelValue)
const preloadedList = ref<any[]>([])
/** 值的标准化处理 */
const normalizeValue = (v: any) => {
if (v === null || v === undefined || v === '') return null
return props.strict ? v : String(v)
}
watch(
searchKeyword,
debounce((val) => {
throttledKeyword.value = val
}, 300)
)
/** 组合搜索参数 */
const combinedParams = computed(() => {
const p: Record<string, any> = { ...props.params }
if (props.showSearch) {
p[props.searchField] = throttledKeyword.value
}
return p
})
watch(
() => props.labelValue,
(val) => {
displayLabel.value = val
}
)
const updateSelectedState = (list: any[], val: any) => {
const match = list.find((r: any) => normalizeValue(r[props.valueField]) === normalizeValue(val))
if (match) {
displayLabel.value = match[props.labelField] || match['REAL_NAME'] || match['USER_NAME']
selectedRow.value = match
emit('update:labelValue', displayLabel.value)
} else {
displayLabel.value = String(val)
}
}
const filterNullAndDuplicate = (list: any[]) => {
if (!Array.isArray(list)) return []
const uniqueData: any[] = []
const seen = new Set()
list.forEach((item) => {
const val = item[props.valueField]
if (val !== undefined && val !== null && String(val).trim() !== '' && !seen.has(val)) {
seen.add(val)
uniqueData.push(item)
}
})
return uniqueData
}
const handleTableResponse = (data: any[]) => {
return props.filterData ? filterNullAndDuplicate(data) : data
}
const fetchPreloadedData = async () => {
if (!props.api) return
try {
const payload: Record<string, any> = {
...props.params,
page: 1,
pageSize: 1000
}
if (props.showSearch) {
payload[props.searchField] = ''
}
const res = await service.post(props.api, payload)
if (res.code === 200) {
const list = Array.isArray(res.data) ? res.data : res.data?.rows || res.data?.data || []
const filteredList = props.filterData ? filterNullAndDuplicate(list) : list
preloadedList.value = filteredList
if (props.modelValue && !displayLabel.value) {
updateSelectedState(filteredList, props.modelValue)
}
}
} catch (e) {
console.error('CommonTableSelect preload failed:', e)
}
}
onMounted(() => {
fetchPreloadedData()
})
watch(
() => props.modelValue,
(val) => {
if (val === null || val === undefined || val === '') {
displayLabel.value = ''
selectedRow.value = null
} else if (preloadedList.value.length > 0) {
updateSelectedState(preloadedList.value, val)
} else {
fetchPreloadedData()
}
}
)
/** 处理行选中 (由用户点击触发) */
const onRowSelect = (row: any) => {
if (row) {
const val = normalizeValue(row[props.valueField])
const label = row[props.labelField] || row['REAL_NAME'] || row['USER_NAME']
displayLabel.value = label
emit('update:modelValue', val)
emit('update:labelValue', label)
emit('change', val, row)
emit('select', row)
showPopover.value = false
}
}
const onTableSuccess = (res: any) => {
if (props.modelValue && !selectedRow.value) {
const list = Array.isArray(res.data) ? res.data : res.data?.rows || res.data?.data || []
const match = list.find((r: any) => normalizeValue(r[props.valueField]) === normalizeValue(props.modelValue))
if (match) selectedRow.value = match
}
}
const onPopoverShow = (show: boolean) => {
if (show) {
selectedRow.value = null
const kw = props.showSearch ? (props.searchValue === 'value' ? props.modelValue : displayLabel.value) || '' : ''
searchKeyword.value = String(kw)
throttledKeyword.value = String(kw)
nextTick(() => {
tableRef.value?.reSearch()
if (props.showSearch) {
searchInputRef.value?.focus()
}
})
}
}
const handleSearch = () => {
throttledKeyword.value = searchKeyword.value
tableRef.value?.reSearch()
}
const onClear = () => {
selectedRow.value = null
displayLabel.value = ''
emit('update:modelValue', null)
emit('update:labelValue', '')
emit('change', null, null)
}
</script>
<style scoped>
:deep(.n-popover-shared) {
padding: 0;
}
</style>
<template>
<n-tag
v-if="(currentValue !== null && currentValue !== undefined && currentLabel) || $slots.default"
:type="tagType"
size="small"
v-bind="$attrs"
:round="round"
:bordered="bordered"
:style="customStyle"
:loading="internalLoading"
>
<slot v-if="$slots.default"></slot>
<template v-else>{{ currentLabel }}</template>
</n-tag>
</template>
<script setup lang="ts">
import type { PropType } from 'vue'
const props = defineProps({
/** 当前值 */
value: {
type: [String, Number, Boolean] as PropType<string | number | boolean | null | undefined>,
default: null
},
/** 接口 URL */
api: {
type: String,
default: ''
},
/** 接口参数 */
params: {
type: Object as PropType<Record<string, any>>,
default: () => ({})
},
/** 静态选项 */
options: {
type: Array as PropType<any[]>,
default: null
},
/**
* 状态颜色转换逻辑
* 1. 提供对象:{ '0': 'success', '1': 'error' }
* 2. 提供函数:(val) => val === 0 ? 'success' : 'error'
* 3. 默认值处理:若不提供,且值为 0/1/true/false,则自动处理 (0/true -> success, 其他 -> error)
*/
typeMap: {
type: [Object, Function] as PropType<Record<string, string> | ((val: any) => string)>,
default: null
},
/** 默认 Tag 类型 */
type: {
type: String as PropType<'default' | 'primary' | 'info' | 'success' | 'warning' | 'error'>,
default: null
},
defaultType: {
type: String as PropType<'default' | 'primary' | 'info' | 'success' | 'warning' | 'error'>,
default: 'default'
},
/** 是否开启语义化自动着色(针对 0/1/true/false) */
autoColor: {
type: Boolean,
default: false
},
/** 是否圆角 */
round: {
type: Boolean,
default: false
},
/** 是否有边框 */
bordered: {
type: Boolean,
default: false
},
/** 严格模式:默认不开启(不开启时自动转字符串比对,不区分 1 和 '1') */
strict: {
type: Boolean,
default: false
}
})
const apiOptions = ref<any[]>([])
const internalLoading = ref(false)
/**
* 值的标准化处理
*/
const normalizeValue = (v: any) => {
if (v === null || v === undefined || v === '') return null
return props.strict ? v : String(v)
}
const currentValue = computed(() => {
return normalizeValue(props.value)
})
const fetchApiOptions = async () => {
if (!props.api) return
internalLoading.value = true
try {
const res = await requestListData(props.api, props.params)
if (res.success) {
apiOptions.value = res.data
}
} catch (e) {
console.error('Fetch tag options failed:', e)
apiOptions.value = []
} finally {
internalLoading.value = false
}
}
watch(
() => props.api,
(val) => {
if (val) fetchApiOptions()
},
{ immediate: true }
)
watch(
() => JSON.stringify(props.params),
(newVal, oldVal) => {
if (newVal !== oldVal && props.api) {
fetchApiOptions()
}
}
)
const currentOption = computed(() => {
if (currentValue.value === null) return null
let list: any[] = []
if (props.options) {
list = props.options
} else if (props.api) {
list = apiOptions.value
}
if (list.length > 0) {
return list.find((opt) => {
const val = opt.VALUE !== undefined ? opt.VALUE : (opt.id ?? opt.value ?? opt.PKID)
return normalizeValue(val) === currentValue.value
})
}
return null
})
const currentLabel = computed(() => {
if (currentOption.value) {
return (
currentOption.value.TEXT ||
currentOption.value.name ||
currentOption.value.label ||
currentOption.value.REAL_NAME ||
currentOption.value.MENU_NAME ||
currentOption.value.title
)
}
// 如果没有找到选项,直接返回原值(或根据业务需求返回空)
return String(props.value ?? '')
})
const tagType = computed(() => {
let result: 'default' | 'primary' | 'info' | 'success' | 'warning' | 'error' = (props.type || props.defaultType || 'default') as any
const valNorm = currentValue.value
if (props.typeMap) {
if (typeof props.typeMap === 'function') {
result = props.typeMap(props.value) as any
} else {
// 在 Map 中查找时需要确保键名匹配
const map = props.typeMap as any
result = (map[valNorm!] || props.defaultType) as any
}
} else if (props.autoColor) {
// 只有开启了 autoColor 才进行内置布尔类型的视觉感知
if (valNorm === '0' || valNorm === 0 || valNorm === 'true' || valNorm === true) {
result = 'success'
} else if (valNorm === '1' || valNorm === 1 || valNorm === 'false' || valNorm === false) {
result = 'error'
}
}
return result
})
const customStyle = computed(() => {
// 默认最小宽度以保证“是/否”等短文本对齐美观
const label = currentLabel.value
const styles: any = {
minWidth: label && String(label).length <= 2 ? '45px' : 'auto',
maxWidth: '100%',
justifyContent: 'center'
}
return styles
})
</script>
<style scoped>
:deep(.n-tag__content) {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
}
</style>
<template>
<n-tree-select
ref="treeSelectInst"
v-model:value="computedValue"
:options="computedOptions"
:loading="isLoading"
:label-field="labelField"
:key-field="keyField"
:multiple="multiple"
:clearable="clearable"
:filterable="filterable"
:default-expand-all="defaultExpandAll"
v-model:show="showMenu"
v-bind="$attrs"
/>
</template>
<script setup lang="ts">
import { service } from '@/api/index'
import type { PropType, ComponentPublicInstance } from 'vue'
const props = defineProps({
/** 绑定的值 (v-model) */
value: {
type: [String, Number, Array] as PropType<string | number | any[] | null | undefined>,
default: null
},
modelValue: {
type: [String, Number, Array] as PropType<string | number | any[] | null | undefined>,
default: null
},
/** 接口 URL */
api: {
type: String,
default: ''
},
/** 接口参数 */
params: {
type: Object as PropType<Record<string, any>>,
default: () => ({})
},
/** 静态选项 */
options: {
type: Array as PropType<any[]>,
default: null
},
/** 根节点 ID (默认 '0') */
rootId: {
type: [String, Number],
default: '0'
},
/** 是否包含一个虚拟的顶级节点 */
includeRoot: {
type: Boolean,
default: false
},
/** 虚拟顶级节点的文本 */
rootLabel: {
type: String,
default: '顶级'
},
/** 是否多选 */
multiple: {
type: Boolean,
default: false
},
/** 是否可清空 */
clearable: {
type: Boolean,
default: true
},
/** 是否可搜索 */
filterable: {
type: Boolean,
default: true
},
/** 默认展开所有 */
defaultExpandAll: {
type: Boolean,
default: true
},
/** 严格模式:默认不开启(不开启时自动转字符串比对,不区分 1 和 '1') */
strict: {
type: Boolean,
default: false
},
/** 外部传入的加载状态 */
loading: {
type: Boolean,
default: false
},
/** 标签字段名,组件会根据数据自动适配,也可显式指定 */
labelField: {
type: String,
default: 'label'
},
/** 键值字段名,组件会根据数据自动适配,也可显式指定 */
keyField: {
type: String,
default: 'value'
}
})
const emit = defineEmits(['update:value', 'update:modelValue', 'change'])
const treeSelectInst = ref<(ComponentPublicInstance & { scrollTo: (options: { key: string | number }) => void }) | null>(null)
const showMenu = ref(false)
const apiData = ref<any>(null)
const internalLoading = ref(false)
const isLoading = computed(() => internalLoading.value || props.loading)
/**
* 值的标准化处理
*/
const normalizeValue = (v: any) => {
if (v === null || v === undefined || v === '') return null
return props.strict ? v : String(v)
}
/**
* 兼容 v-model 和 v-model:value
*/
const computedValue = computed<any>({
get: () => {
const val = props.modelValue ?? props.value
if (Array.isArray(val)) {
return val.map(normalizeValue).filter((v) => v !== null)
}
return normalizeValue(val)
},
set: (val: any) => {
emit('update:value', val)
emit('update:modelValue', val)
emit('change', val)
}
})
/**
* 构建树形结构的通用函数
*/
const buildTree = (data: any, rootId: string | number) => {
if (!data) return []
// 所有的比对逻辑都基于标准化后的值
const rootIdNorm = normalizeValue(rootId)
// 递归辅助函数
const build = (pid: any, source: any): any[] => {
let children: any[] = []
const pidNorm = normalizeValue(pid)
// 情况 A: 数据源是 Map 结构 (以父级 ID 为 Key,如 MENU_TREE_DATA)
if (typeof source === 'object' && !Array.isArray(source)) {
children = source[pidNorm] || []
}
// 情况 B: 数据源是扁平数组
else if (Array.isArray(source)) {
children = source.filter((item: any) => {
const itemPid =
item.PID !== undefined ? item.PID : item.pid !== undefined ? item.pid : item.parentId !== undefined ? item.parentId : ''
return normalizeValue(itemPid) === pidNorm
})
}
return children.map((item: any) => {
const node: any = { ...item }
// 自动适配 label/value
const label = item.MENU_NAME || item.TEXT || item.text || item.label || item.name || item.title
const rawValue = item.PKID !== undefined ? item.PKID : item.id !== undefined ? item.id : item.value !== undefined ? item.value : ''
const value = normalizeValue(rawValue)
if (!node.label) node.label = label
node.value = value // 强制覆盖以便组件识别
// 递归构建子节点
const sub = build(value, source)
if (sub.length > 0) {
node.children = sub
}
return node
})
}
return build(rootIdNorm, data)
}
/**
* 递归格式化已有的树形结构,补充 label 和 value 属性
*/
const formatTreeNodes = (nodes: any[]): any[] => {
if (!nodes) return []
return nodes.map((item: any) => {
const node = { ...item }
// 自动适配 label
const label = item.label || item.MENU_NAME || item.TEXT || item.text || item.name || item.title
// 自动适配 value
const rawValue = item.value !== undefined ? item.value : item.PKID !== undefined ? item.PKID : item.id !== undefined ? item.id : ''
const value = normalizeValue(rawValue)
if (node.label === undefined) node.label = label
if (node.value === undefined) node.value = value
if (item.children && item.children.length > 0) {
node.children = formatTreeNodes(item.children)
}
return node
})
}
const computedOptions = computed(() => {
let nodes: any[] = []
if (props.options) {
// 如果提供了静态 options,假设其已经是树形结构或者需要构建
// 这里为了灵活性,如果是扁平数组则构建,否则直接返回
nodes =
Array.isArray(props.options) && props.options.some((o) => o.pid !== undefined || o.PID !== undefined)
? buildTree(props.options, props.rootId)
: formatTreeNodes(props.options)
} else if (apiData.value) {
nodes = buildTree(apiData.value, props.rootId)
}
if (props.includeRoot) {
return [
{
[props.labelField]: props.rootLabel,
[props.keyField]: String(props.rootId),
children: nodes
}
]
}
return nodes
})
/**
* 获取 API 数据
*/
const fetchApiData = async () => {
if (!props.api) return
internalLoading.value = true
try {
// 直接使用 service.post 以获取完整的 data 结构 (支持 map 类型)
const res = await service.post(props.api, props.params)
if (res.code === 200) {
apiData.value = res.data
}
} catch (e) {
console.error('[CommonTreeSelect] Fetch failed:', e)
apiData.value = null
} finally {
internalLoading.value = false
}
}
// 监听 API 及其参数变化
watch(
() => props.api,
(val) => {
if (val) fetchApiData()
},
{ immediate: true }
)
watch(
() => JSON.stringify(props.params),
(newVal, oldVal) => {
if (newVal !== oldVal && props.api) {
fetchApiData()
}
}
)
/**
* 自动滚动到选中值 (当值变化或菜单打开时触发)
*/
watch(
[computedValue, computedOptions, showMenu],
() => {
if (!showMenu.value) return // 只有打开时才滚动
const val = Array.isArray(computedValue.value) ? computedValue.value[0] : computedValue.value
if (val !== null && val !== undefined && treeSelectInst.value) {
// 延迟执行确保内部 Tree 已经渲染
nextTick(() => {
const inst = treeSelectInst.value as any
// Naive UI 的 n-tree-select 通常通过 treeInstRef 暴露内部的 tree 实例
const targetScrollTo = inst.scrollTo || inst.treeInstRef?.scrollTo
if (typeof targetScrollTo === 'function') {
targetScrollTo({ key: val })
}
})
}
},
{ immediate: true, deep: true }
)
/**
* 清除内部数据(供外部调用)
*/
const clearData = () => {
apiData.value = null
}
defineExpose({
clearData,
fetchApiData
})
</script>
<style>
.n-tree-select-menu {
min-width: max-content !important;
max-width: 600px !important;
}
.n-tree-select-menu .n-tree-node-content {
white-space: nowrap !important;
}
.n-tree-select-menu .n-tree {
min-width: max-content !important;
}
</style>
<style scoped></style>
<template>
<CommonModal v-model="show" :title="title" style="width: 500px">
<div class="p-6">
<n-upload
v-model:file-list="fileList"
:default-upload="false"
:max="1"
:accept="acceptVal"
action="#"
@before-upload="beforeUpload"
@remove="handleRemove"
>
<n-upload-dragger v-if="fileList.length === 0">
<div class="mb-3">
<n-icon size="48" :depth="3">
<CloudUploadOutline />
</n-icon>
</div>
<n-text style="font-size: 16px">点击或将文件拖拽到这里上传</n-text>
<n-p depth="3" style="margin: 8px 0 0 0">
{{ acceptVal && acceptVal.includes('image') ? '支持上传图片格式的附件' : '支持上传各种格式的附件' }}
</n-p>
</n-upload-dragger>
</n-upload>
<n-progress v-if="uploading" type="line" :percentage="uploadProgress" :indicator-placement="'inside'" processing class="mt-4" />
</div>
<template #footer>
<div class="flex justify-end gap-2">
<CommonButton @click="show = false">取消</CommonButton>
<CommonButton type="primary" :loading="uploading" :disabled="fileList.length === 0" @click="handleConfirm">开始上传</CommonButton>
</div>
</template>
</CommonModal>
</template>
<script setup lang="ts">
import { CloudUploadOutline } from '@vicons/ionicons5'
import type { UploadFileInfo } from 'naive-ui'
import { service } from '@/api/index'
export interface UploadOptions {
/** 弹窗标题 */
title?: string
/** 上传接口地址 (FunctionCode), 不传则默认为 /v1/plugins/ATTACHMENT_UPLOAD */
api?: string
/** 允许选择的文件类型,例如 'image/*' */
accept?: string
/** 接口所需的其它业务参数 */
data?: Record<string, any>
/** 上传成功后的回调 */
onSuccess?: (res?: any) => void
}
const show = ref(false)
const uploading = ref(false)
const uploadProgress = ref(0)
const title = ref('上传附件')
const acceptVal = ref('')
const fileList = ref<UploadFileInfo[]>([])
const context = ref<UploadOptions | null>(null)
let progressTimer: any = null
const open = (options: UploadOptions) => {
context.value = options
if (options.title) title.value = options.title
acceptVal.value = options.accept || ''
fileList.value = []
uploadProgress.value = 0
uploading.value = false
show.value = true
}
const beforeUpload = () => {
// 默认不限制,子类业务如果有特定格式要求,可通过组件 prop 扩展
return true
}
const handleRemove = () => {
uploadProgress.value = 0
}
const handleConfirm = async () => {
const ctx = context.value
if (fileList.value.length === 0 || !ctx) return
const uploadApi = ctx.api || '/v1/plugins/ATTACHMENT_UPLOAD'
uploading.value = true
uploadProgress.value = 0
// 模拟进度
progressTimer = setInterval(() => {
if (uploadProgress.value < 90) {
uploadProgress.value += Math.floor(Math.random() * 5) + 1
}
}, 200)
try {
const formData = new FormData()
const file = fileList.value[0]?.file
if (file) {
formData.append('file', file)
}
// 合并业务参数(cat, sourceId 等)
if (ctx.data) {
Object.entries(ctx.data).forEach(([key, val]) => {
formData.append(key, String(val))
})
}
const res = await service.post(uploadApi, formData, { showLoading: false })
if (res.code === 200) {
uploadProgress.value = 100
window.$message.success('上传成功')
ctx.onSuccess?.(res)
setTimeout(() => {
show.value = false
}, 500)
}
} catch (e: any) {
console.error('Upload failed', e)
uploadProgress.value = 0
} finally {
uploading.value = false
if (progressTimer) clearInterval(progressTimer)
}
}
defineExpose({ open })
</script>
import type { GlobalThemeOverrides } from 'naive-ui'
export const lightThemeConfig: GlobalThemeOverrides['common'] = {
baseColor: '#FFF',
primaryColor: '#165DFF',
primaryColorHover: '#4080FF',
primaryColorPressed: '#0E42D2',
primaryColorSuppl: '#94BFFF',
infoColor: '#2080f0',
infoColorHover: '#4098fc',
infoColorPressed: '#1060c9',
infoColorSuppl: '#4098fc',
successColor: '#00B42A',
successColorHover: '#23C343',
successColorPressed: '#009A29',
successColorSuppl: '#7BE188',
warningColor: '#FF7D00',
warningColorHover: '#FF9A2E',
warningColorPressed: '#D25F00',
warningColorSuppl: '#FFCF8B',
errorColor: '#F53F3F',
errorColorHover: '#F76560',
errorColorPressed: '#CB2634',
errorColorSuppl: '#FBACA3',
textColorBase: '#000',
textColor1: '#1D2129',
textColor2: '#4E5969',
textColor3: '#86909C',
textColorDisabled: '#C9CDD4',
placeholderColor: 'rgba(194, 194, 194, 1)',
placeholderColorDisabled: 'rgba(209, 209, 209, 1)',
iconColor: 'rgba(194, 194, 194, 1)',
iconColorHover: 'rgba(146, 146, 146, 1)',
iconColorPressed: 'rgba(175, 175, 175, 1)',
iconColorDisabled: 'rgba(209, 209, 209, 1)',
dividerColor: '#e8eaed',
borderColor: '#dee2e6',
closeIconColor: 'rgba(102, 102, 102, 1)',
closeIconColorHover: 'rgba(102, 102, 102, 1)',
closeIconColorPressed: 'rgba(102, 102, 102, 1)',
closeColorHover: 'rgba(0, 0, 0, .09)',
closeColorPressed: 'rgba(0, 0, 0, .13)',
clearColor: 'rgba(194, 194, 194, 1)',
clearColorHover: 'rgba(146, 146, 146, 1)',
clearColorPressed: 'rgba(175, 175, 175, 1)',
scrollbarColor: 'rgba(0, 0, 0, 0.25)',
scrollbarColorHover: 'rgba(0, 0, 0, 0.4)',
progressRailColor: 'rgba(235, 235, 235, 1)',
railColor: 'rgb(219, 219, 223)',
popoverColor: '#fff',
tableColor: '#fff',
cardColor: '#fff',
modalColor: '#fff',
bodyColor: '#f5f7fa',
tagColor: '#eee',
avatarColor: 'rgba(204, 204, 204, 1)',
invertedColor: 'rgb(0, 20, 40)',
inputColor: 'rgba(255, 255, 255, 1)',
codeColor: 'rgb(244, 244, 248)',
tabColor: 'rgb(247, 247, 250)',
actionColor: 'rgb(250, 250, 252)',
tableHeaderColor: 'rgb(250, 250, 252)',
hoverColor: 'rgb(243, 243, 245)',
tableColorHover: 'rgba(0, 0, 100, 0.03)',
tableColorStriped: 'rgba(0, 0, 100, 0.02)',
pressedColor: 'rgb(237, 237, 239)',
opacityDisabled: '0.5',
inputColorDisabled: 'rgb(250, 250, 252)',
buttonColor2: 'rgba(46, 51, 56, .05)',
buttonColor2Hover: 'rgba(46, 51, 56, .09)',
buttonColor2Pressed: 'rgba(46, 51, 56, .13)',
// 主色
primary6: '#165DFF',
primary5: '#4080FF',
primary7: '#000D4D',
primary4: '#6AA1FF',
primary3: '#94BFFF',
primary2: '#BEDAFF',
primary1: '#E8F3FF',
// 成功色
success6: '#00B42A',
success5: '#23C343',
success7: '#004D1C',
success4: '#4CD263',
success3: '#7BE188',
success2: '#AFF0B5',
success1: '#E8FFEA',
// 警示色
warning6: '#F77234',
warning5: '#F99057',
warning7: '#CC5120',
warning4: '#F99057',
warning3: '#FCC59F',
warning2: '#FDDDC3',
warning1: '#FFF3E8',
// 错误色
danger6: '#F53F3F',
danger5: '#F76560',
danger7: '#CB272D',
danger4: '#F98981',
danger3: '#FBACA3',
danger2: '#FDCDC5',
danger1: '#FFECE8',
// 链接色
link6: '#3491FA',
link5: '#57A9FB',
link7: '#206CCF',
link4: '#7BC0FC',
link3: '#9FD4FD',
link2: '#C3E7FE',
link1: '#E8F7FF',
// 边框颜色
colorBorder1: '#f2f3f5',
colorBorder2: '#e5e6eb',
colorBorder3: '#c9cdd4',
colorBorder4: '#86909c',
// 填充颜色
colorFill1: '#f7f8fa',
colorFill2: '#f2f3f5',
colorFill3: '#e5e6eb',
colorFill4: '#c9cdd4',
// 文字颜色
colorText1: '#1d2129',
colorText2: '#4e5969',
colorText3: '#86909c',
colorText4: '#c9cdd4',
// 背景颜色
colorBg1: '#ffffff',
colorBg2: '#ffffff',
colorBg3: '#ffffff',
colorBg4: '#ffffff',
colorBg5: '#ffffff'
}
export const darkThemeConfig: GlobalThemeOverrides['common'] = {
baseColor: '#000',
primaryColor: '#3c7eff',
primaryColorHover: '#7fe7c4',
primaryColorPressed: '#5acea7',
primaryColorSuppl: 'rgb(42, 148, 125)',
infoColor: '#70c0e8',
infoColorHover: '#8acbec',
infoColorPressed: '#66afd3',
infoColorSuppl: 'rgb(56, 137, 197)',
successColor: '#3c7eff',
successColorHover: '#7fe7c4',
successColorPressed: '#5acea7',
successColorSuppl: 'rgb(42, 148, 125)',
warningColor: '#f2c97d',
warningColorHover: '#f5d599',
warningColorPressed: '#e6c260',
warningColorSuppl: 'rgb(240, 138, 0)',
errorColor: '#e88080',
errorColorHover: '#e98b8b',
errorColorPressed: '#e57272',
errorColorSuppl: 'rgb(208, 58, 82)',
textColorBase: '#fff',
textColor1: 'rgba(255, 255, 255, 0.9)',
textColor2: 'rgba(255, 255, 255, 0.82)',
textColor3: 'rgba(255, 255, 255, 0.52)',
textColorDisabled: 'rgba(255, 255, 255, 0.38)',
placeholderColor: 'rgba(255, 255, 255, 0.38)',
placeholderColorDisabled: 'rgba(255, 255, 255, 0.28)',
iconColor: 'rgba(255, 255, 255, 0.38)',
iconColorDisabled: 'rgba(255, 255, 255, 0.28)',
iconColorHover: 'rgba(255, 255, 255, 0.475)',
iconColorPressed: 'rgba(255, 255, 255, 0.30400000000000005)',
dividerColor: 'rgba(255, 255, 255, 0.09)',
borderColor: 'rgba(255, 255, 255, 0.24)',
closeIconColorHover: 'rgba(255, 255, 255, 0.52)',
closeIconColor: 'rgba(255, 255, 255, 0.52)',
closeIconColorPressed: 'rgba(255, 255, 255, 0.52)',
closeColorHover: 'rgba(255, 255, 255, .12)',
closeColorPressed: 'rgba(255, 255, 255, .08)',
clearColor: 'rgba(255, 255, 255, 0.38)',
clearColorHover: 'rgba(255, 255, 255, 0.48)',
clearColorPressed: 'rgba(255, 255, 255, 0.3)',
scrollbarColor: 'rgba(255, 255, 255, 0.2)',
scrollbarColorHover: 'rgba(255, 255, 255, 0.3)',
progressRailColor: 'rgba(255, 255, 255, 0.12)',
railColor: 'rgba(255, 255, 255, 0.2)',
popoverColor: 'rgb(72, 72, 78)',
tableColor: 'rgb(24, 24, 28)',
cardColor: 'rgb(24, 24, 28)',
modalColor: 'rgb(44, 44, 50)',
bodyColor: 'rgb(16, 16, 20)',
tagColor: 'rgba(51, 51, 51, 1)',
avatarColor: 'rgba(255, 255, 255, 0.18)',
invertedColor: '#000',
inputColor: 'rgba(255, 255, 255, 0.1)',
codeColor: 'rgba(255, 255, 255, 0.12)',
tabColor: 'rgba(255, 255, 255, 0.04)',
actionColor: 'rgba(255, 255, 255, 0.06)',
tableHeaderColor: 'rgba(255, 255, 255, 0.06)',
hoverColor: 'rgba(255, 255, 255, 0.09)',
tableColorHover: 'rgba(255, 255, 255, 0.06)',
tableColorStriped: 'rgba(255, 255, 255, 0.05)',
pressedColor: 'rgba(255, 255, 255, 0.05)',
opacityDisabled: '0.38',
inputColorDisabled: 'rgba(255, 255, 255, 0.06)',
buttonColor2: 'rgba(255, 255, 255, .08)',
buttonColor2Hover: 'rgba(255, 255, 255, .12)',
buttonColor2Pressed: 'rgba(255, 255, 255, .08)',
// 主色
primary6: '#3c7eff',
primary5: '#306fff',
primary7: '#689fff',
primary4: '#1d4dd2',
primary3: '#0e32a6',
primary2: '#041b79',
primary1: '#000d4d',
// 成功色
success6: '#27c346',
success5: '#1db440',
success7: '#50d266',
success4: '#129a37',
success3: '#0a802d',
success2: '#046625',
success1: '#004d1c',
// 警示色
warning6: '#ff9626',
warning5: '#ff8d1f',
warning7: '#ffb357',
warning4: '#d26913',
warning3: '#a64b0a',
warning2: '#793004',
warning1: '#4d1b00',
// 错误色
danger6: '#f76965',
danger5: '#f54e4e',
danger7: '#f98d86',
danger4: '#cb2e34',
danger3: '#a1161f',
danger2: '#770611',
danger1: '#4d000a',
// 链接色
link6: '#3c7eff',
link5: '#306fff',
link7: '#689fff',
link4: '#1d4dd2',
link3: '#0e32a6',
link2: '#041b79',
link1: '#000d4d',
// 边框颜色
colorBorder1: '#2e2e30',
colorBorder2: '#484849',
colorBorder3: '#5f5f60',
colorBorder4: '#929293',
// 填充颜色
colorFill1: '#17171a',
colorFill2: '#2e2e30',
colorFill3: '#484849',
colorFill4: '#5f5f60',
// 文字颜色
colorText1: '#f6f6f6',
colorText2: '#c5c5c5',
colorText3: '#929293',
colorText4: '#5f5f60',
// 背景颜色
colorBg1: '#17171A',
colorBg2: '#232324',
colorBg3: '#2A2A2B',
colorBg4: '#313132',
colorBg5: '#373739'
}
import { useAppStore } from '@/store/app'
import { createDiscreteApi, darkTheme } from 'naive-ui'
import type { DialogOptions } from 'naive-ui'
/**
* 通用 Dialog 封装
* 1. 支持标准 Naive UI 的对象调用方式(推荐): dialog.warning({ title: '', content: '' })
* 2. 也兼容传统的 (title, content, options) 传参方式
* 3. 自动转换为 Promise 风格
*/
const createDialog = (type: 'info' | 'error' | 'success' | 'warning') => {
return function (titleOrOptions: string | DialogOptions, content?: string, config: DialogOptions = {}): Promise<boolean> {
return new Promise((resolve, reject) => {
const appStore = useAppStore()
const { dialog } = createDiscreteApi(['dialog'], {
configProviderProps: { theme: appStore.isDark ? darkTheme : null }
})
let finalOptions: DialogOptions = {}
if (typeof titleOrOptions === 'object') {
// 情况 1: 对象传参
finalOptions = { ...titleOrOptions }
} else {
// 情况 2: (title, content, options) 传参
finalOptions = {
title: titleOrOptions,
content: content,
...config
}
}
// 业务默认配置
const businessConfig: DialogOptions = {
draggable: true,
onPositiveClick: () => resolve(true),
onNegativeClick: () => reject(false),
onClose: () => reject(false),
onEsc: () => reject(false),
onMaskClick: () => reject(false)
}
if (type === 'warning') {
businessConfig.positiveText = '确定'
businessConfig.negativeText = '取消'
}
// 合并配置: 业务默认 < 传入配置
dialog[type]({
...businessConfig,
...finalOptions
})
})
}
}
export const useDialog = () => {
return {
info: createDialog('info'),
error: createDialog('error'),
success: createDialog('success'),
warning: createDialog('warning')
}
}
import mitt from 'mitt'
import { onBeforeUnmount } from 'vue'
type Fn = (...args: any[]) => void
interface Option {
name: string // 事件名称
callback: Fn // 回调
}
const emitter = mitt<any>()
export const useEventBus = (option?: Option) => {
if (option) {
const { name, callback } = option
// 注册事件监听器
emitter.on(name, callback)
// 组件卸载时注销事件监听器
onBeforeUnmount(() => {
emitter.off(name, callback)
})
}
// 返回事件总线的方法
return {
on: emitter.on,
off: emitter.off,
emit: emitter.emit,
all: emitter.all
}
}
import { useAppStore } from '@/store/app'
import { createDiscreteApi, darkTheme } from 'naive-ui'
export const useMessage = () => {
return {
// 消息提示
info(content: string, config = {}) {
const appStore = useAppStore()
const { message } = createDiscreteApi(['message'], {
configProviderProps: { theme: appStore.isDark ? darkTheme : null }
})
message.info(content, config)
},
// 错误消息
error(content: string, config = {}) {
const appStore = useAppStore()
const { message } = createDiscreteApi(['message'], {
configProviderProps: { theme: appStore.isDark ? darkTheme : null }
})
message.error(content, config)
},
// 成功消息
success(content: string, config = {}) {
const appStore = useAppStore()
const { message } = createDiscreteApi(['message'], {
configProviderProps: { theme: appStore.isDark ? darkTheme : null }
})
message.success(content, config)
},
// 警告消息
warning(content: string, config = {}) {
const appStore = useAppStore()
const { message } = createDiscreteApi(['message'], {
configProviderProps: { theme: appStore.isDark ? darkTheme : null }
})
message.warning(content, config)
}
}
}
import { useAppStore } from '@/store/app'
import { createDiscreteApi, darkTheme } from 'naive-ui'
export const useNotification = () => {
return {
// 通知提示
info(content: string, config = {}) {
const appStore = useAppStore()
const { notification } = createDiscreteApi(['notification'], {
configProviderProps: { theme: appStore.isDark ? darkTheme : null }
})
notification.info({
content,
...config
})
},
// 错误通知
error(content: string, config = {}) {
const appStore = useAppStore()
const { notification } = createDiscreteApi(['notification'], {
configProviderProps: { theme: appStore.isDark ? darkTheme : null }
})
notification.error({
content,
...config
})
},
// 成功通知
success(content: string, config = {}) {
const appStore = useAppStore()
const { notification } = createDiscreteApi(['notification'], {
configProviderProps: { theme: appStore.isDark ? darkTheme : null }
})
notification.success({
content,
...config
})
},
// 警告通知
warning(content: string, config = {}) {
const appStore = useAppStore()
const { notification } = createDiscreteApi(['notification'], {
configProviderProps: { theme: appStore.isDark ? darkTheme : null }
})
notification.warning({
content,
...config
})
}
}
}
interface MessageHandler {
(event: MessageEvent): void
}
interface MessageOption {
type: string // 消息类型
callback: MessageHandler // 回调函数
}
interface UsePostMessageOptions {
target?: 'parent' | Ref<HTMLIFrameElement | null> // 目标:parent 表示父窗口,传入 iframe ref 表示子窗口
type?: string // 消息类型
callback?: MessageHandler // 回调函数
}
export interface UsePostMessageReturn {
emit: (type: string, data?: any, targetOrigin?: string) => void
on: (type: string, callback: (data: any, event: MessageEvent) => void) => () => void
off: (type: string, callback: MessageHandler) => void
once: (type: string, timeout?: number) => Promise<any>
cleanup: () => void // 手动清理所有监听器
// RPC 方法:调用远程方法并返回 Promise
call: <T = any>(method: string, data?: any, timeout?: number) => Promise<T>
// RPC 方法:注册可被远程调用的方法
register: (method: string, handler: (data: any) => Promise<any> | any) => () => void
}
/**
* postMessage 通信 Hook
* 用于 iframe 与父窗口之间的双向 postMessage 通信
* @param options 配置项
* - target: 'parent' 表示向父窗口发送消息(用于 iframe 内部),传入 iframe ref 表示向 iframe 发送消息(用于父窗口)
* - type: 消息类型(可选)
* - callback: 回调函数(可选)
*/
export const usePostMessage = (options?: UsePostMessageOptions | MessageOption): UsePostMessageReturn => {
// 兼容旧的 API
let target: 'parent' | Ref<HTMLIFrameElement | null> = 'parent'
let initType: string | undefined
let initCallback: MessageHandler | undefined
if (options) {
// 新 API
if ('target' in options) {
target = options.target || 'parent'
initType = options.type
initCallback = options.callback
} else {
// 旧 API 兼容
initType = options.type
initCallback = options.callback
}
}
// 维护所有监听器的列表,用于手动清理
const listeners: Array<{ handler: (event: MessageEvent) => void }> = []
// 清理所有监听器的函数
const cleanup = () => {
listeners.forEach(({ handler }) => {
window.removeEventListener('message', handler)
})
listeners.length = 0 // 清空数组
}
// 如果提供了初始化选项,自动注册监听器
if (initType && initCallback) {
const messageHandler = (event: MessageEvent) => {
if (event.data && event.data.type === initType && initCallback) {
initCallback(event)
}
}
// 注册消息监听器
window.addEventListener('message', messageHandler)
// 添加到监听器列表
listeners.push({ handler: messageHandler })
}
/**
* 发送消息
* @param type 消息类型
* @param data 消息数据
* @param targetOrigin 目标源(默认为 '*')
*/
const emit = (type: string, data?: any, targetOrigin: string = '*') => {
const message = { type, data }
if (target === 'parent') {
// 向父窗口发送消息
window.parent.postMessage(message, targetOrigin)
} else {
// 向 iframe 发送消息
target.value?.contentWindow?.postMessage(message, targetOrigin)
}
}
/**
* 监听指定类型的消息
* @param type 消息类型
* @param callback 回调函数
* @returns 返回移除监听器的函数
*/
const on = (type: string, callback: (data: any, event: MessageEvent) => void): (() => void) => {
const messageHandler = (event: MessageEvent) => {
if (event.data && event.data.type === type) {
callback(event.data.data, event)
}
}
window.addEventListener('message', messageHandler)
// 添加到监听器列表,组件卸载时自动清理
const listenerItem = { handler: messageHandler }
listeners.push(listenerItem)
// 返回移除监听器的函数
return () => {
window.removeEventListener('message', messageHandler)
// 从列表中移除
const index = listeners.indexOf(listenerItem)
if (index > -1) {
listeners.splice(index, 1)
}
}
}
/**
* 移除消息监听器
* @param type 消息类型
* @param callback 回调函数
*/
const off = (type: string, callback: MessageHandler) => {
window.removeEventListener('message', callback)
}
/**
* 监听一次消息后自动移除
* @param type 消息类型
* @param timeout 超时时间(毫秒),超时后自动移除监听器
* @returns Promise,resolve 消息数据,reject 超时错误
*/
const once = (type: string, timeout?: number): Promise<any> => {
return new Promise((resolve, reject) => {
let timeoutId: any = null
const messageHandler = (event: MessageEvent) => {
if (event.data && event.data.type === type) {
if (timeoutId) clearTimeout(timeoutId)
window.removeEventListener('message', messageHandler)
// 从列表中移除
const index = listeners.findIndex((item) => item.handler === messageHandler)
if (index > -1) {
listeners.splice(index, 1)
}
resolve(event.data.data)
}
}
window.addEventListener('message', messageHandler)
// 添加到监听器列表,组件卸载时自动清理
listeners.push({ handler: messageHandler })
// 设置超时
if (timeout) {
timeoutId = setTimeout(() => {
window.removeEventListener('message', messageHandler)
// 从列表中移除
const index = listeners.findIndex((item) => item.handler === messageHandler)
if (index > -1) {
listeners.splice(index, 1)
}
reject(new Error(`等待消息 ${type} 超时`))
}, timeout)
}
})
}
/**
* RPC 调用:调用远程方法并等待返回结果
* @param method 方法名
* @param data 参数
* @param timeout 超时时间(毫秒)
* @returns Promise,resolve 返回值,reject 错误
*/
const call = <T = any>(method: string, data?: any, timeout: number = 30000): Promise<T> => {
return new Promise((resolve, reject) => {
// 生成唯一的调用 ID
const callId = `${method}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
let timeoutId: any = null
// 监听响应
const responseHandler = (event: MessageEvent) => {
if (event.data && event.data.type === '__RPC_RESPONSE__' && event.data.data && event.data.data.callId === callId) {
if (timeoutId) clearTimeout(timeoutId)
window.removeEventListener('message', responseHandler)
// 从监听器列表中移除
const index = listeners.findIndex((item) => item.handler === responseHandler)
if (index > -1) {
listeners.splice(index, 1)
}
const { success, result, error } = event.data.data
if (success) {
resolve(result)
} else {
reject(new Error(error || '远程调用失败'))
}
}
}
window.addEventListener('message', responseHandler)
listeners.push({ handler: responseHandler })
// 发送调用请求
const message = {
type: '__RPC_CALL__',
data: {
callId,
method,
params: data
}
}
if (target === 'parent') {
window.parent.postMessage(message, '*')
} else {
target.value?.contentWindow?.postMessage(message, '*')
}
// 设置超时
if (timeout) {
timeoutId = setTimeout(() => {
window.removeEventListener('message', responseHandler)
const index = listeners.findIndex((item) => item.handler === responseHandler)
if (index > -1) {
listeners.splice(index, 1)
}
reject(new Error(`调用方法 ${method} 超时`))
}, timeout)
}
})
}
/**
* RPC 注册:注册可被远程调用的方法
* @param method 方法名
* @param handler 处理函数,可以返回 Promise 或普通值
* @returns 返回取消注册的函数
*/
const register = (method: string, handler: (data: any) => Promise<any> | any): (() => void) => {
const requestHandler = async (event: MessageEvent) => {
if (event.data && event.data.type === '__RPC_CALL__' && event.data.data && event.data.data.method === method) {
const { callId, params } = event.data.data
try {
// 执行处理函数,支持异步和同步
const result = await handler(params)
// 发送成功响应
const responseMessage = {
type: '__RPC_RESPONSE__',
data: {
callId,
success: true,
result
}
}
if (target === 'parent') {
window.parent.postMessage(responseMessage, '*')
} else {
target.value?.contentWindow?.postMessage(responseMessage, '*')
}
} catch (error: any) {
// 发送失败响应
const responseMessage = {
type: '__RPC_RESPONSE__',
data: {
callId,
success: false,
error: error.message || '方法执行失败'
}
}
if (target === 'parent') {
window.parent.postMessage(responseMessage, '*')
} else {
target.value?.contentWindow?.postMessage(responseMessage, '*')
}
}
}
}
window.addEventListener('message', requestHandler)
listeners.push({ handler: requestHandler })
// 返回取消注册的函数
return () => {
window.removeEventListener('message', requestHandler)
const index = listeners.findIndex((item) => item.handler === requestHandler)
if (index > -1) {
listeners.splice(index, 1)
}
}
}
return {
emit,
on,
off,
once,
cleanup,
call,
register
}
}
/**
* 统一列表页面搜索逻辑 Hook
*/
export function useTableSearch<T extends object>(
defaultForm: T,
tableRef?: any,
options?: {
exportModalRef?: any
importModalRef?: any
fileName?: string
functionCode?: string
importTitle?: string
importApi?: string
templateName?: string
templateApi?: string
templateTitle?: string
}
) {
// UI 绑定的表单状态
const searchForm = reactive({ ...defaultForm })
// 实际触发表格请求的参数状态
const tableParams = reactive({ ...defaultForm })
const handleSearch = () => {
// 判断参数是否发生实质性变化
const isChanged = JSON.stringify(searchForm) !== JSON.stringify(tableParams)
Object.assign(tableParams, searchForm)
if (isChanged) {
// 参数变了,必须跳回第一页(Search 行为)
tableRef?.value?.reSearch()
} else {
// 参数没变,只是刷新当前页数据(Refresh 行为)
tableRef?.value?.refresh()
}
}
const resetSearch = () => {
// 重置 UI 表单
Object.assign(searchForm, defaultForm)
// 重置请求参数
Object.assign(tableParams, defaultForm)
// 重新查询
tableRef.value?.reSearch()
}
/** 统一处理导出逻辑 */
const handleExport = (overrideFileName?: string, overrideFunctionCode?: string) => {
// 核心修复:如果第一个参数是事件对象(来自 @click="handleExport"),则忽略它作为文件名
const isString = typeof overrideFileName === 'string'
const fileName = (isString ? overrideFileName : options?.fileName) || '导出文件'
const functionCode = overrideFunctionCode || options?.functionCode
// 获取当前表格的页码和页大小
const page = tableRef?.value?.currentPage
const rows = tableRef?.value?.currentPageSize
const modal = options?.exportModalRef?.value || window.$exportModal
if (modal) {
modal.open({
fileName,
functionCode,
params: {
...tableParams,
page,
rows
}
})
} else {
console.warn('useTableSearch: Global $exportModal or local exportModalRef is not provided.')
}
}
/** 统一处理导入逻辑 */
const handleImport = () => {
const modal = options?.importModalRef?.value || window.$importModal
if (modal) {
modal.open({
title: options?.importTitle,
api: options?.importApi,
templateName: options?.templateName,
templateApi: options?.templateApi,
templateTitle: options?.templateTitle,
onSuccess: () => tableRef.value?.refresh()
})
} else {
console.warn('useTableSearch: Global $importModal or local importModalRef is not provided.')
}
}
return {
searchForm,
tableParams,
handleSearch,
resetSearch,
handleExport,
handleImport
}
}
<template>
<n-layout has-sider class="h-screen">
<n-layout-sider
collapse-mode="width"
:collapsed-width="64"
:width="240"
:collapsed="collapsed"
:native-scrollbar="true"
@collapse="collapsed = true"
@expand="collapsed = false"
bordered
class="sider-with-trigger"
>
<div class="sider-inner">
<!-- Logo 区 -->
<div class="h-16 px-4 flex items-center justify-center border-b flex-shrink-0" :style="{ borderColor: themeVars.dividerColor }">
<n-icon size="32" :color="themeVars.primaryColor"><airplane-outline /></n-icon>
<span
v-if="!collapsed"
class="text-xl font-black ml-3 truncate tracking-widest transition-colors"
:style="{ color: themeVars.textColor1 }"
>
AMRO 系统
</span>
</div>
<!-- 底部固定按钮 -->
<div class="sider-collapse-btn-wrap flex-shrink-0" :style="{ borderColor: themeVars.dividerColor }">
<div
class="sider-collapse-btn"
:style="{ borderColor: themeVars.dividerColor, background: themeVars.cardColor }"
@click="collapsed = !collapsed"
>
<n-icon size="18" :color="themeVars.textColor2">
<menu-outline />
</n-icon>
</div>
</div>
</div>
</n-layout-sider>
<n-layout class="flex flex-col h-screen overflow-hidden">
<n-layout-header
bordered
:position="appStore.fixedHeader ? 'absolute' : 'static'"
class="h-16 flex items-center justify-between px-6 flex-shrink-0 transition-colors"
:style="{ zIndex: appStore.fixedHeader ? 10 : 'auto' }"
>
<div class="flex items-center space-x-3">
<!-- 主题切换 -->
<CommonButton quaternary circle @click="appStore.isDark = !appStore.isDark">
<template #icon>
<n-icon>
<sunny-outline v-if="appStore.isDark" />
<moon-outline v-else />
</n-icon>
</template>
</CommonButton>
<!-- 偏好设置 -->
<SettingsDrawer />
</div>
</n-layout-header>
<n-layout-content
:content-style="{
paddingTop: appStore.contentCompact ? '6px' : '12px',
paddingBottom: appStore.contentCompact ? '6px' : '12px',
paddingLeft: appStore.contentCompact ? '6px' : '12px',
paddingRight: appStore.settingsPinned && appStore.settingsOpen ? '332px' : appStore.contentCompact ? '6px' : '12px',
display: 'flex',
flexDirection: 'column',
transition: 'all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1)'
}"
class="transition-colors"
:style="{ backgroundColor: themeVars.bodyColor }"
>
<router-view v-slot="{ Component, route }">
<transition :name="appStore.transitionName || 'none'" mode="out-in">
<keep-alive>
<component :is="Component" :key="route.fullPath" class="flex-1 flex flex-col min-h-0" />
</keep-alive>
</transition>
</router-view>
</n-layout-content>
</n-layout>
</n-layout>
<CommonImportModal ref="globalImportModalRef" />
<CommonExportModal ref="globalExportModalRef" />
<CommonUploadModal ref="globalUploadModalRef" />
<CommonDownloadModal ref="globalDownloadModalRef" />
</template>
<script setup lang="ts">
import { useAppStore } from '@/store/app/index'
import SettingsDrawer from './components/SettingsDrawer.vue'
import CommonDownloadModal from '@/components/CommonDownloadModal.vue'
import { AirplaneOutline, SunnyOutline, MoonOutline, MenuOutline } from '@vicons/ionicons5'
import router from '@/router/index'
const themeVars = useThemeVars()
const route = useRoute()
const appStore = useAppStore()
const globalImportModalRef = ref()
const globalExportModalRef = ref()
const globalAttachmentModalRef = ref()
const globalUploadModalRef = ref()
const globalPreviewModalRef = ref()
const globalDownloadModalRef = ref()
const collapsed = computed({
get: () => appStore.collapsed,
set: (val) => (appStore.collapsed = val)
})
const activeKey = ref(route.path)
// 菜单数据已通过 Pinia 持久化,无需在此重复获取
// 全局快捷键处理
const handleGlobalKeydown = (e: KeyboardEvent) => {
const ctrl = e.ctrlKey || e.metaKey
const alt = e.altKey
const shift = e.shiftKey
// Ctrl+Shift+D: 切换深色/浅色主题
if (ctrl && shift && e.key.toLowerCase() === 'd') {
e.preventDefault()
appStore.isDark = !appStore.isDark
return
}
// Alt+H: 返回首页
if (alt && e.key.toLowerCase() === 'h') {
e.preventDefault()
router.push('/')
return
}
}
onMounted(() => {
window.addEventListener('keydown', handleGlobalKeydown)
window.$importModal = globalImportModalRef.value
window.$exportModal = globalExportModalRef.value
window.$attachmentModal = globalAttachmentModalRef.value
window.$uploadModal = globalUploadModalRef.value
window.$previewModal = globalPreviewModalRef.value
window.$downloadModal = globalDownloadModalRef.value
})
onUnmounted(() => {
window.removeEventListener('keydown', handleGlobalKeydown)
})
</script>
<style scoped>
/* 无动画 */
.none-enter-active,
.none-leave-active {
transition: none !important;
}
/* 淡入淡出 */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
/* 滑动渐变 (原默认动画) */
.fade-slide-enter-active,
.fade-slide-leave-active {
transition: all 0.3s ease;
}
.fade-slide-enter-from {
opacity: 0;
transform: translateX(-20px);
}
.fade-slide-leave-to {
opacity: 0;
transform: translateX(20px);
}
/* 缩放渐变 */
.zoom-enter-active,
.zoom-leave-active {
transition: all 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.zoom-enter-from,
.zoom-leave-to {
opacity: 0;
transform: scale(0.96);
}
/* 沉浸式风格:选中背景充满侧边栏宽度 */
:deep(.n-menu-item-content) {
border-radius: 0 !important;
margin: 1px 0 !important; /* 保持极小的上下间隙以区分项 */
transition: all 0.3s ease !important;
}
/* 修复折叠状态下的全宽居中问题 */
:deep(.n-menu.n-menu--collapsed .n-menu-item-content) {
padding: 0 !important;
margin: 1px 0 !important;
display: flex !important;
justify-content: center !important;
align-items: center !important;
width: 64px !important; /* 必须显式指定宽度以覆盖 Naive UI 内部计算值 */
}
:deep(.n-menu.n-menu--collapsed .n-menu-item-content .n-menu-item-content__icon) {
margin-right: 0 !important;
}
/* 确保折叠时文字和箭头不占位 */
:deep(.n-menu.n-menu--collapsed .n-menu-item-content-header),
:deep(.n-menu.n-menu--collapsed .n-menu-item-content__arrow) {
display: none !important;
}
:deep(.n-menu-item-content--selected) {
font-weight: 600;
}
/* 自定义折叠按钮区域 */
.sider-collapse-btn-wrap {
padding: 10px 0 12px;
display: flex;
justify-content: center;
border-top: 1px solid;
}
.sider-collapse-btn {
width: 40px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid;
border-radius: 6px;
cursor: pointer;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
transition: all 0.2s ease;
}
.sider-collapse-btn:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
transform: translateY(-1px);
}
/* sider 内部 flex 列布局,按钮固定底部 */
.sider-with-trigger :deep(.n-layout-sider-scroll-container) {
overflow: hidden !important;
}
.sider-inner {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
.sider-menu-scroll {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
}
</style>
<template>
<!-- 触发按钮 -->
<CommonButton quaternary circle @click="appStore.settingsOpen = true">
<template #icon>
<n-icon><settings-outline /></n-icon>
</template>
</CommonButton>
<!-- 偏好设置抽屉 -->
<n-drawer
v-model:show="appStore.settingsOpen"
:width="320"
placement="right"
:show-mask="!appStore.settingsPinned"
:mask-closable="!appStore.settingsPinned"
:block-scroll="!appStore.settingsPinned"
>
<n-drawer-content>
<template #header>
<div class="flex items-center justify-between w-full">
<div>
<div class="font-bold text-base">偏好设置</div>
<div class="text-xs mt-0.5" :style="{ color: themeVars.textColor3 }">自定义偏好设置 &amp; 实时预览</div>
</div>
<n-space :size="8">
<n-tooltip trigger="hover" placement="bottom">
<template #trigger>
<CommonButton quaternary circle size="small" @click="resetPrefs">
<template #icon>
<n-icon><reload-outline /></n-icon>
</template>
</CommonButton>
</template>
重置默认
</n-tooltip>
<n-tooltip trigger="hover" placement="bottom">
<template #trigger>
<CommonButton quaternary circle size="small" @click="appStore.settingsPinned = !appStore.settingsPinned">
<template #icon>
<n-icon :color="appStore.settingsPinned ? themeVars.primaryColor : undefined">
<pin-outline />
</n-icon>
</template>
</CommonButton>
</template>
{{ appStore.settingsPinned ? '取消固定' : '固定面板' }}
</n-tooltip>
</n-space>
</div>
</template>
<n-tabs type="line" animated :tab-style="{ padding: '8px 12px' }">
<!-- 外观 tab -->
<n-tab-pane name="appearance" tab="外观">
<!-- 主题模式 -->
<div class="mb-5">
<div class="text-sm font-semibold mb-3" :style="{ color: themeVars.textColor2 }">主题</div>
<div class="grid grid-cols-3 gap-2">
<div
v-for="mode in themeModes"
:key="mode.value"
class="flex flex-col items-center gap-1.5 p-3 rounded-lg cursor-pointer border-2 transition-all"
:style="{
borderColor: currentThemeMode === mode.value ? themeVars.primaryColor : themeVars.borderColor,
backgroundColor: currentThemeMode === mode.value ? themeVars.primaryColor + '10' : 'transparent'
}"
@click="setThemeMode(mode.value)"
>
<n-icon size="20" :color="currentThemeMode === mode.value ? themeVars.primaryColor : themeVars.textColor3">
<component :is="mode.icon" />
</n-icon>
<span
class="text-xs"
:style="{ color: currentThemeMode === mode.value ? themeVars.primaryColor : themeVars.textColor3 }"
>
{{ mode.label }}
</span>
</div>
</div>
</div>
<n-divider class="my-4" />
<!-- 内置主题色 -->
<div class="mb-5">
<div class="text-sm font-semibold mb-3" :style="{ color: themeVars.textColor2 }">内置主题</div>
<div class="grid grid-cols-3 gap-2">
<div
v-for="preset in colorPresets"
:key="preset.color"
class="flex flex-col items-center gap-1.5 p-2 rounded-lg cursor-pointer border-2 transition-all"
:style="{
borderColor: appStore.primaryColor === preset.color ? themeVars.primaryColor : themeVars.borderColor
}"
@click="setColor(preset.color)"
>
<div class="w-7 h-7 rounded-lg" :style="{ backgroundColor: preset.color }" />
<span class="text-xs" :style="{ color: themeVars.textColor3 }">{{ preset.label }}</span>
</div>
<!-- 自定义 -->
<div
class="flex flex-col items-center gap-1.5 p-2 rounded-lg cursor-pointer border-2 transition-all"
:style="{ borderColor: themeVars.borderColor }"
>
<n-color-picker
v-model:value="customColor"
:show-alpha="false"
:swatches="[]"
placement="bottom"
@update:value="setColor"
>
<template #label><span /></template>
</n-color-picker>
<span class="text-xs" :style="{ color: themeVars.textColor3 }">自定义</span>
</div>
</div>
</div>
<n-divider class="my-4" />
<!-- 圆角 -->
<div class="mb-5">
<div class="text-sm font-semibold mb-3" :style="{ color: themeVars.textColor2 }">圆角</div>
<div class="flex gap-2">
<div
v-for="r in borderRadiusOptions"
:key="r"
class="flex-1 text-center py-1.5 rounded-lg cursor-pointer border-2 text-sm font-medium transition-all"
:style="{
borderColor: appStore.borderRadius === r ? themeVars.primaryColor : themeVars.borderColor,
color: appStore.borderRadius === r ? themeVars.primaryColor : themeVars.textColor3,
backgroundColor: appStore.borderRadius === r ? themeVars.primaryColor + '10' : 'transparent'
}"
@click="setBorderRadius(r)"
>
{{ r }}
</div>
</div>
</div>
<!-- 字体大小 -->
<div class="mb-5">
<div class="text-sm font-semibold mb-3" :style="{ color: themeVars.textColor2 }">字体大小</div>
<div class="flex items-center gap-3">
<CommonButton size="small" circle quaternary @click="decreaseFontSize">
<template #icon>
<n-icon><remove-outline /></n-icon>
</template>
</CommonButton>
<div
class="flex-1 text-center py-1.5 rounded-lg border text-sm font-bold"
:style="{ borderColor: themeVars.borderColor, color: themeVars.textColor1 }"
>
{{ appStore.fontSize }}
</div>
<CommonButton size="small" circle quaternary @click="increaseFontSize">
<template #icon>
<n-icon><add-outline /></n-icon>
</template>
</CommonButton>
<span class="text-xs" :style="{ color: themeVars.textColor3 }">px</span>
</div>
<div class="text-xs mt-2" :style="{ color: themeVars.textColor3 }">调整全局字体大小,实时预览效果</div>
</div>
<n-divider class="my-4" />
<!-- 页面切换动画 -->
<div class="mb-5">
<div class="text-sm font-semibold mb-3" :style="{ color: themeVars.textColor2 }">页面切换动画</div>
<n-select v-model:value="appStore.transitionName" :options="transitionOptions" placeholder="请选择切换动画" />
</div>
<n-divider class="my-4" />
<!-- 其它 -->
<div>
<div class="text-sm font-semibold mb-3" :style="{ color: themeVars.textColor2 }">其它</div>
<div class="flex items-center justify-between mb-3">
<span class="text-sm" :style="{ color: themeVars.textColor2 }">色弱模式</span>
<n-switch v-model:value="appStore.colorWeak" @update:value="applyAndSave" />
</div>
<div class="flex items-center justify-between">
<span class="text-sm" :style="{ color: themeVars.textColor2 }">灰色模式</span>
<n-switch v-model:value="appStore.grayMode" @update:value="applyAndSave" />
</div>
</div>
</n-tab-pane>
<!-- 布局 tab -->
<n-tab-pane name="layout" tab="布局">
<div class="space-y-1">
<div class="text-sm font-semibold mb-3" :style="{ color: themeVars.textColor2 }">界面元素</div>
<!-- 标签栏 -->
<div class="flex items-center justify-between py-3 border-b" :style="{ borderColor: themeVars.dividerColor }">
<div>
<div class="text-sm" :style="{ color: themeVars.textColor1 }">显示标签栏</div>
<div class="text-xs mt-0.5" :style="{ color: themeVars.textColor3 }">顶部多标签页导航</div>
</div>
</div>
<!-- 面包屑 -->
<div class="flex items-center justify-between py-3 border-b" :style="{ borderColor: themeVars.dividerColor }">
<div>
<div class="text-sm" :style="{ color: themeVars.textColor1 }">显示面包屑</div>
<div class="text-xs mt-0.5" :style="{ color: themeVars.textColor3 }">顶栏左侧路径导航</div>
</div>
<n-switch v-model:value="appStore.showBreadcrumb" />
</div>
<!-- 固定顶栏 -->
<div class="flex items-center justify-between py-3 border-b" :style="{ borderColor: themeVars.dividerColor }">
<div>
<div class="text-sm" :style="{ color: themeVars.textColor1 }">固定顶栏</div>
<div class="text-xs mt-0.5" :style="{ color: themeVars.textColor3 }">滚动时顶栏保持可见</div>
</div>
<n-switch v-model:value="appStore.fixedHeader" />
</div>
<!-- 紧凑模式 -->
<div class="flex items-center justify-between py-3 border-b" :style="{ borderColor: themeVars.dividerColor }">
<div>
<div class="text-sm" :style="{ color: themeVars.textColor1 }">紧凑模式</div>
<div class="text-xs mt-0.5" :style="{ color: themeVars.textColor3 }">减少内容区内边距</div>
</div>
<n-switch v-model:value="appStore.contentCompact" />
</div>
<!-- 侧边栏 -->
<div class="flex items-center justify-between py-3" :style="{ borderColor: themeVars.dividerColor }">
<div>
<div class="text-sm" :style="{ color: themeVars.textColor1 }">折叠侧边栏</div>
<div class="text-xs mt-0.5" :style="{ color: themeVars.textColor3 }">收起左侧导航菜单</div>
</div>
<n-switch v-model:value="appStore.collapsed" />
</div>
</div>
</n-tab-pane>
<!-- 快捷键 tab -->
<n-tab-pane name="shortcuts" tab="快捷键">
<div class="space-y-3">
<div v-for="s in shortcuts" :key="s.label" class="flex items-center justify-between">
<span class="text-sm" :style="{ color: themeVars.textColor2 }">{{ s.label }}</span>
<div class="flex gap-1">
<span
v-for="k in s.keys"
:key="k"
class="px-2 py-0.5 text-xs rounded border font-mono"
:style="{
borderColor: themeVars.borderColor,
color: themeVars.textColor2,
backgroundColor: themeVars.actionColor
}"
>
{{ k }}
</span>
</div>
</div>
</div>
</n-tab-pane>
</n-tabs>
<template #footer>
<div class="flex items-center justify-between w-full">
<n-space :size="8">
<CommonButton type="primary" size="small" text @click="copyPrefs">
<template #icon>
<n-icon><copy-outline /></n-icon>
</template>
复制
</CommonButton>
<CommonButton type="info" size="small" text @click="importPrefs">
<template #icon>
<n-icon><download-outline /></n-icon>
</template>
导入
</CommonButton>
</n-space>
<CommonButton text type="error" size="small" @click="clearAndLogout">清空并退出</CommonButton>
</div>
</template>
</n-drawer-content>
</n-drawer>
</template>
<script setup lang="ts">
import {
SettingsOutline,
SunnyOutline,
MoonOutline,
PhonePortraitOutline,
ReloadOutline,
PinOutline,
RemoveOutline,
AddOutline,
CopyOutline,
DownloadOutline
} from '@vicons/ionicons5'
import { useAppStore } from '@/store/app/index'
const themeVars = useThemeVars()
const appStore = useAppStore()
const router = useRouter()
const customColor = ref(appStore.primaryColor)
const currentThemeMode = computed(() => {
return appStore.isDark ? 'dark' : 'light'
})
const themeModes = [
{ value: 'light', label: '浅色', icon: SunnyOutline },
{ value: 'dark', label: '深色', icon: MoonOutline },
{ value: 'system', label: '跟随系统', icon: PhonePortraitOutline }
]
const colorPresets = [
{ label: '默认', color: '#165DFF' },
{ label: '紫罗兰', color: '#7C3AED' },
{ label: '樱花粉', color: '#DB2777' },
{ label: '柠檬黄', color: '#D97706' },
{ label: '天蓝色', color: '#0EA5E9' },
{ label: '浅绿色', color: '#10B981' },
{ label: '锌色灰', color: '#71717A' },
{ label: '深绿色', color: '#059669' },
{ label: '深蓝色', color: '#1D4ED8' },
{ label: '橙黄色', color: '#EA580C' },
{ label: '玫瑰红', color: '#E11D48' },
{ label: '中性色', color: '#6B7280' },
{ label: '石板灰', color: '#475569' },
{ label: '中灰色', color: '#9CA3AF' }
]
const borderRadiusOptions = [0, 0.25, 0.5, 0.75, 1]
const transitionOptions = [
{ label: '无动画', value: 'none' },
{ label: '淡入淡出', value: 'fade' },
{ label: '滑动渐变', value: 'fade-slide' },
{ label: '缩放渐变', value: 'zoom' }
]
const shortcuts = [
{ label: '全局搜索', keys: ['Ctrl', 'K'] },
{ label: '切换主题', keys: ['Ctrl', 'Shift', 'D'] },
{ label: '返回首页', keys: ['Alt', 'H'] },
{ label: '关闭当前标签', keys: ['Alt', 'W'] }
]
const setThemeMode = (mode: string) => {
if (mode === 'system') {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
appStore.isDark = prefersDark
} else {
appStore.isDark = mode === 'dark'
}
}
const setColor = (color: string) => {
appStore.primaryColor = color
customColor.value = color
applyAndSave()
}
const setBorderRadius = (r: number) => {
appStore.borderRadius = r
applyAndSave()
}
const applyAndSave = () => {
appStore.applyTheme()
}
const decreaseFontSize = () => {
appStore.fontSize = Math.max(12, appStore.fontSize - 1)
applyAndSave()
}
const increaseFontSize = () => {
appStore.fontSize = Math.min(20, appStore.fontSize + 1)
applyAndSave()
}
const resetPrefs = () => {
appStore.primaryColor = '#165DFF'
appStore.borderRadius = 0
appStore.fontSize = 14
appStore.colorWeak = false
appStore.grayMode = false
appStore.isDark = false
appStore.showBreadcrumb = true
appStore.contentCompact = true
appStore.fixedHeader = false
appStore.collapsed = false
appStore.transitionName = 'none'
customColor.value = '#165DFF'
applyAndSave()
window.$message.success('已重置为默认设置')
}
const copyPrefs = async () => {
const prefs = JSON.stringify(
{
primaryColor: appStore.primaryColor,
borderRadius: appStore.borderRadius,
fontSize: appStore.fontSize,
isDark: appStore.isDark,
colorWeak: appStore.colorWeak,
grayMode: appStore.grayMode,
transitionName: appStore.transitionName
},
null,
2
)
try {
await navigator.clipboard.writeText(prefs)
window.$message.success('配置已复制到剪贴板')
} catch (err) {
window.$message.error('复制失败')
}
}
const importPrefs = async () => {
try {
const text = await navigator.clipboard.readText()
if (!text) {
window.$message.warning('剪贴板中没有内容')
return
}
const conf = JSON.parse(text)
// 验证关键字段
if (conf.primaryColor) appStore.primaryColor = conf.primaryColor
if (typeof conf.borderRadius === 'number') appStore.borderRadius = conf.borderRadius
if (typeof conf.fontSize === 'number') appStore.fontSize = conf.fontSize
if (typeof conf.isDark === 'boolean') appStore.isDark = conf.isDark
if (typeof conf.colorWeak === 'boolean') appStore.colorWeak = conf.colorWeak
if (typeof conf.grayMode === 'boolean') appStore.grayMode = conf.grayMode
if (conf.transitionName) appStore.transitionName = conf.transitionName
applyAndSave()
window.$message.success('偏好设置已成功导入并应用')
} catch (err) {
window.$message.error('导入失败:请确保剪贴板内容是有效的 JSON 格式')
}
}
const clearAndLogout = async () => {
try {
await window.$dialog.warning('确认清空并退出', '确定要清空所有本地缓存并退出登录吗?此操作不可逆。')
localStorage.clear()
router.push('/login')
window.$message.success('已清空并退出')
} catch (error) {
// User cancelled
}
}
onMounted(() => {
applyAndSave()
})
</script>
export default {
common: {
confirm: 'Confirm',
cancel: 'Cancel',
save: 'Save',
delete: 'Delete',
edit: 'Edit',
add: 'Add',
search: 'Search',
reset: 'Reset',
logout: 'Logout'
}
}
import { createI18n } from 'vue-i18n'
import zhCN from './zh-CN/index'
import enUS from './en-US/index'
const i18n = createI18n({
legacy: false, // Use Composition API
locale: localStorage.getItem('lang') || 'zh-CN',
fallbackLocale: 'en-US',
messages: {
'zh-CN': zhCN,
'en-US': enUS
}
})
export default i18n
export default {
common: {
confirm: '确定',
cancel: '取消',
save: '保存',
delete: '删除',
edit: '编辑',
add: '新增',
search: '查询',
reset: '重置',
logout: '退出登录'
}
}
import './style.css'
import App from './App.vue'
import router from './router'
import pinia from './store'
import i18n from './locales'
import 'vfonts/Lato.css'
import 'vfonts/FiraCode.css'
import { setupNaiveDefaults } from '@/plugins/naive-ui-defaults'
// 设置 Naive UI 组件默认属性
setupNaiveDefaults()
import { setupNaiveDiscreteApi } from '@/utils/naive'
const app = createApp(App)
app.use(pinia)
app.use(i18n)
app.use(router)
// 初始化全局 Discrete API (window.$message, window.$dialog 等)
// 必须在 app.use(pinia) 之后,确保 hooks 内部的 store 可用
setupNaiveDiscreteApi()
app.mount('#app')
import { NInput, NDatePicker, NTimePicker, NCascader, NInputNumber, NTreeSelect, NAutoComplete, NMention, NColorPicker, NRate } from 'naive-ui'
/**
* 默认开启所有表单组件的 clearable 属性
*/
const components = [NInput, NDatePicker, NTimePicker, NCascader, NInputNumber, NTreeSelect, NAutoComplete, NMention, NColorPicker, NRate]
export function setupNaiveDefaults() {
components.forEach((component: any) => {
if (component.props && component.props.clearable !== undefined) {
const clearable = component.props.clearable
if (typeof clearable === 'object' && clearable !== null && !Array.isArray(clearable)) {
clearable.default = true
} else {
// 如果是 Boolean 构造函数或数组,则重写为对象形式
component.props.clearable = {
type: clearable,
default: true
}
}
}
})
}
import type { RouteRecordRaw } from 'vue-router'
import MainLayout from '@/layouts/MainLayout.vue'
// 静态路由
const constantRoutes: Array<RouteRecordRaw> = [
{
path: '/404',
name: '404',
component: () => import('@/views/404.vue'),
meta: { title: '404' }
},
{
path: '/',
name: 'layout',
component: MainLayout,
children: [
{
path: '/theme',
name: 'theme',
component: () => import('@/views/theme/index.vue'),
meta: { title: '主题色板' }
},
{
path: '/xml-editor',
name: 'xml-editor',
component: () => import('@/views/xmlEditor.vue'),
meta: { title: '工卡 XML 编辑器' }
}
]
},
{
path: '/views/:pathMatch(.*)*',
component: MainLayout
}
]
const router = createRouter({
history: createWebHashHistory(),
routes: constantRoutes
})
router.beforeEach(() => {
window.$loadingBar?.start()
})
router.afterEach((to) => {
window.$loadingBar?.finish()
const title = (to.meta?.title as string) || (to.name as string)
if (title && title !== 'layout') {
document.title = `${title} | AMRO 系统`
} else {
document.title = 'AMRO 系统'
}
})
router.onError(() => {
window.$loadingBar?.error()
})
export default router
import type { AppState } from './types'
export const useAppStore = defineStore('app', {
state: (): AppState => ({
collapsed: false,
isDark: false,
primaryColor: '#165DFF',
borderRadius: 0,
fontSize: 14,
colorWeak: false,
grayMode: false,
showBreadcrumb: true,
contentCompact: true,
fixedHeader: false,
rememberMe: false,
savedUsername: '',
savedPassword: '',
loading: false,
loadingText: '加载中...',
transitionName: 'none',
settingsPinned: false,
settingsOpen: false
}),
actions: {
toggleSidebar() {
this.collapsed = !this.collapsed
},
applyTheme() {
const root = document.documentElement
root.style.setProperty('--primary-color', this.primaryColor)
root.style.setProperty('--border-radius-base', `${this.borderRadius}rem`)
root.style.setProperty('--font-size-base', `${this.fontSize}px`)
document.body.style.fontSize = `${this.fontSize}px`
document.body.classList.toggle('color-weak', this.colorWeak)
document.body.classList.toggle('gray-mode', this.grayMode)
}
},
persist: true
})
export interface AppState {
collapsed: boolean
isDark: boolean
primaryColor: string
borderRadius: number
fontSize: number
colorWeak: boolean
grayMode: boolean
// 布局
showBreadcrumb: boolean
contentCompact: boolean
fixedHeader: boolean
rememberMe: boolean
savedUsername: string
savedPassword: string
// 全局加载状态
loading: boolean
loadingText?: string
transitionName: string
settingsPinned: boolean
settingsOpen: boolean
}
import { findNodeById, serializeTreeToXml, findParentNode, parseXmlToTree } from '@/utils/xmlParser'
import type { XmlNode } from '@/types/xmlNode'
import type { EditorState } from './types'
export const useEditorStore = defineStore('editor', {
state: (): EditorState => ({
xmlTree: null,
selectedNodeId: null,
undoStack: [],
redoStack: []
}),
getters: {
selectedNode(state): XmlNode | null {
if (!state.xmlTree || !state.selectedNodeId) return null
return findNodeById(state.xmlTree, state.selectedNodeId)
},
selectedNodeParent(state): XmlNode | null {
if (!state.xmlTree || !state.selectedNodeId) return null
return findParentNode(state.xmlTree, state.selectedNodeId)
}
},
actions: {
setXmlTree(tree: XmlNode) {
this.xmlTree = tree
this.selectedNodeId = tree.id
this.undoStack = []
this.redoStack = []
},
setSelectedNodeId(id: string | null) {
this.selectedNodeId = id
},
/**
* 强制触发当前状态同步快照
*/
triggerSync() {
this.saveSnapshot()
},
/**
* 保存当前状态到撤销栈
*/
saveSnapshot() {
if (!this.xmlTree) return
const xmlStr = serializeTreeToXml(this.xmlTree)
// 限制撤销栈大小为 50
if (this.undoStack.length >= 50) {
this.undoStack.shift()
}
this.undoStack.push(xmlStr)
// 每次新操作后,清空重做栈
this.redoStack = []
},
/**
* 撤销
*/
undo() {
if (this.undoStack.length === 0 || !this.xmlTree) return
const currentXml = serializeTreeToXml(this.xmlTree)
this.redoStack.push(currentXml)
const previousXml = this.undoStack.pop()!
// 重新解析上一个 XML 状态并设置
const tree = parseXmlToTree(previousXml)
// 保持选中 ID 存在于新树中,若不存在则默认选中根节点
this.xmlTree = tree
if (this.selectedNodeId && !findNodeById(tree, this.selectedNodeId)) {
this.selectedNodeId = tree.id
}
},
/**
* 重做
*/
redo() {
if (this.redoStack.length === 0 || !this.xmlTree) return
const currentXml = serializeTreeToXml(this.xmlTree)
this.undoStack.push(currentXml)
const nextXml = this.redoStack.pop()!
const tree = parseXmlToTree(nextXml)
this.xmlTree = tree
if (this.selectedNodeId && !findNodeById(tree, this.selectedNodeId)) {
this.selectedNodeId = tree.id
}
},
/**
* 更新当前选中节点的属性
*/
updateSelectedNodeAttributes(attributes: Record<string, string>) {
const node = this.selectedNode
if (!node) return
this.saveSnapshot()
node.attributes = { ...attributes }
},
/**
* 更新当前选中节点的文本内容
*/
updateSelectedNodeText(text: string) {
const node = this.selectedNode
if (!node) return
this.saveSnapshot()
node.textContent = text
node.mixedContent = [] // 若纯文本修改,清空混合内容
},
/**
* 更新当前选中节点的混合内容
*/
updateSelectedNodeMixedContent(mixedContent: any[], children: XmlNode[]) {
const node = this.selectedNode
if (!node) return
this.saveSnapshot()
node.mixedContent = mixedContent
node.children = children
node.textContent = '' // 清空纯文本内容
},
/**
* 向当前选中节点添加子节点
*/
addChildNode(newNode: XmlNode, index?: number) {
const node = this.selectedNode
if (!node) return
this.saveSnapshot()
newNode.parentId = node.id
if (index !== undefined) {
node.children.splice(index, 0, newNode)
} else {
node.children.push(newNode)
}
},
/**
* 删除当前选中的节点
*/
deleteSelectedNode() {
if (!this.xmlTree || !this.selectedNodeId || this.selectedNodeId === this.xmlTree.id) {
return // 不能删除根节点
}
const parent = this.selectedNodeParent
if (!parent) return
this.saveSnapshot()
const index = parent.children.findIndex((c: XmlNode) => c.id === this.selectedNodeId)
if (index !== -1) {
parent.children.splice(index, 1)
// 处理混合内容 (mixedContent) 中的对应项
parent.mixedContent = parent.mixedContent.filter((item: any) => item.nodeId !== this.selectedNodeId)
// 选中父节点
this.selectedNodeId = parent.id
}
},
/**
* 移动子节点位置
*/
moveChildNode(nodeId: string, direction: 'up' | 'down') {
const parent = findParentNode(this.xmlTree!, nodeId)
if (!parent) return
const index = parent.children.findIndex((c) => c.id === nodeId)
if (index === -1) return
const targetIndex = direction === 'up' ? index - 1 : index + 1
if (targetIndex < 0 || targetIndex >= parent.children.length) return
this.saveSnapshot()
// 交换 children 中的元素
const temp = parent.children[index]
parent.children[index] = parent.children[targetIndex]
parent.children[targetIndex] = temp
// 同步调整 mixedContent 里的顺序
const mixedIndex1 = parent.mixedContent.findIndex((item) => item.nodeId === nodeId)
const otherNodeId = parent.children[index].id
const mixedIndex2 = parent.mixedContent.findIndex((item) => item.nodeId === otherNodeId)
if (mixedIndex1 !== -1 && mixedIndex2 !== -1) {
const tempMixed = parent.mixedContent[mixedIndex1]
parent.mixedContent[mixedIndex1] = parent.mixedContent[mixedIndex2]
parent.mixedContent[mixedIndex2] = tempMixed
}
}
}
})
import type { XmlNode } from '@/types/xmlNode'
export interface EditorState {
xmlTree: XmlNode | null
selectedNodeId: string | null
undoStack: string[] // 保存 XML 字符串的历史快照
redoStack: string[] // 保存 XML 字符串的重做快照
}
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
export default pinia
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Custom Scrollbar Styling - Theme Aware */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(128, 128, 128, 0.2);
border-radius: 10px;
transition: background 0.3s;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(128, 128, 128, 0.4);
}
/* For Firefox */
* {
scrollbar-width: thin;
scrollbar-color: rgba(128, 128, 128, 0.3) transparent;
}
/* Naive UI internal scrollbar overrides for better theme integration */
.n-scrollbar-rail,
.n-data-table .n-scrollbar-rail {
background-color: transparent !important;
opacity: 1 !important;
}
.n-scrollbar-rail--vertical {
width: 6px !important;
}
.n-scrollbar-rail--horizontal {
height: 6px !important;
}
.n-scrollbar-rail > .n-scrollbar-rail__scrollbar {
background-color: rgba(128, 128, 128, 0.2) !important;
}
.n-scrollbar-rail:hover > .n-scrollbar-rail__scrollbar {
background-color: rgba(128, 128, 128, 0.4) !important;
}
.n-button:not(:last-child) {
margin-right: 10px;
}
.n-data-table .n-data-table__pagination{
margin: 0;
}
/* 全局弹框组件样式 */
.app-modal {
border-radius: 12px;
}
\ No newline at end of file
/**
* 全局通用类型定义 (Global Types)
* 此文件定义的类型可以在全项目直接使用,无需 import
*/
import type { DialogOptions, MessageOptions, NotificationOptions } from 'naive-ui'
declare global {
/**
* 基础响应结构
*/
interface ResponseData<T = any> {
code: number | string
msg: string
data: T
total?: number
[key: string]: any
}
/**
* 业务封装后的 Dialog API (Promise 风格)
*/
interface CustomDialogApi {
info: (titleOrOptions: string | DialogOptions, content?: string, config?: DialogOptions) => Promise<boolean>
error: (titleOrOptions: string | DialogOptions, content?: string, config?: DialogOptions) => Promise<boolean>
success: (titleOrOptions: string | DialogOptions, content?: string, config?: DialogOptions) => Promise<boolean>
warning: (titleOrOptions: string | DialogOptions, content?: string, config?: DialogOptions) => Promise<boolean>
}
/**
* 业务封装后的 Message API
*/
interface CustomMessageApi {
info: (content: string, config?: MessageOptions) => void
error: (content: string, config?: MessageOptions) => void
success: (content: string, config?: MessageOptions) => void
warning: (content: string, config?: MessageOptions) => void
}
/**
* 业务封装后的 Notification API
*/
interface CustomNotificationApi {
info: (content: string, config?: NotificationOptions) => void
error: (content: string, config?: NotificationOptions) => void
success: (content: string, config?: NotificationOptions) => void
warning: (content: string, config?: NotificationOptions) => void
}
/**
* 增加 window 全局变量定义 (用于 Discrete API)
*/
interface Window {
$message: CustomMessageApi
$dialog: CustomDialogApi
$notification: CustomNotificationApi
$loadingBar: import('naive-ui').LoadingBarApi
$loading: {
start: (text?: string) => void
finish: () => void
}
$importModal: any
$exportModal: any
$attachmentModal: any
$uploadModal: any
$previewModal: any
$downloadModal: any
$modal: {
open: (options: any) => import('naive-ui').ModalReactive
}
}
}
export {}
/**
* 扩展 Naive UI 主题类型,使 useThemeVars() 能够识别项目自定义的色阶 Token
* 扩展目标:CustomThemeCommonVars(naive-ui 专为用户扩展预留的空接口)
* useThemeVars() 返回类型为 ThemeCommonVars & CustomThemeCommonVars,因此扩展后可直接访问
* 对应 src/configs/tailwind.ui.config.ts 中定义的扩展变量
*/
declare module 'naive-ui' {
interface CustomThemeCommonVars {
// 主色阶
primary1?: string
primary2?: string
primary3?: string
primary4?: string
primary5?: string
primary6?: string
primary7?: string
// 成功色阶
success1?: string
success2?: string
success3?: string
success4?: string
success5?: string
success6?: string
success7?: string
// 警示色阶
warning1?: string
warning2?: string
warning3?: string
warning4?: string
warning5?: string
warning6?: string
warning7?: string
// 错误/危险色阶
danger1?: string
danger2?: string
danger3?: string
danger4?: string
danger5?: string
danger6?: string
danger7?: string
// 链接色阶
link1?: string
link2?: string
link3?: string
link4?: string
link5?: string
link6?: string
link7?: string
// 边框颜色
colorBorder1?: string
colorBorder2?: string
colorBorder3?: string
colorBorder4?: string
// 填充颜色
colorFill1?: string
colorFill2?: string
colorFill3?: string
colorFill4?: string
// 文字颜色
colorText1?: string
colorText2?: string
colorText3?: string
colorText4?: string
// 背景颜色
colorBg1?: string
colorBg2?: string
colorBg3?: string
colorBg4?: string
colorBg5?: string
}
}
/**
* XML 节点统一数据结构
* 用于在编辑器中表示 XML 文档树的每一个元素节点
*/
export interface XmlNode {
/** 唯一标识 */
id: string
/** 元素标签名,如 "CEP", "PARA" */
tagName: string
/** 元素属性键值对 */
attributes: Record<string, string>
/** 子节点列表 */
children: XmlNode[]
/** 文本内容(#PCDATA),混合内容节点中文本片段 */
textContent: string
/** 混合内容片段列表(文本和子元素交替出现时使用) */
mixedContent: MixedContentItem[]
/** 父节点 ID(不参与序列化) */
parentId: string | null
}
/**
* 混合内容项
* 用于表示 PARA 等混合内容节点中文本和行内元素交替出现的情况
*/
export interface MixedContentItem {
/** 内容类型 */
type: 'text' | 'element'
/** 文本内容(type=text 时) */
text?: string
/** 子元素节点 ID(type=element 时) */
nodeId?: string
}
/**
* DTD 中元素的内容模型(解析后的结构)
*/
export interface DtdContentModel {
type: 'sequence' | 'choice' | 'elementRef' | 'pcdata' | 'mixed' | 'empty'
occurrence: 'once' | 'optional' | 'zeroOrMore' | 'oneOrMore'
name?: string
children?: DtdContentModel[]
}
/**
* DTD 中元素的属性定义
*/
export interface DtdAttribute {
typeDefinition: string
parsedType: string
requirement: string
enumValues: string[] | null
defaultValue: string | null
}
/**
* DTD 中元素的完整定义
*/
export interface DtdElement {
description: string
contentModel: {
raw: string
parsed: DtdContentModel
humanReadable: string
}
attributes: Record<string, DtdAttribute>
allowedChildren: string[]
}
/**
* DTD JSON 的根结构
*/
export interface DtdSchema {
elements: Record<string, DtdElement>
}
/**
* 编辑器操作历史记录
*/
export interface EditorAction {
type: 'update_attribute' | 'update_text' | 'add_node' | 'delete_node' | 'move_node'
timestamp: number
/** 操作前的快照数据 */
before: any
/** 操作后的快照数据 */
after: any
/** 受影响的节点 ID */
nodeId: string
}
/**
* 节点树显示配置
*/
export interface TreeNodeOption {
key: string
label: string
tagName: string
isLeaf: boolean
children?: TreeNodeOption[]
prefix?: string
}
import { service } from '@/api/index'
/**
* 通用列表数据请求工具
* 逻辑来源:抽离自 CommonTable 和 CommonSelect 的数据获取逻辑
* @param url 接口地址
* @param params 请求参数
* @returns 统一格式的列表数据对象
*/
export async function requestListData(url: string, params: any = {}, method: 'POST' | 'GET' = 'POST') {
try {
let res: any
if (method === 'GET') {
const queryParams = new URLSearchParams()
Object.keys(params).forEach((key) => {
const val = params[key]
if (val !== null && val !== undefined) {
queryParams.append(key, val)
}
})
res = await service.get(`${url}?${queryParams.toString()}`)
} else {
res = await service.post(url, params)
}
if (res.code === 200) {
let data = res.data
let rows: any[] = []
// 0. 支持 EasyUI 根路径下直接返回 rows 数组的结构
if (Array.isArray(res.rows)) {
rows = res.rows
}
// 1. 基础数组直接返回
else if (Array.isArray(data)) {
rows = data
}
// 2. 对象结构处理
else if (data && typeof data === 'object') {
// 优先匹配 rows (标准表格分页)
if (Array.isArray(data.rows)) {
rows = data.rows
}
// 匹配和 domainCode 相同的 key (字典/域查询常见)
else if (params.domainCode && Array.isArray(data[params.domainCode])) {
rows = data[params.domainCode]
}
// 查找首个数组字段或特定的 "0" 字段
else {
const arrayKey = Object.keys(data).find((k) => k !== 'code' && Array.isArray(data[k]))
if (arrayKey) {
rows = data[arrayKey]
} else if (Array.isArray(data['0'])) {
rows = data['0']
}
}
}
// 计算总数:优先取外层 total,次之取 data.total,最后取数组长度
const total = res.total !== undefined ? res.total : data?.total !== undefined ? data.total : rows.length
return {
success: true,
data: rows,
total,
raw: res // 保留原始响应以备不时之需
}
}
return { success: false, data: [], total: 0, raw: res }
} catch (e) {
console.error(`Request list data failed [${url}]:`, e)
return { success: false, data: [], total: 0, error: e }
}
}
import dayjs from 'dayjs'
/**
* 格式化日期时间
* @param date 日期
* @param format 格式,默认 YYYY-MM-DD HH:mm:ss
*/
export function formatDateTime(date: string | number | Date | any, format = 'YYYY-MM-DD HH:mm:ss') {
if (!date || date === '-' || date === 'null' || date === 'undefined') return '-'
// 处理后端返回的奇怪对象格式 (有些后端返回的是带 year, monthValue 等属性的对象)
if (typeof date === 'object' && date.year && date.monthValue) {
return dayjs()
.set('year', date.year)
.set('month', date.monthValue - 1)
.set('date', date.dayOfMonth)
.set('hour', date.hour || 0)
.set('minute', date.minute || 0)
.set('second', date.second || 0)
.format(format)
}
const d = dayjs(date)
return d.isValid() ? d.format(format) : '-'
}
/**
* 格式化日期
* @param date 日期
* @param format 格式,默认 YYYY-MM-DD
*/
export function formatDate(date: string | number | Date | any, format = 'YYYY-MM-DD') {
if (!date || date === '-' || date === 'null' || date === 'undefined') return null
const d = dayjs(date)
return d.isValid() ? d.format(format) : null
}
/**
* 获取当前日期时间
*/
export function now(format = 'YYYY-MM-DD HH:mm:ss') {
return dayjs().format(format)
}
/**
* 计算相对时间 (如:几分钟前)
* @param date 日期
*/
export function fromNow(date: string | number | Date | any) {
if (!date) return '-'
// 如果需要更复杂的相对时间,通常需要引入 dayjs/plugin/relativeTime
return dayjs(date).toString() // 占位,之后可按需增强
}
/**
* 毫秒转可读时间字符串
* @param ms 毫秒数
* @returns 格式化的时间字符串(如:1天2小时3分4秒 或 26毫秒)
*/
export function toMsTimeString(ms: number | string | null | undefined) {
if (ms === null || ms === undefined || ms === '') return ''
const numMs = Number(ms)
// 如果小于1s,显示毫秒 (满足高精度需求)
if (numMs < 1000 && numMs > 0) return `${numMs}毫秒`
if (numMs <= 0) return '0秒'
let totalSeconds = Math.floor(numMs / 1000)
const d = Math.floor(totalSeconds / (24 * 3600))
totalSeconds %= 24 * 3600
const h = Math.floor(totalSeconds / 3600)
totalSeconds %= 3600
const m = Math.floor(totalSeconds / 60)
const s = totalSeconds % 60
let res = ''
if (d > 0) res += `${d}天`
if (h > 0) res += `${h}小时`
if (m > 0) res += `${m}分`
if (s > 0 || res === '') res += `${s}秒`
return res
}
/**
* 增加天数并格式化
* @param dateStr 日期字符串
* @param days 增加的天数
* @param format 格式,默认 YYYY-MM-DD
*/
export function addDate(dateStr: string | number | Date | any, days: number, format = 'YYYY-MM-DD') {
if (!dateStr || dateStr === '-' || dateStr === 'null' || dateStr === 'undefined') return ''
const d = dayjs(dateStr)
return d.isValid() ? d.add(days, 'day').format(format) : ''
}
import type { DtdSchema, DtdElement, DtdAttribute, DtdContentModel } from '@/types/xmlNode'
/**
* DTD 规则管理器
* 提供基于 dtd.json 的元素规则查询能力
*/
let _schema: DtdSchema | null = null
/**
* 加载 DTD Schema
*/
export function loadDtdSchema(json: DtdSchema): void {
_schema = json
}
/**
* 获取已加载的 DTD Schema
*/
export function getDtdSchema(): DtdSchema | null {
return _schema
}
/**
* 获取指定元素的 DTD 定义
*/
export function getElementRule(tagName: string): DtdElement | null {
if (!_schema) return null
return _schema.elements[tagName] || null
}
/**
* 获取元素允许的子元素列表
*/
export function getAllowedChildren(tagName: string): string[] {
const rule = getElementRule(tagName)
if (!rule) return []
return rule.allowedChildren || []
}
/**
* 获取元素的属性定义
*/
export function getElementAttributes(tagName: string): Record<string, DtdAttribute> {
const rule = getElementRule(tagName)
if (!rule) return {}
return rule.attributes || {}
}
/**
* 获取元素的内容模型
*/
export function getContentModel(tagName: string): DtdContentModel | null {
const rule = getElementRule(tagName)
if (!rule) return null
return rule.contentModel.parsed
}
/**
* 判断元素是否为空元素
*/
export function isEmptyElement(tagName: string): boolean {
const model = getContentModel(tagName)
if (!model) return false
return model.type === 'empty'
}
/**
* 判断元素是否只包含文本
*/
export function isTextOnlyElement(tagName: string): boolean {
const model = getContentModel(tagName)
if (!model) return false
return model.type === 'pcdata'
}
/**
* 判断元素是否为混合内容(文本+子元素)
*/
export function isMixedContentElement(tagName: string): boolean {
const model = getContentModel(tagName)
if (!model) return false
return model.type === 'mixed'
}
/**
* 根据父元素的内容模型,判断某个子元素是否可以添加
* 考虑 occurrence 约束
*/
export function canAddChild(parentTagName: string, childTagName: string, currentChildCount: number): boolean {
const allowed = getAllowedChildren(parentTagName)
if (!allowed.includes(childTagName)) return false
// 进一步检查 occurrence 约束
const rule = getElementRule(parentTagName)
if (!rule) return true
const model = rule.contentModel.parsed
const childOccurrence = findChildOccurrence(model, childTagName)
if (childOccurrence === 'once' && currentChildCount >= 1) return false
if (childOccurrence === 'optional' && currentChildCount >= 1) return false
return true
}
/**
* 判断某个子节点是否可以被删除(考虑 occurrence: once 约束)
*/
export function canDeleteChild(parentTagName: string, childTagName: string, currentChildCount: number): boolean {
const rule = getElementRule(parentTagName)
if (!rule) return true
const model = rule.contentModel.parsed
const childOccurrence = findChildOccurrence(model, childTagName)
// 'once' 或 'oneOrMore' 的子元素至少需要一个
if ((childOccurrence === 'once' || childOccurrence === 'oneOrMore') && currentChildCount <= 1) {
return false
}
return true
}
/**
* 在内容模型树中查找子元素的出现约束
*/
function findChildOccurrence(model: DtdContentModel, childTagName: string): string {
if (model.type === 'elementRef' && model.name === childTagName) {
return model.occurrence
}
if (model.children) {
for (const child of model.children) {
const result = findChildOccurrence(child, childTagName)
if (result !== 'unknown') return result
}
}
return 'unknown'
}
/**
* 获取可插入到指定位置的元素列表
* 基于父节点的 allowedChildren 过滤已满的元素
*/
export function getInsertableChildren(parentTagName: string, existingChildren: string[]): string[] {
const allowed = getAllowedChildren(parentTagName)
const rule = getElementRule(parentTagName)
if (!rule) return allowed
const model = rule.contentModel.parsed
return allowed.filter(childTag => {
const count = existingChildren.filter(t => t === childTag).length
const occurrence = findChildOccurrence(model, childTag)
if (occurrence === 'once' && count >= 1) return false
if (occurrence === 'optional' && count >= 1) return false
return true
})
}
/**
* 创建一个新节点的默认结构
* 根据 DTD 规则,自动填充必要的属性默认值
*/
export function createDefaultAttributes(tagName: string): Record<string, string> {
const attrs = getElementAttributes(tagName)
const result: Record<string, string> = {}
for (const [name, def] of Object.entries(attrs)) {
if (def.defaultValue) {
result[name] = def.defaultValue
}
}
return result
}
/**
* 获取所有已注册的元素名称列表
*/
export function getAllElementNames(): string[] {
if (!_schema) return []
return Object.keys(_schema.elements)
}
import { createDiscreteApi, darkTheme, lightTheme } from 'naive-ui'
import { useAppStore } from '@/store/app'
/**
* 这个文件负责提供在非组件环境(如 .ts 文件)中使用的 Naive UI 实例
* 虽然 hooks 目录下的文件也提供了类似逻辑,但这里通过 window 将其实例化
* 以确保在全项目(甚至非 Vue 环境)都能通过 window.$message 等访问到业务封装后的逻辑
*/
// 注:所有的初始化逻辑放在 setupNaiveDiscreteApi 中,由 main.ts 在 app.use(pinia) 之后调用
export function setupNaiveDiscreteApi() {
// 1. 对于 LoadingBar,我们仍使用原生的 discreteApi(因为没有对应的业务 hook)
const { loadingBar } = createDiscreteApi(['loadingBar'], {
configProviderProps: computed(() => {
const appStore = useAppStore()
return {
theme: appStore.isDark ? darkTheme : lightTheme,
themeOverrides: {
common: {
primaryColor: appStore.primaryColor,
primaryColorHover: appStore.primaryColor + 'cc',
primaryColorPressed: appStore.primaryColor + '99',
primaryColorSuppl: appStore.primaryColor
}
}
}
})
})
// 2. 对于 Message / Dialog / Notification,使用 hooks 目录下的业务封装版本
// 这样可以确保 window.$message.success() 等调用能享受到 hook 中定义的 Promise 封装或主题自动切换能力
window.$message = useMessage()
window.$dialog = useDialog()
window.$notification = useNotification()
window.$loadingBar = loadingBar
// 3. 挂载全局 Modal 离散 API
const { modal } = createDiscreteApi(['modal'], {
configProviderProps: computed(() => {
const appStore = useAppStore()
return {
theme: (appStore as any).isDark ? darkTheme : lightTheme
}
})
})
window.$modal = {
open: (options: any) => {
const { width, ...rest } = options
const modalOptions = {
preset: 'card',
style: width ? { width: typeof width === 'number' ? `${width}px` : width } : {},
...rest
}
return modal.create(modalOptions)
}
}
// 4. 挂载全局阻塞加载控制
window.$loading = {
start: (text?: string) => {
const appStore = useAppStore()
appStore.loading = true
appStore.loadingText = text || '加载中...'
},
finish: () => {
const appStore = useAppStore()
appStore.loading = false
}
}
}
// 注意:在 .ts 文件顶层解构时,可能 window.$xxx 还没被初始化
// 导出 getter 函数或直接使用 window.$xxx 更安全
export const message = () => window.$message
export const dialog = () => window.$dialog
export const notification = () => window.$notification
import { h } from 'vue'
import { NText } from 'naive-ui'
import CommonButton from '@/components/CommonButton.vue'
export interface AttachmentRenderOptions {
/** 是否有附件的数据标识 (如 row.UPLOAD) */
upload: string | number | null | undefined
/** 附件所属分类 (如 'MANUAL') */
category: string
/** 关联的业务 ID */
sourceId: string | number
/** 弹窗标题 */
title?: string
/** 是否开启编辑(删除)功能 */
editable?: boolean
/** 上传接口地址 */
uploadApi?: string
/** 关闭弹窗后的回调 */
onClose?: () => void
}
/**
* 通用附件查看列渲染方法
* @description 根据是否有附件渲染“查看”按钮或“暂无”文本,并处理弹窗打开逻辑
*/
export const renderAttachmentAction = (options: AttachmentRenderOptions) => {
const { upload, category, sourceId, title, uploadApi, onClose } = options
if (!upload) return h(NText, { depth: 3 }, { default: () => '暂无' })
return h(
CommonButton,
{
type: 'primary',
text: true,
onClick: (e: MouseEvent) => {
e.stopPropagation()
window.$attachmentModal?.open({
category,
sourceId: String(sourceId),
title: title || '附件列表',
editable: options.editable,
uploadApi,
onClose
})
}
},
{ default: () => '查看' }
)
}
export interface UploadActionOptions {
/** 上传接口名 (FunctionCode), 不传则默认为底座定义的兜底接口 */
api?: string
/** 业务分类值 */
category?: string
/** 业务 ID */
sourceId?: string | number
/** 弹窗标题 */
title?: string
/** 允许选择的文件类型,例如 'image/*' */
accept?: string
/** 成功后的回调 */
onSuccess?: (res?: any) => void
/** 分类字段名,默认 'fileCategory' */
categoryKey?: string
/** 业务 ID 字段名,默认 'sourceId' */
sourceIdKey?: string
/** 其它额外参数 */
extraData?: Record<string, any>
}
/**
* 通用附件上传调用方法
* @description 弹出统一的附件上传模块
*/
export const openUploadModal = (options: UploadActionOptions) => {
const { api, category, sourceId, title, accept, onSuccess, categoryKey = 'fileCategory', sourceIdKey = 'sourceId', extraData = {} } = options
const data: Record<string, any> = { ...extraData }
if (category) data[categoryKey] = category
if (sourceId) data[sourceIdKey] = String(sourceId)
window.$uploadModal?.open({
api: api || '/v1/plugins/ATTACHMENT_UPLOAD',
title: title || '上传附件',
accept,
data,
onSuccess
})
}
export interface DownloadActionOptions {
/** 下载接口地址,默认为 '/v1/plugins/ATTACHMENT_DOWN' */
api?: string
/** 请求参数 */
params?: any
/** 保存的文件名 */
fileName?: string
/** 弹窗标题 */
title?: string
/** 回调方法 */
callback?: (success: boolean) => void
}
/**
* 通用模板下载调用方法
* @description 弹出带有进度条的统一下载模块
*/
export const openDownloadModal = (options: DownloadActionOptions) => {
window.$downloadModal?.open(options)
}
/**
* 获取基础 Origin,用于附件和预览文件的请求基准
*/
export const getBaseOrigin = () => {
if (import.meta.env.DEV) {
return window.location.origin
}
const proxyUrl = import.meta.env.VITE_APP_PROXY_URL
if (proxyUrl) {
return proxyUrl.trim().replace(/\/$/, '')
}
return window.location.origin
}
export interface OpenPDFModalOptions {
/** PDF 路径,支持:纯数字 ID、相对路径、绝对路径、或 Base64 数据 */
path: string
/** 附件拉取接口,默认使用 ATTACHMENT_GET */
api?: string
/** 是否在打开预览的同时自动拉起打印 */
print?: boolean
/** 是否为 Base64 编码的 PDF 数据 */
isBase64?: boolean
}
/**
* 打开统一的PDF预览弹窗
* @param options.path - PDF 路径(数字 ID / 相对路径 / 绝对 URL / Base64)
* @param options.api - 拉取接口,默认 '/api/v1/plugins/ATTACHMENT_GET?down=Y&pkid='
* @param options.print - 是否拉起打印,默认 false
* @param options.isBase64 - 是否是 Base64 数据,默认 false
*/
export const openPDFModal = (options: OpenPDFModalOptions) => {
const { path, api = '/api/v1/plugins/ATTACHMENT_GET?down=Y&pkid=', print = false, isBase64 = false } = options
if (!path) {
window.$message?.error('未提供文件路径')
return
}
// 统一打印与新窗口预览的处理函数
const handleAction = (fileUrl: string) => {
if (print) {
const newWindow = window.open('', '_blank')
if (!newWindow) {
window.$message?.error('新窗口打开失败,请检查浏览器是否拦截了弹窗')
return
}
newWindow.document.write(`
<html>
<head>
<title>打印预览</title>
<style>
body, html { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; }
iframe { width: 100%; height: 100%; border: none; }
</style>
</head>
<body>
<iframe id="pdfFrame" src="${fileUrl}"></iframe>
<script>
const iframe = document.getElementById('pdfFrame');
iframe.onload = () => {
try {
iframe.contentWindow.focus();
iframe.contentWindow.print();
} catch (e) {
console.error('Print error in new window:', e);
window.print();
}
};
</script>
</body>
</html>
`)
newWindow.document.close()
} else {
window.open(fileUrl, '_blank')
}
}
let trimmedPath = path.trim()
if (isBase64) {
try {
// 兼容带有 data:application/pdf;base64, 前缀的情况,并去除多余空白符与换行
let cleanBase64 = trimmedPath
if (cleanBase64.startsWith('data:')) {
const commaIndex = cleanBase64.indexOf(',')
if (commaIndex !== -1) {
cleanBase64 = cleanBase64.substring(commaIndex + 1)
}
}
cleanBase64 = cleanBase64.replace(/\s/g, '')
const binStr = atob(cleanBase64)
const len = binStr.length
const arr = new Uint8Array(len)
for (let i = 0; i < len; i++) {
arr[i] = binStr.charCodeAt(i)
}
const blob = new Blob([arr], { type: 'application/pdf' })
trimmedPath = URL.createObjectURL(blob)
} catch (e) {
console.error('Failed to convert base64 to blob URL:', e)
window.$message?.error('解析预览数据失败')
return
}
}
if (trimmedPath.endsWith('/')) {
trimmedPath = trimmedPath.slice(0, -1)
}
const isAbsolute = trimmedPath.startsWith('http://') || trimmedPath.startsWith('https://') || trimmedPath.startsWith('blob:')
if (isAbsolute) {
handleAction(trimmedPath)
return
}
// 确保以 / 开头(如果是相对路径且不以 / 开头)
const normalizedPath = trimmedPath.startsWith('/') ? trimmedPath : '/' + trimmedPath
const isPdfFormat = (str: string) => /^\/\d+\.pdf$/.test(str)
let url = normalizedPath
let extension = ''
let fileName = ''
let fullUrl = ''
const origin = getBaseOrigin()
if (isNaN(parseFloat(url))) {
extension = url.split('.').pop()?.toLowerCase() || ''
fileName = url.split('/').pop() || ''
fullUrl = `${origin}${url}`
if (isPdfFormat(url)) {
const match = url.match(/\d+/)
if (match) {
url = match[0]
}
}
}
// 再次判定是否是数字 ID (如纯数字或者是 /12345.pdf 格式转换后的数字)
// 或者如果显式传入了非默认的 api,说明用户指定了拉取接口,一律走指定的 api 拼接
const isCustomApi = api !== '/api/v1/plugins/ATTACHMENT_GET?down=Y&pkid='
if (!isNaN(parseFloat(url)) || isCustomApi) {
const base = api.startsWith('http://') || api.startsWith('https://') ? '' : origin
const paramValue = api.toLowerCase().includes('pkid') ? url : trimmedPath
const fileUrl = `${base}${api}${paramValue}`
handleAction(fileUrl)
} else {
if (extension === 'pdf') {
handleAction(fullUrl)
} else {
// 下载逻辑
openDownloadModal({
api: fullUrl,
fileName,
title: '文件下载'
})
}
}
}
/**
* 通用超链接列渲染方法
* @description 检测传入的文本是否是网址链接,或是否传入了自定义 onClick 回调。若是网址则渲染为可在新窗口打开的链接,若有 onClick 则渲染为可点击超链接,否则渲染为普通文本
*/
export const renderLink = (value: string | null, onClick?: (e: MouseEvent) => void) => {
if (!value) return null
const isUrl = /^(https?:\/\/|www\.)/i.test(value)
if (isUrl || onClick) {
const href = isUrl ? (value.toLowerCase().startsWith('http') ? value : `http://${value}`) : undefined
return h(
NText,
{
tag: href ? 'a' : 'span',
href,
target: href ? '_blank' : undefined,
type: 'primary',
class: 'hover:underline cursor-pointer',
onClick
},
{ default: () => value }
)
}
return h(NText, null, { default: () => value })
}
import type { XmlNode, MixedContentItem } from '@/types/xmlNode'
/**
* 生成唯一 ID
*/
function generateId(): string {
return crypto.randomUUID ? crypto.randomUUID() : `node_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`
}
/**
* 判断节点是否为行内元素(嵌在文本中的元素)
*/
const INLINE_ELEMENTS = new Set([
'REFBLOCK', 'REFINT', 'REFEXT', 'EIN', 'PAN', 'STDNAME', 'TED',
'TOOLNBR', 'TOOLNAME', 'ZONE', 'EFFECT', 'CONEFFECT',
'CB', 'CBNAME', 'CBLOC', 'GRPHCREF'
])
export function isInlineElement(tagName: string): boolean {
return INLINE_ELEMENTS.has(tagName)
}
/**
* 将 XML 字符串解析为 XmlNode 树
*/
export function parseXmlToTree(xmlString: string): XmlNode {
const parser = new DOMParser()
const doc = parser.parseFromString(xmlString, 'application/xml')
// 检查解析错误
const parseError = doc.querySelector('parsererror')
if (parseError) {
throw new Error(`XML 解析错误: ${parseError.textContent}`)
}
const root = doc.documentElement
return domElementToXmlNode(root, null)
}
/**
* 递归将 DOM Element 转换为 XmlNode
*/
function domElementToXmlNode(element: Element, parentId: string | null): XmlNode {
const id = generateId()
// 提取属性
const attributes: Record<string, string> = {}
for (const attr of Array.from(element.attributes)) {
attributes[attr.name] = attr.value
}
// 处理子内容
const children: XmlNode[] = []
const mixedContent: MixedContentItem[] = []
let textContent = ''
const childNodes = Array.from(element.childNodes)
const hasElementChildren = childNodes.some(n => n.nodeType === Node.ELEMENT_NODE)
const hasTextChildren = childNodes.some(n => n.nodeType === Node.TEXT_NODE && n.textContent?.trim())
if (hasElementChildren && hasTextChildren) {
// 混合内容节点(如 PARA, PARAC 中嵌有 REFBLOCK 等行内元素)
for (const child of childNodes) {
if (child.nodeType === Node.TEXT_NODE) {
const text = child.textContent || ''
if (text) {
mixedContent.push({ type: 'text', text })
}
} else if (child.nodeType === Node.ELEMENT_NODE) {
const childNode = domElementToXmlNode(child as Element, id)
children.push(childNode)
mixedContent.push({ type: 'element', nodeId: childNode.id })
}
}
} else if (hasElementChildren) {
// 纯元素子节点
for (const child of childNodes) {
if (child.nodeType === Node.ELEMENT_NODE) {
children.push(domElementToXmlNode(child as Element, id))
}
}
} else {
// 纯文本内容
textContent = element.textContent || ''
}
return {
id,
tagName: element.tagName,
attributes,
children,
textContent,
mixedContent,
parentId
}
}
/**
* 将 XmlNode 树序列化回 XML 字符串
*/
export function serializeTreeToXml(node: XmlNode, indent: number = 0): string {
const pad = ' '.repeat(indent)
const attrs = Object.entries(node.attributes)
.map(([k, v]) => `${k}="${escapeXmlAttr(v)}"`)
.join(' ')
const openTag = attrs ? `${node.tagName} ${attrs}` : node.tagName
// 空元素(无子节点、无文本、无混合内容)
if (node.children.length === 0 && !node.textContent && node.mixedContent.length === 0) {
return `${pad}<${openTag}/>`
}
// 混合内容节点
if (node.mixedContent.length > 0) {
let content = ''
for (const item of node.mixedContent) {
if (item.type === 'text') {
content += escapeXmlText(item.text || '')
} else if (item.type === 'element' && item.nodeId) {
const child = node.children.find(c => c.id === item.nodeId)
if (child) {
content += serializeTreeToXml(child, 0)
}
}
}
return `${pad}<${openTag}>${content}</${node.tagName}>`
}
// 纯文本节点
if (node.children.length === 0 && node.textContent) {
return `${pad}<${openTag}>${escapeXmlText(node.textContent)}</${node.tagName}>`
}
// 纯元素子节点
const childrenXml = node.children.map(c => serializeTreeToXml(c, indent + 1)).join('\n')
return `${pad}<${openTag}>\n${childrenXml}\n${pad}</${node.tagName}>`
}
/**
* 转义 XML 属性值
*/
function escapeXmlAttr(str: string): string {
return str.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
}
/**
* 转义 XML 文本内容
*/
function escapeXmlText(str: string): string {
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
}
/**
* 在节点树中根据 ID 查找节点
*/
export function findNodeById(root: XmlNode, id: string): XmlNode | null {
if (root.id === id) return root
for (const child of root.children) {
const found = findNodeById(child, id)
if (found) return found
}
return null
}
/**
* 查找节点的父节点
*/
export function findParentNode(root: XmlNode, nodeId: string): XmlNode | null {
for (const child of root.children) {
if (child.id === nodeId) return root
const found = findParentNode(child, nodeId)
if (found) return found
}
return null
}
/**
* 获取从根节点到目标节点的路径
*/
export function getNodePath(root: XmlNode, nodeId: string): XmlNode[] {
const path: XmlNode[] = []
function walk(node: XmlNode): boolean {
path.push(node)
if (node.id === nodeId) return true
for (const child of node.children) {
if (walk(child)) return true
}
path.pop()
return false
}
walk(root)
return path
}
/**
* 深拷贝节点(生成新 ID)
*/
export function cloneNode(node: XmlNode, newParentId: string | null = null): XmlNode {
const newId = generateId()
return {
id: newId,
tagName: node.tagName,
attributes: { ...node.attributes },
children: node.children.map(c => cloneNode(c, newId)),
textContent: node.textContent,
mixedContent: node.mixedContent.map(item => {
if (item.type === 'text') return { ...item }
// 元素引用需要更新 nodeId,但这里只做浅复制标记
return { ...item }
}),
parentId: newParentId
}
}
/**
* 获取节点在树中的显示名称
*/
export function getNodeDisplayName(node: XmlNode): string {
const tag = node.tagName
// 有标题的节点
const titleChild = node.children.find(c => c.tagName === 'TITLEC' || c.tagName === 'TITLE')
if (titleChild) {
const title = titleChild.textContent || getTextFromMixedContent(titleChild)
if (title) return `${tag}: ${title.slice(0, 40)}${title.length > 40 ? '...' : ''}`
}
// 有 KEY 属性的节点
if (node.attributes.KEY) {
return `${tag} [${node.attributes.KEY}]`
}
// EFFECT 节点显示适用范围
if (tag === 'EFFECT' && node.attributes.EFFRG) {
return `${tag}: ${node.attributes.EFFRG}`
}
// SIGNOFF 节点
if (tag === 'SIGNOFF' && node.attributes['CK-LEVEL']) {
return `${tag} (Level ${node.attributes['CK-LEVEL']})`
}
// 有文本的叶子节点
if (node.textContent) {
const text = node.textContent.trim()
return `${tag}: ${text.slice(0, 30)}${text.length > 30 ? '...' : ''}`
}
return tag
}
/**
* 从混合内容中提取纯文本
*/
function getTextFromMixedContent(node: XmlNode): string {
if (node.textContent) return node.textContent
return node.mixedContent
.filter(item => item.type === 'text')
.map(item => item.text || '')
.join('')
}
<template>
<div class="h-screen w-full flex flex-col items-center justify-center p-4">
<n-result status="404" title="404 资源不存在" description="生活总归带点荒谬">
<template #footer>
<CommonButton type="primary" @click="$router.push('/')">回到首页</CommonButton>
</template>
</n-result>
</div>
</template>
<script setup lang="ts"></script>
<template>
<div class="theme-page h-full overflow-auto">
<!-- 主色板 -->
<div class="section-card mb-5" :style="cardStyle">
<div class="section-header">
<div class="section-title text-pr">🎨 主色(Primary)</div>
<p class="section-desc">品牌主色,用于按钮、链接、选中态等核心交互元素</p>
</div>
<div class="flex gap-2 flex-wrap">
<div
v-for="shade in primaryShades"
:key="shade.token"
class="palette-swatch cursor-pointer"
:style="swatchStyle(shade.color, shade.level)"
@click="copyColor(shade.color, shade.token)"
>
<span class="swatch-label">{{ copiedToken === shade.token ? '✓ 已复制' : shade.label }}</span>
<span class="swatch-value">{{ truncate(shade.color) }}</span>
</div>
</div>
</div>
<!-- 成功色板 -->
<div class="section-card mb-5" :style="cardStyle">
<div class="section-header">
<div class="section-title">✅ 成功色(Success)</div>
<p class="section-desc">操作成功、完成状态等正向反馈场景</p>
</div>
<div class="flex gap-2 flex-wrap">
<div
v-for="shade in successShades"
:key="shade.token"
class="palette-swatch cursor-pointer"
:style="swatchStyle(shade.color, shade.level)"
@click="copyColor(shade.color, shade.token)"
>
<span class="swatch-label">{{ copiedToken === shade.token ? '✓ 已复制' : shade.label }}</span>
<span class="swatch-value">{{ truncate(shade.color) }}</span>
</div>
</div>
</div>
<!-- 警示色板 -->
<div class="section-card mb-5" :style="cardStyle">
<div class="section-header">
<div class="section-title">⚠️ 警示色(Warning)</div>
<p class="section-desc">需要注意的操作提醒、告警信息等场景</p>
</div>
<div class="flex gap-2 flex-wrap">
<div
v-for="shade in warningShades"
:key="shade.token"
class="palette-swatch cursor-pointer"
:style="swatchStyle(shade.color, shade.level)"
@click="copyColor(shade.color, shade.token)"
>
<span class="swatch-label">{{ copiedToken === shade.token ? '✓ 已复制' : shade.label }}</span>
<span class="swatch-value">{{ truncate(shade.color) }}</span>
</div>
</div>
</div>
<!-- 错误色板 -->
<div class="section-card mb-5" :style="cardStyle">
<div class="section-header">
<div class="section-title">❌ 错误色(Danger)</div>
<p class="section-desc">错误提示、危险操作、失败状态等场景</p>
</div>
<div class="flex gap-2 flex-wrap">
<div
v-for="shade in dangerShades"
:key="shade.token"
class="palette-swatch cursor-pointer"
:style="swatchStyle(shade.color, shade.level)"
@click="copyColor(shade.color, shade.token)"
>
<span class="swatch-label">{{ copiedToken === shade.token ? '✓ 已复制' : shade.label }}</span>
<span class="swatch-value">{{ truncate(shade.color) }}</span>
</div>
</div>
</div>
<!-- 链接色板 -->
<div class="section-card mb-5" :style="cardStyle">
<div class="section-header">
<div class="section-title">🔗 链接色(Link)</div>
<p class="section-desc">超链接、跳转指引等导航场景</p>
</div>
<div class="flex gap-2 flex-wrap">
<div
v-for="shade in linkShades"
:key="shade.token"
class="palette-swatch cursor-pointer"
:style="swatchStyle(shade.color, shade.level)"
@click="copyColor(shade.color, shade.token)"
>
<span class="swatch-label">{{ copiedToken === shade.token ? '✓ 已复制' : shade.label }}</span>
<span class="swatch-value">{{ truncate(shade.color) }}</span>
</div>
</div>
</div>
<!-- 文字 / 边框 / 填充 / 背景 一行 -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-5">
<!-- 文字颜色 -->
<div class="section-card" :style="cardStyle">
<div class="section-header">
<div class="section-title">📝 文字颜色(Text)</div>
<p class="section-desc">不同层级的文字颜色,从强调到辅助</p>
</div>
<div class="flex flex-wrap gap-4">
<div v-for="item in textColors" :key="item.token" class="color-chip cursor-pointer" @click="copyColor(item.color, item.token)">
<div class="chip-circle" :style="{ background: item.color, borderColor: themeVars.dividerColor }" />
<div class="chip-info">
<div class="chip-label" :style="{ color: themeVars.textColor2 }">
{{ copiedToken === item.token ? '✓ 已复制' : item.label }}
</div>
<div class="chip-token" :style="{ color: themeVars.textColor3 }">{{ item.token }}</div>
</div>
</div>
</div>
</div>
<!-- 边框颜色 -->
<div class="section-card" :style="cardStyle">
<div class="section-header">
<div class="section-title">▭ 边框颜色(Border)</div>
<p class="section-desc">分割线、输入框、卡片等边框场景</p>
</div>
<div class="flex flex-wrap gap-4">
<div v-for="item in borderColors" :key="item.token" class="color-chip cursor-pointer" @click="copyColor(item.color, item.token)">
<div class="chip-circle" :style="{ background: item.color, borderColor: themeVars.dividerColor }" />
<div class="chip-info">
<div class="chip-label" :style="{ color: themeVars.textColor2 }">
{{ copiedToken === item.token ? '✓ 已复制' : item.label }}
</div>
<div class="chip-token" :style="{ color: themeVars.textColor3 }">{{ item.token }}</div>
</div>
</div>
</div>
</div>
<!-- 填充颜色 -->
<div class="section-card" :style="cardStyle">
<div class="section-header">
<div class="section-title">🪣 填充颜色(Fill)</div>
<p class="section-desc">背景、禁用态、hover 态等填充场景</p>
</div>
<div class="flex flex-wrap gap-4">
<div v-for="item in fillColors" :key="item.token" class="color-chip cursor-pointer" @click="copyColor(item.color, item.token)">
<div class="chip-circle" :style="{ background: item.color, borderColor: themeVars.dividerColor }" />
<div class="chip-info">
<div class="chip-label" :style="{ color: themeVars.textColor2 }">
{{ copiedToken === item.token ? '✓ 已复制' : item.label }}
</div>
<div class="chip-token" :style="{ color: themeVars.textColor3 }">{{ item.token }}</div>
</div>
</div>
</div>
</div>
<!-- 背景颜色 -->
<div class="section-card" :style="cardStyle">
<div class="section-header">
<div class="section-title">🖼️ 背景颜色(Background)</div>
<p class="section-desc">页面、卡片、弹窗等不同层级背景色</p>
</div>
<div class="flex flex-wrap gap-4">
<div v-for="item in bgColors" :key="item.token" class="color-chip cursor-pointer" @click="copyColor(item.color, item.token)">
<div class="chip-circle" :style="{ background: item.color, borderColor: themeVars.dividerColor }" />
<div class="chip-info">
<div class="chip-label" :style="{ color: themeVars.textColor2 }">
{{ copiedToken === item.token ? '✓ 已复制' : item.label }}
</div>
<div class="chip-token" :style="{ color: themeVars.textColor3 }">{{ item.token }}</div>
</div>
</div>
</div>
</div>
</div>
<!-- 语义化颜色 -->
<div class="section-card mb-5" :style="cardStyle">
<div class="section-header">
<div class="section-title">🌈 功能色概览(Semantic)</div>
<p class="section-desc">Naive UI 组件系统核心语义化颜色,含 hover / pressed 状态</p>
</div>
<div class="semantic-grid">
<div
v-for="item in semanticColors"
:key="item.label"
class="semantic-block"
:style="{ border: `1px solid ${themeVars.dividerColor}` }"
>
<div class="semantic-main" :style="{ background: item.color }">
<span>{{ item.label }}</span>
</div>
<div class="semantic-sub">
<div class="semantic-sub-item" :style="{ background: item.hover }">hover</div>
<div class="semantic-sub-item" :style="{ background: item.pressed }">pressed</div>
</div>
<div class="semantic-footer" :style="{ background: themeVars.actionColor, color: themeVars.textColor3 }">
{{ item.token }}
</div>
</div>
</div>
</div>
<!-- 组件预览 -->
<div class="section-card mb-5" :style="cardStyle">
<div class="section-header">
<div class="section-title">🧩 组件效果预览</div>
<p class="section-desc">当前主题在常见 Naive UI 组件上的视觉表现</p>
</div>
<div class="components-preview">
<!-- 按钮 -->
<div class="preview-group">
<div class="preview-label" :style="{ color: themeVars.textColor3 }">按钮(Button)</div>
<div class="flex flex-wrap gap-2 items-center">
<n-button type="primary">主要按钮</n-button>
<n-button type="success">成功</n-button>
<n-button type="warning">警告</n-button>
<n-button type="error">错误</n-button>
<n-button>默认</n-button>
<n-button type="primary" ghost>描边</n-button>
<n-button type="primary" dashed>虚线</n-button>
<n-button type="primary" text>文字</n-button>
<n-button type="primary" disabled>禁用</n-button>
</div>
</div>
<!-- 标签 -->
<div class="preview-group">
<div class="preview-label" :style="{ color: themeVars.textColor3 }">标签(Tag)</div>
<div class="flex flex-wrap gap-2 items-center">
<n-tag type="primary">主要</n-tag>
<n-tag type="success">成功</n-tag>
<n-tag type="warning">警告</n-tag>
<n-tag type="error">错误</n-tag>
<n-tag type="info">信息</n-tag>
<n-tag type="primary" :bordered="false">无边框</n-tag>
<n-tag type="success" round>圆角</n-tag>
<n-tag type="warning" checkable>可选</n-tag>
</div>
</div>
<!-- 进度条 -->
<div class="preview-group">
<div class="preview-label" :style="{ color: themeVars.textColor3 }">进度条(Progress)</div>
<div class="space-y-2" style="max-width: 480px">
<n-progress type="line" :percentage="90" />
<n-progress type="line" :percentage="75" status="success" />
<n-progress type="line" :percentage="50" status="warning" />
<n-progress type="line" :percentage="30" status="error" />
</div>
</div>
<!-- 提示 -->
<div class="preview-group">
<div class="preview-label" :style="{ color: themeVars.textColor3 }">提示(Alert)</div>
<div class="space-y-2">
<n-alert type="info" title="信息提示" :bordered="false">这是一条信息提示</n-alert>
<n-alert type="success" title="成功提示" :bordered="false">操作执行成功</n-alert>
<n-alert type="warning" title="警告提示" :bordered="false">请注意相关风险</n-alert>
<n-alert type="error" title="错误提示" :bordered="false">操作发生异常</n-alert>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useAppStore } from '@/store/app/index'
import { lightThemeConfig, darkThemeConfig } from '@/configs/tailwind.ui.config'
const appStore = useAppStore()
const themeVars = useThemeVars()
// 当前生效的主题配置(响应式)
const currentTheme = computed(() => (appStore.isDark ? darkThemeConfig : lightThemeConfig))
/** 读取主题配置中的自定义扩展 token(GlobalThemeOverrides['common'] 类型之外的字段) */
const tok = (key: string): string => (currentTheme.value as Record<string, string>)[key] ?? ''
// 点击复制的 token 标识
const copiedToken = ref<string | null>(null)
/** 复制颜色值到剪贴板 */
async function copyColor(color: string, token: string) {
try {
await navigator.clipboard.writeText(color)
copiedToken.value = token
setTimeout(() => (copiedToken.value = null), 1500)
} catch (_) {}
}
/** 截断颜色值显示 */
function truncate(color: string): string {
return color.length > 13 ? color.slice(0, 13) + '…' : color
}
// ============ 卡片样式 ============
const cardStyle = computed(() => ({
background: themeVars.value.cardColor,
borderColor: themeVars.value.dividerColor
}))
// ============ 色板色块样式 ============
function swatchStyle(color: string, level: number) {
const isLight = level <= 2
return {
background: color,
border: level === 1 ? `1px solid ${themeVars.value.dividerColor}` : 'none',
'--swatch-text': isLight ? '#333' : '#fff'
}
}
// ============ 色阶构建 ============
function buildShades(prefix: string) {
return [1, 2, 3, 4, 5, 6, 7].map((n) => ({
level: n,
color: computed(() => (currentTheme.value as any)[`${prefix}${n}`] as string).value,
label: `${prefix.toUpperCase()}${n}`,
token: `${prefix}${n}`
}))
}
// 让色阶随主题响应式更新
const primaryShades = computed(() => buildShades('primary'))
const successShades = computed(() => buildShades('success'))
const warningShades = computed(() => buildShades('warning'))
const dangerShades = computed(() => buildShades('danger'))
const linkShades = computed(() => buildShades('link'))
// ============ 颜色分组 ============
const textColors = computed(() => [
{ label: '文字1(强调)', token: 'colorText1', color: tok('colorText1') },
{ label: '文字2(主要)', token: 'colorText2', color: tok('colorText2') },
{ label: '文字3(次要)', token: 'colorText3', color: tok('colorText3') },
{ label: '文字4(禁用)', token: 'colorText4', color: tok('colorText4') }
])
const borderColors = computed(() => [
{ label: '边框1(轻)', token: 'colorBorder1', color: tok('colorBorder1') },
{ label: '边框2(默认)', token: 'colorBorder2', color: tok('colorBorder2') },
{ label: '边框3(强)', token: 'colorBorder3', color: tok('colorBorder3') },
{ label: '边框4(最强)', token: 'colorBorder4', color: tok('colorBorder4') }
])
const fillColors = computed(() => [
{ label: '填充1', token: 'colorFill1', color: tok('colorFill1') },
{ label: '填充2', token: 'colorFill2', color: tok('colorFill2') },
{ label: '填充3', token: 'colorFill3', color: tok('colorFill3') },
{ label: '填充4', token: 'colorFill4', color: tok('colorFill4') }
])
const bgColors = computed(() => [
{ label: '背景1(基底)', token: 'colorBg1', color: tok('colorBg1') },
{ label: '背景2', token: 'colorBg2', color: tok('colorBg2') },
{ label: '背景3', token: 'colorBg3', color: tok('colorBg3') },
{ label: '背景4', token: 'colorBg4', color: tok('colorBg4') },
{ label: '背景5(深)', token: 'colorBg5', color: tok('colorBg5') }
])
const semanticColors = computed(() => [
{
label: '主色',
token: 'primaryColor',
color: themeVars.value.primaryColor,
hover: themeVars.value.primaryColorHover,
pressed: themeVars.value.primaryColorPressed
},
{
label: '信息色',
token: 'infoColor',
color: themeVars.value.infoColor,
hover: themeVars.value.infoColorHover,
pressed: themeVars.value.infoColorPressed
},
{
label: '成功色',
token: 'successColor',
color: themeVars.value.successColor,
hover: themeVars.value.successColorHover,
pressed: themeVars.value.successColorPressed
},
{
label: '警告色',
token: 'warningColor',
color: themeVars.value.warningColor,
hover: themeVars.value.warningColorHover,
pressed: themeVars.value.warningColorPressed
},
{
label: '错误色',
token: 'errorColor',
color: themeVars.value.errorColor,
hover: themeVars.value.errorColorHover,
pressed: themeVars.value.errorColorPressed
}
])
</script>
<style scoped>
.theme-page {
padding: 4px 0;
}
.page-header {
padding: 4px 0 8px;
}
.page-title {
font-size: 22px;
font-weight: 800;
margin: 0 0 4px;
color: v-bind('themeVars.textColor1');
letter-spacing: -0.5px;
}
.page-subtitle {
font-size: 13px;
color: v-bind('themeVars.textColor3');
margin: 0;
}
/* ========== 章节卡片 ========== */
.section-card {
border-width: 1px;
border-style: solid;
border-radius: 16px;
padding: 20px 24px;
transition:
box-shadow 0.3s ease,
background 0.3s ease;
}
.section-card:hover {
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
}
.section-header {
margin-bottom: 16px;
}
.section-title {
font-size: 15px;
font-weight: 700;
color: v-bind('themeVars.textColor1');
margin-bottom: 4px;
}
.section-desc {
font-size: 12px;
color: v-bind('themeVars.textColor3');
margin: 0;
}
/* ========== 色阶色块 ========== */
.palette-swatch {
border-radius: 12px;
width: 100px;
height: 72px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 3px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: all 0.2s ease;
user-select: none;
}
.palette-swatch:hover {
transform: scale(1.06) translateY(-3px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
}
.swatch-label {
font-size: 11px;
font-weight: 700;
color: var(--swatch-text, #fff);
opacity: 0.9;
}
.swatch-value {
font-size: 10px;
color: var(--swatch-text, #fff);
opacity: 0.6;
font-family: monospace;
}
/* ========== 圆形色块 ========== */
.color-chip {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
min-width: 86px;
transition: transform 0.2s ease;
}
.color-chip:hover {
transform: translateY(-2px);
}
.chip-circle {
width: 56px;
height: 56px;
border-radius: 50%;
border-width: 2px;
border-style: solid;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transition: transform 0.2s ease;
}
.color-chip:hover .chip-circle {
transform: scale(1.1);
}
.chip-info {
text-align: center;
}
.chip-label {
font-size: 11px;
font-weight: 600;
}
.chip-token {
font-size: 10px;
font-family: monospace;
}
/* ========== 语义化色块 ========== */
.semantic-grid {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.semantic-block {
border-radius: 12px;
overflow: hidden;
min-width: 150px;
flex: 1;
transition: box-shadow 0.2s ease;
}
.semantic-block:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}
.semantic-main {
height: 56px;
display: flex;
align-items: center;
padding-left: 16px;
font-weight: 700;
color: #fff;
font-size: 14px;
}
.semantic-sub {
display: flex;
}
.semantic-sub-item {
flex: 1;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
color: rgba(255, 255, 255, 0.9);
}
.semantic-footer {
padding: 5px 12px;
font-size: 11px;
font-family: monospace;
}
/* ========== 组件预览 ========== */
.components-preview {
display: flex;
flex-direction: column;
gap: 24px;
}
.preview-group {
display: flex;
flex-direction: column;
gap: 10px;
}
.preview-label {
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.8px;
}
</style>
<template>
<div class="flex-1 flex flex-col min-h-0 bg-transparent">
<!-- 头部工具栏 -->
<EditorToolbar
@save="handleSave"
@validate="handleValidate"
@export="handleExport"
@expand-all="handleExpandAll"
@collapse-all="handleCollapseAll"
/>
<!-- 主体布局 -->
<div class="flex-1 flex min-h-0">
<!-- 左侧节点树导航 -->
<div class="w-80 flex-shrink-0 h-full flex flex-col">
<NodeTree v-model:expandedKeys="expandedKeys" />
</div>
<!-- 右侧核心编辑区 -->
<div class="flex-1 h-full flex flex-col overflow-hidden" :style="{ background: themeVars.bodyColor }">
<EditorPanel />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useEditorStore } from '@/store/editor'
import { useXmlEditor } from './xmlEditor/functionals'
import EditorToolbar from './xmlEditor/components/EditorToolbar/index.vue'
import NodeTree from './xmlEditor/components/NodeTree/index.vue'
import EditorPanel from './xmlEditor/components/EditorPanel/index.vue'
const themeVars = useThemeVars()
const editorStore = useEditorStore()
const expandedKeys = ref<string[]>([])
// 实例化业务逻辑 Hook
const { initialize, save, exportXml, getAllNodeKeys, validate } = useXmlEditor()
onMounted(() => {
initialize()
})
// 保存操作
function handleSave() {
save()
}
// 导出文件操作
function handleExport() {
exportXml()
}
// 全部展开
function handleExpandAll() {
expandedKeys.value = getAllNodeKeys()
}
// 全部折叠
function handleCollapseAll() {
if (editorStore.xmlTree) {
expandedKeys.value = [editorStore.xmlTree.id]
}
}
// 基于 DTD 的静态语法检测
function handleValidate() {
validate()
}
</script>
<style scoped>
</style>
/**
* AttributeEditor 组件专用静态常量
*/
export const COMPONENT_NAME = 'AttributeEditor'
import { useEditorStore } from '@/store/editor'
/**
* 属性配置面板 (AttributeEditor) 组件专用 Hook 逻辑
*/
export function useAttributeEditor() {
const store = useEditorStore()
function updateAttributes(model: Record<string, string>): void {
const updatedAttrs: Record<string, string> = {}
for (const [k, v] of Object.entries(model)) {
if (v !== '' && v !== null && v !== undefined) {
updatedAttrs[k] = v
}
}
store.updateSelectedNodeAttributes(updatedAttrs)
}
return {
updateAttributes
}
}
<template>
<div v-if="hasAttributes" class="p-4 border border-divider rounded-lg shadow-sm bg-fill-2">
<div class="text-sm font-bold mb-3 flex items-center space-x-1 text-color1">
<n-icon :color="themeVars.primaryColor"><settings-outline /></n-icon>
<span>属性配置 ({{ node.tagName }})</span>
</div>
<n-form label-placement="left" label-width="120" size="small">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<n-form-item
v-for="(def, name) in attributesDef"
:key="name"
:label="name.toString()"
:required="def.requirement === '#REQUIRED'"
>
<n-select
v-if="def.enumValues && def.enumValues.length > 0"
v-model:value="model[name]"
:options="def.enumValues.map((v: any) => ({ label: v, value: v }))"
placeholder="请选择"
@update:value="handleAttrChange"
/>
<n-input
v-else
v-model:value="model[name]"
placeholder="请输入属性值"
@input="handleAttrChange"
/>
</n-form-item>
</div>
</n-form>
</div>
</template>
<script setup lang="ts">
import { SettingsOutline } from '@vicons/ionicons5'
import type { XmlNode } from '@/types/xmlNode'
import { getElementAttributes } from '@/utils/dtdManager'
import { useAttributeEditor } from './functionals'
const props = defineProps<{
node: XmlNode
}>()
const themeVars = useThemeVars()
const { updateAttributes } = useAttributeEditor()
const model = ref<Record<string, string>>({})
const attributesDef = computed(() => {
return getElementAttributes(props.node.tagName)
})
const hasAttributes = computed(() => {
return Object.keys(attributesDef.value).length > 0
})
watch(() => props.node.id, () => {
const nextModel: Record<string, string> = {}
for (const name of Object.keys(attributesDef.value)) {
nextModel[name] = props.node.attributes[name] || ''
}
model.value = nextModel
}, { immediate: true })
function handleAttrChange() {
updateAttributes(model.value)
}
</script>
<style scoped>
:deep(.n-form-item-blank) {
width: 100%;
}
</style>
// 列表节点标签定义
export const ALL_LIST_TAGS = ['LIST1', 'LIST2', 'LIST3', 'UNLIST']
// 表格节点标签定义
export const CALS_TABLE_TAGS = ['TABLE', 'TGROUP']
/**
* EditorPanel 组件级业务方法
*/
export class EditorPanelClass {
// 暂无组件私有逻辑
}
<template>
<div class="flex-1 flex flex-col min-h-0 bg-transparent">
<template v-if="selectedNode">
<!-- 顶部面包屑路径 -->
<div
class="px-4 py-2 border-b border-divider bg-fill-2 flex items-center space-x-2 text-xs"
>
<span class="text-color3 select-none">当前路径:</span>
<n-breadcrumb>
<n-breadcrumb-item
v-for="n in nodePath"
:key="n.id"
@click="editorStore.setSelectedNodeId(n.id)"
class="cursor-pointer hover:text-primary transition-colors text-color2 font-medium"
>
{{ n.tagName }}
</n-breadcrumb-item>
</n-breadcrumb>
</div>
<!-- 编辑主体区域 -->
<div class="flex-1 overflow-auto p-4 space-y-4 flex flex-col min-h-0">
<!-- 属性面板 -->
<AttributeEditor :node="selectedNode" />
<!-- 内容面板 -->
<div
class="flex-1 flex flex-col rounded-lg border border-divider shadow-sm overflow-hidden bg-fill-2"
>
<div class="px-4 py-3 border-b border-divider flex items-center space-x-1">
<n-icon :color="themeVars.primaryColor"><document-text-outline /></n-icon>
<span class="text-sm font-bold text-color1">{{ editorTitle }}</span>
</div>
<!-- 可视化编辑器按节点类型动态分流 -->
<component :is="activeEditor" :node="selectedNode" class="flex-1" />
</div>
</div>
</template>
<template v-else>
<div class="flex-1 flex flex-col items-center justify-center text-color3">
<n-empty size="large" description="请在左侧菜单树选择节点以开始编辑" />
</div>
</template>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { DocumentTextOutline } from '@vicons/ionicons5'
import { useEditorStore } from '@/store/editor'
import { getNodePath } from '@/utils/xmlParser'
import AttributeEditor from '../AttributeEditor/index.vue'
import TextBlockEditor from '../TextBlockEditor/index.vue'
import TableEditor from '../TableEditor/index.vue'
import ListEditor from '../ListEditor/index.vue'
import { CALS_TABLE_TAGS, ALL_LIST_TAGS } from './constants'
const themeVars = useThemeVars()
const editorStore = useEditorStore()
const selectedNode = computed(() => editorStore.selectedNode)
const nodePath = computed(() => {
if (!editorStore.xmlTree || !editorStore.selectedNodeId) return []
return getNodePath(editorStore.xmlTree, editorStore.selectedNodeId)
})
// 根据不同节点,展示对应样式的标题
const editorTitle = computed(() => {
if (!selectedNode.value) return '内容编辑'
const tag = selectedNode.value.tagName
if (CALS_TABLE_TAGS.includes(tag)) return 'CALS 可视化表格编辑'
if (ALL_LIST_TAGS.includes(tag)) return '层级列表可视化编辑'
return '内容编辑'
})
// 核心:分流渲染逻辑
const activeEditor = computed(() => {
if (!selectedNode.value) return TextBlockEditor
const tag = selectedNode.value.tagName
if (CALS_TABLE_TAGS.includes(tag)) {
return TableEditor
}
if (ALL_LIST_TAGS.includes(tag)) {
return ListEditor
}
return TextBlockEditor
})
</script>
<style scoped>
</style>
/**
* EditorToolbar 组件级静态常量
*/
export const TOOLBAR_TITLE = 'XML 编辑工具栏'
/**
* EditorToolbar 组件级业务逻辑 Hook
*/
export function useEditorToolbar() {
// 暂无专用逻辑,事件通过 emit 委托给父级页面
return {}
}
<template>
<div
class="flex items-center justify-between px-4 py-3 border-b border-divider bg-fill-2 transition-colors"
>
<!-- 左侧操作组 -->
<div class="flex items-center space-x-2">
<div class="flex items-center border border-divider rounded overflow-hidden">
<CommonButton type="primary" size="small" @click="emit('save')">
<template #icon>
<n-icon><save-outline /></n-icon>
</template>
保存
</CommonButton>
<CommonButton size="small" @click="emit('validate')" secondary class="border-l border-divider">
<template #icon>
<n-icon><checkmark-circle-outline /></n-icon>
</template>
验证 DTD
</CommonButton>
</div>
<n-divider vertical />
<!-- 撤销/重做 -->
<n-tooltip trigger="hover">
<template #trigger>
<CommonButton
size="small"
quaternary
:disabled="!canUndo"
@click="editorStore.undo()"
>
<template #icon>
<n-icon><arrow-undo-outline /></n-icon>
</template>
</CommonButton>
</template>
撤销 (Ctrl+Z)
</n-tooltip>
<n-tooltip trigger="hover">
<template #trigger>
<CommonButton
size="small"
quaternary
:disabled="!canRedo"
@click="editorStore.redo()"
>
<template #icon>
<n-icon><arrow-redo-outline /></n-icon>
</template>
</CommonButton>
</template>
重做 (Ctrl+Y)
</n-tooltip>
<n-divider vertical />
<!-- 展开/折叠 -->
<CommonButton size="small" secondary @click="emit('expand-all')">
展开全部
</CommonButton>
<CommonButton size="small" secondary @click="emit('collapse-all')" class="ml-2">
折叠全部
</CommonButton>
</div>
<!-- 右侧辅助组 -->
<div class="flex items-center space-x-2">
<n-tag :type="isValid ? 'success' : 'warning'" size="small" round>
{{ isValid ? 'DTD 验证通过' : '未验证 / 存在警告' }}
</n-tag>
<CommonButton size="small" tertiary @click="emit('export')">
<template #icon>
<n-icon><download-outline /></n-icon>
</template>
导出 XML
</CommonButton>
</div>
</div>
</template>
<script setup lang="ts">
import {
SaveOutline,
CheckmarkCircleOutline,
ArrowUndoOutline,
ArrowRedoOutline,
DownloadOutline
} from '@vicons/ionicons5'
import { useEditorStore } from '@/store/editor'
const emit = defineEmits(['save', 'validate', 'export', 'expand-all', 'collapse-all'])
const editorStore = useEditorStore()
const canUndo = computed(() => editorStore.undoStack.length > 0)
const canRedo = computed(() => editorStore.redoStack.length > 0)
const isValid = ref(true)
</script>
<style scoped>
</style>
// 列表项模型定义
export interface ListItemModel {
id: string
tagName: string
text: string
}
// 静态有序列表标签定义
export const ORDERED_LIST_TAGS = ['LIST1', 'LIST2', 'LIST3']
export const ALL_LIST_TAGS = ['LIST1', 'LIST2', 'LIST3', 'UNLIST']
import { useEditorStore } from '@/store/editor'
import type { XmlNode } from '@/types/xmlNode'
import type { ListItemModel } from '../constants'
import { ALL_LIST_TAGS } from '../constants'
/**
* 列表结构编辑器 (ListEditor) 组件专用 Hook 逻辑
*/
export function useListEditor() {
const store = useEditorStore()
/**
* 判断当前节点是否为列表容器
*/
function isListContainer(tagName: string): boolean {
return ALL_LIST_TAGS.includes(tagName)
}
/**
* 根据容器标签获取相对应的列表项子标签
*/
function getItemTagName(containerTag: string): string {
switch (containerTag) {
case 'LIST1': return 'L1ITEM'
case 'LIST2': return 'L2ITEM'
case 'LIST3': return 'L3ITEM'
case 'UNLIST': return 'UNLITEM'
default: return 'L1ITEM'
}
}
/**
* 提取列表容器内的所有子列表项
*/
function parseListItems(node: XmlNode): ListItemModel[] {
if (!isListContainer(node.tagName)) return []
const itemTagName = getItemTagName(node.tagName)
return node.children
.filter(c => c.tagName === itemTagName)
.map(itemNode => {
let text = itemNode.textContent || ''
const firstPara = itemNode.children.find(c => ['PARA', 'PARAC'].includes(c.tagName))
if (firstPara) {
text = firstPara.textContent || ''
}
return {
id: itemNode.id,
tagName: itemNode.tagName,
text
}
})
}
/**
* 修改具体列表项的内容,实时同步到列表项子树中
*/
function updateItemText(node: XmlNode, itemId: string, text: string): void {
const itemNode = node.children.find(c => c.id === itemId)
if (!itemNode) return
const para = itemNode.children.find(c => ['PARA', 'PARAC'].includes(c.tagName))
if (para) {
para.textContent = text
if (para.mixedContent.length > 0) {
para.mixedContent = [{ type: 'text', text }]
}
} else {
itemNode.textContent = text
}
store.triggerSync()
}
/**
* 添加一个新的列表项
*/
function addItem(node: XmlNode): void {
const itemTag = getItemTagName(node.tagName)
const itemId = crypto.randomUUID()
const newItem: XmlNode = {
id: itemId,
tagName: itemTag,
attributes: {},
children: [],
textContent: '',
mixedContent: [],
parentId: node.id
}
const paraId = crypto.randomUUID()
const newPara: XmlNode = {
id: paraId,
tagName: 'PARA',
attributes: {},
children: [],
textContent: '新列表项',
mixedContent: [],
parentId: itemId
}
newItem.children.push(newPara)
node.children.push(newItem)
store.triggerSync()
}
/**
* 删除一个列表项
*/
function deleteItem(node: XmlNode, itemId: string): void {
const idx = node.children.findIndex(c => c.id === itemId)
if (idx !== -1) {
node.children.splice(idx, 1)
store.triggerSync()
}
}
/**
* 移动列表项顺序
*/
function moveItem(node: XmlNode, itemId: string, direction: 'up' | 'down'): void {
const idx = node.children.findIndex(c => c.id === itemId)
if (idx === -1) return
const target = direction === 'up' ? idx - 1 : idx + 1
if (target < 0 || target >= node.children.length) return
const itemTag = getItemTagName(node.tagName)
if (node.children[target].tagName !== itemTag) return
const temp = node.children[idx]
node.children[idx] = node.children[target]
node.children[target] = temp
store.triggerSync()
}
return {
parseListItems,
updateItemText,
addItem,
deleteItem,
moveItem
}
}
<template>
<div class="flex-1 flex flex-col p-4 space-y-4 overflow-auto bg-transparent">
<!-- 顶部操作 -->
<div class="flex items-center justify-between pb-2 border-b border-divider">
<div class="flex items-center space-x-2">
<n-tag type="info" size="small">{{ node.tagName }}</n-tag>
<span class="text-xs text-color3">
{{ isOrdered ? '有序' : '无序' }}列表编辑器 (子项共: {{ listItems.length }} 个)
</span>
</div>
<CommonButton type="primary" size="tiny" secondary @click="handleAddItem">
<template #icon><n-icon><add-outline /></n-icon></template>
添加列表项
</CommonButton>
</div>
<!-- 列表项管理列表 -->
<div v-if="listItems.length > 0" class="space-y-3 max-w-4xl">
<div
v-for="(item, index) in listItems"
:key="item.id"
class="flex items-start space-x-3 p-2 rounded-lg border border-divider bg-fill-2 group hover:shadow-sm transition-all"
>
<!-- 序号/项目符号 -->
<div class="flex-shrink-0 text-sm font-bold text-color1 mt-1.5 w-6 select-none text-center">
<span v-if="isOrdered">{{ index + 1 }}.</span>
<span v-else class="text-base leading-none"></span>
</div>
<!-- 列表项内容修改框 -->
<div class="flex-1 flex flex-col space-y-1">
<div
contenteditable="true"
class="flex-1 min-h-[32px] px-2 py-1.5 rounded border border-divider focus:outline-none focus:ring-1 focus:ring-primary text-sm bg-fill-3 text-color2 leading-relaxed"
@blur="(e) => handleTextBlur(item.id, e)"
v-text="item.text"
></div>
</div>
<!-- 操作按钮组 -->
<div class="flex items-center space-x-1 opacity-0 group-hover:opacity-100 transition-opacity">
<CommonButton size="tiny" quaternary circle @click="handleMove(item.id, 'up')" :disabled="index === 0">
<template #icon><n-icon><arrow-up-outline /></n-icon></template>
</CommonButton>
<CommonButton size="tiny" quaternary circle @click="handleMove(item.id, 'down')" :disabled="index === listItems.length - 1">
<template #icon><n-icon><arrow-down-outline /></n-icon></template>
</CommonButton>
<CommonButton size="tiny" quaternary circle type="error" @click="handleDelete(item.id)">
<template #icon><n-icon><trash-outline /></n-icon></template>
</CommonButton>
</div>
</div>
</div>
<!-- 空状态 -->
<div v-else class="h-48 flex items-center justify-center text-color3">
<n-empty description="当前列表无任何子项,请点击上方“添加列表项”开始编辑" />
</div>
</div>
</template>
<script setup lang="ts">
import { AddOutline, TrashOutline, ArrowUpOutline, ArrowDownOutline } from '@vicons/ionicons5'
import type { XmlNode } from '@/types/xmlNode'
import { useListEditor } from './functionals'
import { ORDERED_LIST_TAGS } from './constants'
const props = defineProps<{
node: XmlNode
}>()
const { parseListItems, updateItemText, addItem, deleteItem, moveItem } = useListEditor()
const isOrdered = computed(() => ORDERED_LIST_TAGS.includes(props.node.tagName))
const listItems = ref(parseListItems(props.node))
watch(() => props.node, (newVal) => {
listItems.value = parseListItems(newVal)
}, { deep: true, immediate: true })
function handleTextBlur(itemId: string, e: FocusEvent) {
const el = e.target as HTMLElement
const val = el.innerText || ''
updateItemText(props.node, itemId, val)
}
function handleAddItem() {
addItem(props.node)
}
async function handleDelete(itemId: string) {
try {
await window.$dialog.warning({
title: '敏感删除确认',
content: '确定要删除该列表项吗?此操作将彻底删除此项及其所有文本。'
})
deleteItem(props.node, itemId)
window.$message.success('删除列表项成功')
} catch (e) {
// 取消删除
}
}
function handleMove(itemId: string, direction: 'up' | 'down') {
moveItem(props.node, itemId, direction)
}
</script>
<style scoped>
</style>
// 列表/文档型叶子节点标签判定
export const DOCUMENT_LIKE_TAGS = ['PARA', 'PARAC', 'TITLE', 'TITLEC', 'WARNING', 'CAUTION', 'NOTE']
import { useEditorStore } from '@/store/editor'
import { h } from 'vue'
import type { TreeOption, DropdownOption } from 'naive-ui'
import type { XmlNode } from '@/types/xmlNode'
import { findNodeById, findParentNode } from '@/utils/xmlParser'
import {
getAllowedChildren,
getInsertableChildren,
canDeleteChild,
createDefaultAttributes
} from '@/utils/dtdManager'
/**
* 节点树 (NodeTree) 组件专用 Hook 逻辑
*/
export function useNodeTree() {
const store = useEditorStore()
/**
* 将 XML 内部节点树结构,转换为 Naive UI n-tree 组件接收的 Option 格式
*/
function convertNodeToOption(node: XmlNode): TreeOption {
let subtitle = ''
if (node.attributes.ID) {
subtitle = ` : ${node.attributes.ID}`
} else if (node.attributes.EFFECT) {
subtitle = ` : ${node.attributes.EFFECT}`
} else if (node.textContent && node.textContent.trim().length > 0) {
const clean = node.textContent.trim()
subtitle = ` : ${clean.length > 25 ? clean.substring(0, 25) + '...' : clean}`
}
const option: TreeOption = {
key: node.id,
label: node.tagName,
tagName: node.tagName,
subtitle: subtitle,
children: []
}
if (node.children && node.children.length > 0) {
option.children = node.children.map(c => convertNodeToOption(c))
}
return option
}
/**
* 自定义树节点渲染 Label 方法
*/
function renderLabel({ option }: { option: TreeOption }) {
const tagName = (option.tagName as string) || (option.label as string)
const subtitle = (option.subtitle as string) || ''
return h('div', { class: 'flex items-center space-x-1' }, [
h('span', { class: 'font-bold text-sm' }, tagName),
subtitle ? h('span', { class: 'text-xs text-secondary italic' }, subtitle) : null
])
}
/**
* 动态生成右键菜单选项,支持基于 DTD 的添加和删除校验
*/
function getDropdownOptions(nodeId: string): DropdownOption[] {
const tree = store.xmlTree
if (!tree) return []
const node = findNodeById(tree, nodeId)
if (!node) return []
const options: DropdownOption[] = []
const allowed = getAllowedChildren(node.tagName)
if (allowed.length > 0) {
const insertable = getInsertableChildren(node.tagName, node.children.map(c => c.tagName))
const subOptions = insertable.map(tag => ({
label: tag,
key: `add-child-${tag}`
}))
options.push({
label: '添加子节点',
key: 'add-child',
children: subOptions.length > 0 ? subOptions : [{ label: '无可用子节点 (超约束上限)', key: 'none', disabled: true }]
})
}
const parent = findParentNode(tree, nodeId)
if (parent) {
const childCount = parent.children.filter(c => c.tagName === node.tagName).length
const deletable = canDeleteChild(parent.tagName, node.tagName, childCount)
options.push({
label: deletable ? '删除该节点' : '删除该节点 (不可删,受 DTD 约束)',
key: 'delete-node',
disabled: !deletable
})
}
if (parent) {
const parentAllowed = getAllowedChildren(parent.tagName)
const parentInsertable = getInsertableChildren(parent.tagName, parent.children.map(c => c.tagName))
const insertableOptions = parentInsertable.map(tag => ({
label: tag,
key: `insert-sibling-${tag}`
}))
if (insertableOptions.length > 0) {
options.push({
label: '在后方插入兄弟节点',
key: 'insert-sibling',
children: insertableOptions
})
}
}
return options
}
/**
* 分发并执行右键菜单指令
*/
async function handleDropdownAction(key: string, nodeId: string): Promise<void> {
const tree = store.xmlTree
if (!tree) return
const node = findNodeById(tree, nodeId)
if (!node) return
const parent = findParentNode(tree, nodeId)
if (key.startsWith('add-child-')) {
const childTag = key.replace('add-child-', '')
const childId = crypto.randomUUID()
const newChild: XmlNode = {
id: childId,
tagName: childTag,
attributes: createDefaultAttributes(childTag),
children: [],
textContent: '',
mixedContent: [],
parentId: nodeId
}
store.addChildNode(newChild)
store.setSelectedNodeId(childId)
window.$message.success(`成功添加子节点 <${childTag}>`)
}
else if (key === 'delete-node') {
if (nodeId === tree.id) {
window.$message.warning('不能删除根节点')
return
}
try {
await window.$dialog.warning({
title: '敏感删除确认',
content: `确定要删除节点 <${node.tagName}> 吗?该操作将连带删除其所有子节点!`
})
store.deleteSelectedNode()
window.$message.success('节点删除成功')
} catch (e) {
// 取消删除
}
}
else if (key.startsWith('insert-sibling-')) {
if (!parent) return
const sibTag = key.replace('insert-sibling-', '')
const sibId = crypto.randomUUID()
const newSib: XmlNode = {
id: sibId,
tagName: sibTag,
attributes: createDefaultAttributes(sibTag),
children: [],
textContent: '',
mixedContent: [],
parentId: parent.id
}
const currIdx = parent.children.findIndex(c => c.id === nodeId)
store.saveSnapshot()
parent.children.splice(currIdx + 1, 0, newSib)
store.setSelectedNodeId(sibId)
window.$message.success(`成功在后方插入兄弟节点 <${sibTag}>`)
}
}
return {
convertNodeToOption,
renderLabel,
getDropdownOptions,
handleDropdownAction
}
}
<template>
<div class="flex flex-col h-full border-r border-divider relative">
<!-- 搜索过滤 -->
<div class="p-3 border-b border-divider">
<n-input v-model:value="pattern" placeholder="搜索节点名称..." size="small">
<template #prefix>
<n-icon><search-outline /></n-icon>
</template>
</n-input>
</div>
<!-- 树组件容器 -->
<div class="flex-1 overflow-auto p-2">
<n-tree
v-if="treeData.length > 0"
block-line
expand-on-click
:data="treeData"
:expanded-keys="expandedKeys"
:selected-keys="selectedKeys"
:pattern="pattern"
:render-label="renderLabel"
:render-prefix="renderPrefix"
@update:selected-keys="handleSelect"
@update:expanded-keys="handleExpand"
@node-contextmenu="handleContextMenu"
/>
<div v-else class="h-full flex items-center justify-center text-color3">
<n-empty description="暂无节点数据" />
</div>
</div>
<!-- 右键下拉菜单 -->
<n-dropdown
trigger="manual"
placement="bottom-start"
:show="showDropdown"
:options="dropdownOptions"
:x="dropdownX"
:y="dropdownY"
@clickoutside="showDropdown = false"
@select="handleDropdownSelect"
/>
</div>
</template>
<script setup lang="ts">
import type { TreeOption, DropdownOption } from 'naive-ui'
import { NIcon } from 'naive-ui'
import {
SearchOutline,
CodeWorkingOutline,
DocumentTextOutline,
FolderOpenOutline
} from '@vicons/ionicons5'
import { useEditorStore } from '@/store/editor'
import { useNodeTree } from './functionals'
import { DOCUMENT_LIKE_TAGS } from './constants'
const props = defineProps<{
expandedKeys: string[]
}>()
const emit = defineEmits(['update:expandedKeys'])
const editorStore = useEditorStore()
const { convertNodeToOption, renderLabel, getDropdownOptions, handleDropdownAction } = useNodeTree()
const pattern = ref('')
const selectedKeys = computed(() => editorStore.selectedNodeId ? [editorStore.selectedNodeId] : [])
// 下拉菜单状态
const showDropdown = ref(false)
const dropdownX = ref(0)
const dropdownY = ref(0)
const contextNodeId = ref<string | null>(null)
// 转换数据源,委托给 Hook
const treeData = computed(() => {
if (!editorStore.xmlTree) return []
return [convertNodeToOption(editorStore.xmlTree)]
})
// 默认展开所有一级子节点
const expandedKeys = ref<string[]>([])
watch(() => editorStore.xmlTree, (newVal) => {
if (newVal && expandedKeys.value.length === 0) {
const keys = [newVal.id, ...newVal.children.map((c: any) => c.id)]
expandedKeys.value = keys
emit('update:expandedKeys', keys)
}
}, { immediate: true })
watch(() => props.expandedKeys, (keys) => {
expandedKeys.value = keys
})
function handleSelect(keys: string[]) {
if (keys.length > 0) {
editorStore.setSelectedNodeId(keys[0])
}
}
function handleExpand(keys: string[]) {
expandedKeys.value = keys
emit('update:expandedKeys', keys)
}
function renderPrefix({ option }: { option: TreeOption }) {
const tagName = option.tagName as string
let icon = CodeWorkingOutline
if (option.children && option.children.length > 0) {
icon = FolderOpenOutline
} else if (DOCUMENT_LIKE_TAGS.includes(tagName)) {
icon = DocumentTextOutline
}
return h(NIcon, { size: 16 }, { default: () => h(icon) })
}
// 右键下拉菜单数据
const dropdownOptions = computed<DropdownOption[]>(() => {
if (!contextNodeId.value) return []
return getDropdownOptions(contextNodeId.value)
})
function handleContextMenu(e: MouseEvent, option: TreeOption) {
e.preventDefault()
showDropdown.value = false
contextNodeId.value = option.key as string
nextTick(() => {
dropdownX.value = e.clientX
dropdownY.value = e.clientY
showDropdown.value = true
})
}
// 菜单选择处理,委托给 Hook
async function handleDropdownSelect(key: string) {
showDropdown.value = false
if (!contextNodeId.value) return
await handleDropdownAction(key, contextNodeId.value)
// 如果添加了节点,确保父级节点展开状态
if (key.startsWith('add-child-')) {
if (!expandedKeys.value.includes(contextNodeId.value)) {
const nextKeys = [...expandedKeys.value, contextNodeId.value]
expandedKeys.value = nextKeys
emit('update:expandedKeys', nextKeys)
}
}
}
</script>
<style scoped>
:deep(.n-tree-node-content) {
padding: 4px 8px !important;
border-radius: 4px;
transition: all 0.2s ease;
}
:deep(.n-tree-node-content:hover) {
background-color: var(--primary-color-hover) !important;
opacity: 0.85;
}
:deep(.n-tree-node-content--selected) {
background-color: var(--primary-color) !important;
color: white !important;
}
:deep(.n-tree-node-content--selected .n-text) {
color: white !important;
}
</style>
import type { XmlNode } from '@/types/xmlNode'
export interface TableCellModel {
id: string
text: string
attributes: Record<string, string>
}
export interface TableRowModel {
id: string
cells: TableCellModel[]
}
export interface TableStructureModel {
cols: number
colSpecs: XmlNode[]
theadRows: TableRowModel[]
tbodyRows: TableRowModel[]
}
// 表格组件相关的提示文本定义
export const CELL_EDIT_TIP = '提示:表格支持直接点击单元格进行双击/聚焦修改,光标离开时自动保存文本。'
export const DEFAULT_CELL_TEXT = '新单元格'
import { useEditorStore } from '@/store/editor'
import type { XmlNode } from '@/types/xmlNode'
import type { TableRowModel, TableStructureModel } from '../constants'
/**
* CALS 表格编辑器 (TableEditor) 组件专用 Hook 逻辑
*/
export function useTableEditor() {
const store = useEditorStore()
/**
* 辅助查找当前节点树中的 TGROUP 节点
*/
function findTgroup(node: XmlNode): XmlNode | null {
if (node.tagName === 'TGROUP') return node
if (node.tagName === 'TABLE') {
const tgroup = node.children.find(c => c.tagName === 'TGROUP')
return tgroup || null
}
return null
}
/**
* 解析 TABLE/TGROUP 结构,转化为可视化渲染的模型
*/
function parseTable(node: XmlNode): TableStructureModel {
const tgroup = findTgroup(node)
if (!tgroup) {
return { cols: 0, colSpecs: [], theadRows: [], tbodyRows: [] }
}
const cols = parseInt(tgroup.attributes.COLS || '0', 10) || 1
const colSpecs = tgroup.children.filter(c => c.tagName === 'COLSPEC')
const thead = tgroup.children.find(c => c.tagName === 'THEAD')
const tbody = tgroup.children.find(c => c.tagName === 'TBODY')
const parseRows = (sectionNode?: XmlNode): TableRowModel[] => {
if (!sectionNode) return []
return sectionNode.children
.filter(c => c.tagName === 'ROW')
.map(rowNode => {
const cells = rowNode.children
.filter(c => c.tagName === 'ENTRY')
.map(cellNode => ({
id: cellNode.id,
text: cellNode.textContent || '',
attributes: { ...cellNode.attributes }
}))
// 补齐缺少的列,避免渲染空洞
while (cells.length < cols) {
cells.push({
id: crypto.randomUUID(),
text: '',
attributes: {}
})
}
return {
id: rowNode.id,
cells
}
})
}
return {
cols,
colSpecs,
theadRows: parseRows(thead),
tbodyRows: parseRows(tbody)
}
}
/**
* 创建一个默认的 ENTRY 单元格
*/
function createDefaultEntry(parentRowId: string): XmlNode {
return {
id: crypto.randomUUID(),
tagName: 'ENTRY',
attributes: {},
children: [],
textContent: '',
mixedContent: [],
parentId: parentRowId
}
}
/**
* 更新具体单元格的文本值
*/
function updateCellText(node: XmlNode, cellId: string, text: string): void {
const tgroup = findTgroup(node)
if (!tgroup) return
const findAndChange = (currentNode: XmlNode): boolean => {
if (currentNode.id === cellId) {
currentNode.textContent = text
if (currentNode.mixedContent.length > 0) {
currentNode.mixedContent = [{ type: 'text', text }]
}
return true
}
for (const child of currentNode.children) {
if (findAndChange(child)) return true
}
return false
}
findAndChange(tgroup)
store.triggerSync()
}
/**
* 添加一行数据
*/
function addRow(node: XmlNode, section: 'THEAD' | 'TBODY' = 'TBODY'): void {
const tgroup = findTgroup(node)
if (!tgroup) return
let sectionNode = tgroup.children.find(c => c.tagName === section)
if (!sectionNode) {
sectionNode = {
id: crypto.randomUUID(),
tagName: section,
attributes: {},
children: [],
textContent: '',
mixedContent: [],
parentId: tgroup.id
}
tgroup.children.push(sectionNode)
}
const cols = parseInt(tgroup.attributes.COLS || '1', 10)
const rowId = crypto.randomUUID()
const newRow: XmlNode = {
id: rowId,
tagName: 'ROW',
attributes: {},
children: [],
textContent: '',
mixedContent: [],
parentId: sectionNode.id
}
for (let i = 0; i < cols; i++) {
newRow.children.push(createDefaultEntry(rowId))
}
sectionNode.children.push(newRow)
store.triggerSync()
}
/**
* 删除一行数据
*/
function deleteRow(node: XmlNode, rowId: string): void {
const tgroup = findTgroup(node)
if (!tgroup) return
const thead = tgroup.children.find(c => c.tagName === 'THEAD')
const tbody = tgroup.children.find(c => c.tagName === 'TBODY')
const removeNode = (sectionNode?: XmlNode): boolean => {
if (!sectionNode) return false
const idx = sectionNode.children.findIndex(c => c.id === rowId)
if (idx !== -1) {
sectionNode.children.splice(idx, 1)
return true
}
return false
}
if (removeNode(tbody) || removeNode(thead)) {
store.triggerSync()
}
}
/**
* 增加一列数据
*/
function addColumn(node: XmlNode): void {
const tgroup = findTgroup(node)
if (!tgroup) return
const currentCols = parseInt(tgroup.attributes.COLS || '0', 10)
const nextCols = currentCols + 1
tgroup.attributes.COLS = nextCols.toString()
const colSpecIndex = tgroup.children.filter(c => c.tagName === 'COLSPEC').length
const newColSpec: XmlNode = {
id: crypto.randomUUID(),
tagName: 'COLSPEC',
attributes: {
COLNAME: `col${colSpecIndex + 1}`,
COLNUM: (colSpecIndex + 1).toString()
},
children: [],
textContent: '',
mixedContent: [],
parentId: tgroup.id
}
const lastColSpecIdx = tgroup.children.reduce((acc, curr, idx) => {
return curr.tagName === 'COLSPEC' ? idx : acc
}, -1)
tgroup.children.splice(lastColSpecIdx + 1, 0, newColSpec)
const thead = tgroup.children.find(c => c.tagName === 'THEAD')
const tbody = tgroup.children.find(c => c.tagName === 'TBODY')
const appendCellToRows = (sectionNode?: XmlNode) => {
if (!sectionNode) return
sectionNode.children
.filter(c => c.tagName === 'ROW')
.forEach(row => {
row.children.push(createDefaultEntry(row.id))
})
}
appendCellToRows(thead)
appendCellToRows(tbody)
store.triggerSync()
}
/**
* 删除一列数据
*/
function deleteColumn(node: XmlNode, colIndex: number): void {
const tgroup = findTgroup(node)
if (!tgroup) return
const currentCols = parseInt(tgroup.attributes.COLS || '0', 10)
if (currentCols <= 1) {
window.$message.warning('表格必须保留至少一列')
return
}
tgroup.attributes.COLS = (currentCols - 1).toString()
const colSpecs = tgroup.children.filter(c => c.tagName === 'COLSPEC')
if (colSpecs[colIndex]) {
const specId = colSpecs[colIndex].id
const idx = tgroup.children.findIndex(c => c.id === specId)
if (idx !== -1) tgroup.children.splice(idx, 1)
}
const thead = tgroup.children.find(c => c.tagName === 'THEAD')
const tbody = tgroup.children.find(c => c.tagName === 'TBODY')
const deleteCellFromRows = (sectionNode?: XmlNode) => {
if (!sectionNode) return
sectionNode.children
.filter(c => c.tagName === 'ROW')
.forEach(row => {
const entries = row.children.filter(c => c.tagName === 'ENTRY')
if (entries[colIndex]) {
const cellId = entries[colIndex].id
const entryIdx = row.children.findIndex(c => c.id === cellId)
if (entryIdx !== -1) row.children.splice(entryIdx, 1)
}
})
}
deleteCellFromRows(thead)
deleteCellFromRows(tbody)
store.triggerSync()
}
return {
parseTable,
updateCellText,
addRow,
deleteRow,
addColumn,
deleteColumn
}
}
<template>
<div class="flex-1 flex flex-col p-4 space-y-4 overflow-auto bg-transparent min-h-0">
<!-- 表格工具栏 -->
<div class="flex items-center justify-between pb-2 border-b border-divider">
<div class="flex items-center space-x-2">
<n-tag type="info" size="small">{{ node.tagName }}</n-tag>
<span class="text-xs text-color3">{{ CELL_EDIT_TIP }}</span>
</div>
<div class="flex items-center space-x-2">
<CommonButton size="tiny" secondary type="primary" @click="handleColumnAdd">
<template #icon><n-icon><add-outline /></n-icon></template>
添加列
</CommonButton>
<CommonButton size="tiny" secondary type="primary" @click="handleRowAdd('TBODY')">
<template #icon><n-icon><add-outline /></n-icon></template>
添加行
</CommonButton>
</div>
</div>
<!-- 可视化二维表格视图 -->
<div class="flex-1 overflow-auto border border-divider rounded-lg shadow-inner bg-fill-2 p-2">
<table class="w-full border-collapse text-sm table-fixed min-w-[600px]">
<!-- 表头规格 -->
<colgroup>
<col v-for="i in structure.cols" :key="i" class="min-w-[120px]" />
<col class="w-[80px]" />
</colgroup>
<!-- THEAD 渲染 -->
<thead v-if="structure.theadRows.length > 0">
<tr
v-for="row in structure.theadRows"
:key="row.id"
class="border-b border-divider hover:bg-fill-3 group"
>
<th
v-for="(cell, cIdx) in row.cells"
:key="cell.id"
class="p-2 text-left font-bold bg-fill-4 border border-divider text-color1"
>
<div
contenteditable="true"
class="w-full min-h-[28px] px-1.5 py-1 rounded focus:outline-none focus:ring-1 focus:ring-primary focus:bg-fill-1 text-color1"
@blur="(e) => handleCellBlur(cell.id, e)"
v-text="cell.text"
></div>
</th>
<!-- 表头操作栏 -->
<th class="p-2 border border-divider bg-fill-4 text-center">
<CommonButton size="tiny" quaternary circle type="error" @click="handleRowDelete(row.id)">
<template #icon><n-icon><trash-outline /></n-icon></template>
</CommonButton>
</th>
</tr>
</thead>
<!-- TBODY 渲染 -->
<tbody>
<tr
v-for="row in structure.tbodyRows"
:key="row.id"
class="border-b border-divider hover:bg-fill-3 group"
>
<td
v-for="cell in row.cells"
:key="cell.id"
class="p-2 border border-divider text-color2"
>
<div
contenteditable="true"
class="w-full min-h-[28px] px-1.5 py-1 rounded focus:outline-none focus:ring-1 focus:ring-primary focus:bg-fill-1 text-color2 leading-relaxed"
@blur="(e) => handleCellBlur(cell.id, e)"
v-text="cell.text"
></div>
</td>
<!-- 行删除按钮 -->
<td class="p-2 border border-divider text-center">
<CommonButton size="tiny" quaternary circle type="error" @click="handleRowDelete(row.id)">
<template #icon><n-icon><trash-outline /></n-icon></template>
</CommonButton>
</td>
</tr>
<!-- 列操作管理辅助行 -->
<tr class="hover:bg-transparent">
<td
v-for="(_, cIdx) in structure.cols"
:key="cIdx"
class="p-1 text-center bg-transparent border-none"
>
<CommonButton
size="tiny"
quaternary
circle
type="error"
:disabled="structure.cols <= 1"
@click="handleColumnDelete(cIdx)"
>
<template #icon><n-icon><trash-outline /></n-icon></template>
</CommonButton>
</td>
<td class="p-1 bg-transparent border-none"></td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script setup lang="ts">
import { AddOutline, TrashOutline } from '@vicons/ionicons5'
import type { XmlNode } from '@/types/xmlNode'
import { useTableEditor } from './functionals'
import { CELL_EDIT_TIP } from './constants'
const props = defineProps<{
node: XmlNode
}>()
const { parseTable, updateCellText, addRow, deleteRow, addColumn, deleteColumn } = useTableEditor()
const structure = ref(parseTable(props.node))
watch(() => props.node, (newVal) => {
structure.value = parseTable(newVal)
}, { deep: true, immediate: true })
function handleCellBlur(cellId: string, e: FocusEvent) {
const el = e.target as HTMLElement
const val = el.innerText || ''
updateCellText(props.node, cellId, val)
}
function handleRowAdd(section: 'THEAD' | 'TBODY') {
addRow(props.node, section)
}
async function handleRowDelete(rowId: string) {
try {
await window.$dialog.warning({
title: '敏感删除确认',
content: '确定要删除这一行吗?该行内所有的单元格数据将被同时清除。'
})
deleteRow(props.node, rowId)
window.$message.success('删除行成功')
} catch (e) {
// 取消删除
}
}
function handleColumnAdd() {
addColumn(props.node)
}
async function handleColumnDelete(colIndex: number) {
try {
await window.$dialog.warning({
title: '敏感删除确认',
content: '确定要删除这一列吗?整列中所有行对应的单元格数据将被彻底清除!'
})
deleteColumn(props.node, colIndex)
window.$message.success('删除列成功')
} catch (e) {
// 取消删除
}
}
</script>
<style scoped>
</style>
export interface SliceItem {
type: 'text' | 'element'
text?: string
elementTagName?: string
attributes: Record<string, string>
attributesDef?: any
}
// 静态配置和选项名
export const DEFAULT_INSERT_TEXT_VAL = '新文本内容'
import { useEditorStore } from '@/store/editor'
import {
isMixedContentElement,
getElementAttributes,
createDefaultAttributes
} from '@/utils/dtdManager'
import type { XmlNode, MixedContentItem } from '@/types/xmlNode'
import type { SliceItem } from '../constants'
import { DEFAULT_INSERT_TEXT_VAL } from '../constants'
/**
* 内容编辑面板 (TextBlockEditor) 组件专用 Hook 逻辑
*/
export function useTextBlockEditor() {
const store = useEditorStore()
function parseSlices(node: XmlNode): SliceItem[] {
const isMixed = isMixedContentElement(node.tagName)
if (!isMixed) return []
const list: SliceItem[] = []
if (node.mixedContent.length === 0 && node.textContent) {
list.push({
type: 'text',
text: node.textContent,
attributes: {}
})
} else {
for (const item of node.mixedContent) {
if (item.type === 'text') {
list.push({
type: 'text',
text: item.text || '',
attributes: {}
})
} else if (item.type === 'element' && item.nodeId) {
const child = node.children.find(c => c.id === item.nodeId)
if (child) {
list.push({
type: 'element',
elementTagName: child.tagName,
attributes: { ...child.attributes },
attributesDef: getElementAttributes(child.tagName)
})
}
}
}
}
return list
}
function syncSlices(nodeId: string, slicesList: SliceItem[]): void {
const newMixedContent: MixedContentItem[] = []
const newChildren: XmlNode[] = []
for (const slice of slicesList) {
if (slice.type === 'text') {
newMixedContent.push({
type: 'text',
text: slice.text || ''
})
} else if (slice.type === 'element' && slice.elementTagName) {
const uuid = crypto.randomUUID()
newMixedContent.push({
type: 'element',
nodeId: uuid
})
newChildren.push({
id: uuid,
tagName: slice.elementTagName,
attributes: { ...slice.attributes },
children: [],
textContent: '',
mixedContent: [],
parentId: nodeId
})
}
}
store.updateSelectedNodeMixedContent(newMixedContent, newChildren)
}
function insertSlice(slicesList: SliceItem[], key: string, index: number): SliceItem[] {
const newList = [...slicesList]
if (key === 'insert-text') {
newList.splice(index, 0, {
type: 'text',
text: DEFAULT_INSERT_TEXT_VAL,
attributes: {}
})
} else if (key.startsWith('insert-element-')) {
const tag = key.replace('insert-element-', '')
newList.splice(index, 0, {
type: 'element',
elementTagName: tag,
attributes: createDefaultAttributes(tag),
attributesDef: getElementAttributes(tag)
})
}
return newList
}
function moveSlice(slicesList: SliceItem[], index: number, direction: 'up' | 'down'): SliceItem[] {
const newList = [...slicesList]
const target = direction === 'up' ? index - 1 : index + 1
if (target < 0 || target >= newList.length) return newList
const temp = newList[index]
newList[index] = newList[target]
newList[target] = temp
return newList
}
return {
parseSlices,
syncSlices,
insertSlice,
moveSlice
}
}
<template>
<div class="flex-1 flex flex-col p-4 space-y-4 overflow-auto bg-transparent">
<!-- 头部节点提示 -->
<div class="flex items-center justify-between pb-2 border-b border-divider">
<div class="flex items-center space-x-2">
<n-tag type="info" size="small">{{ node.tagName }}</n-tag>
<span class="text-xs text-color3">{{ dtdDescription }}</span>
</div>
<div v-if="isMixed" class="text-xs text-color3">
混合内容节点(支持嵌入行内元素)
</div>
</div>
<!-- 情况 1:纯文本编辑 -->
<div v-if="isTextOnly" class="flex-1 flex flex-col space-y-2">
<div class="text-xs font-bold text-color2">节点文本内容:</div>
<div
contenteditable="true"
class="content-editable-box rounded p-3 text-sm focus:outline-none focus:ring-1 focus:ring-primary border border-divider bg-fill-3 text-color2 min-h-[120px] leading-relaxed"
@blur="handleTextBlur"
@keydown.enter.prevent
v-text="pureTextVal"
></div>
<div class="text-xs text-color3 italic">光标移开或失去焦点时会自动保存修改。</div>
</div>
<!-- 情况 2:混合内容编辑 (切片积木式) -->
<div v-else-if="isMixed" class="flex-1 flex flex-col space-y-3">
<div class="flex items-center justify-between">
<span class="text-xs font-bold text-color2">混合内容片段流:</span>
<CommonButton size="tiny" secondary type="primary" @click="handleAddHeaderSlice">
<template #icon><n-icon><add-outline /></n-icon></template>
头部插入片段
</CommonButton>
</div>
<div class="space-y-2 max-w-4xl">
<div
v-for="(item, index) in slices"
:key="index"
class="slice-item flex items-start space-x-2 p-2 rounded border border-divider group transition-all"
:style="{
background: item.type === 'element' ? 'var(--primary-color-hover)' : themeVars.cardColor
}"
>
<!-- 片段序号 -->
<div class="text-xs text-color3 mt-1.5 w-6 select-none text-center">
#{{ index + 1 }}
</div>
<!-- 1. 文本片段 -->
<div v-if="item.type === 'text'" class="flex-1 flex items-center space-x-2">
<n-tag size="small" type="success" class="flex-shrink-0">文本</n-tag>
<div
contenteditable="true"
class="flex-1 min-h-[32px] px-2 py-1.5 rounded border border-divider focus:outline-none focus:ring-1 focus:ring-primary text-sm bg-fill-3 text-color2"
@blur="(e) => handleSliceTextBlur(index, e)"
v-text="item.text"
></div>
</div>
<!-- 2. 行内元素标签片段 -->
<div v-else-if="item.type === 'element'" class="flex-1 flex flex-col space-y-2">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-2">
<n-tag size="small" type="warning" class="font-bold">
{{ item.elementTagName }}
</n-tag>
<span class="text-xs text-color3">行内嵌入元素</span>
</div>
<div class="text-xs italic text-color3 max-w-md truncate">
{{ formatAttributes(item.attributes) }}
</div>
</div>
<!-- 行内元素属性快捷编辑 -->
<div class="grid grid-cols-2 gap-2 p-2 rounded bg-fill-3 border border-divider text-xs">
<div v-for="(attrDef, attrName) in item.attributesDef" :key="attrName" class="flex items-center space-x-1">
<span class="text-color3 w-20 truncate">{{ attrName }}:</span>
<n-select
v-if="attrDef.enumValues && attrDef.enumValues.length > 0"
v-model:value="item.attributes[attrName]"
:options="attrDef.enumValues.map((v: any) => ({ label: v, value: v }))"
size="tiny"
placeholder="选择"
class="flex-1"
@update:value="syncMixedContent"
/>
<n-input
v-else
v-model:value="item.attributes[attrName]"
size="tiny"
placeholder="输入"
class="flex-1"
@input="syncMixedContent"
/>
</div>
</div>
</div>
<!-- 右侧控制区 -->
<div class="flex items-center space-x-1 opacity-0 group-hover:opacity-100 transition-opacity">
<CommonButton size="tiny" quaternary circle @click="handleMoveSlice(index, 'up')" :disabled="index === 0">
<template #icon><n-icon><arrow-up-outline /></n-icon></template>
</CommonButton>
<CommonButton size="tiny" quaternary circle @click="handleMoveSlice(index, 'down')" :disabled="index === slices.length - 1">
<template #icon><n-icon><arrow-down-outline /></n-icon></template>
</CommonButton>
<CommonButton size="tiny" quaternary circle type="error" @click="deleteSlice(index)">
<template #icon><n-icon><trash-outline /></n-icon></template>
</CommonButton>
</div>
</div>
<!-- 底部添加按钮 -->
<div class="flex justify-center py-2">
<n-dropdown
trigger="click"
:options="insertOptions"
@select="(key) => handleInsertSelect(key, slices.length)"
>
<CommonButton size="small" dashed type="primary">
<template #icon><n-icon><add-outline /></n-icon></template>
追加片段
</CommonButton>
</n-dropdown>
</div>
</div>
</div>
<!-- 情况 3:空节点 -->
<div v-else-if="isEmpty" class="flex-1 flex items-center justify-center">
<n-result status="info" title="空元素" description="此节点属于空元素类型,无需填写文本。请在上方属性面板配置其所需属性值。">
</n-result>
</div>
<!-- 情况 4:容器节点 -->
<div v-else class="flex-1 flex items-center justify-center">
<n-result status="success" title="容器结构节点" description="此节点是文档树中的容器分类,无单独的文本内容。请在左侧节点树管理它的子节点。">
</n-result>
</div>
</div>
</template>
<script setup lang="ts">
import { AddOutline, TrashOutline, ArrowUpOutline, ArrowDownOutline } from '@vicons/ionicons5'
import type { DropdownOption } from 'naive-ui'
import type { XmlNode } from '@/types/xmlNode'
import {
isTextOnlyElement,
isMixedContentElement,
isEmptyElement,
getElementRule,
getAllowedChildren
} from '@/utils/dtdManager'
import { useEditorStore } from '@/store/editor'
import { useTextBlockEditor } from './functionals'
import type { SliceItem } from './constants'
const props = defineProps<{
node: XmlNode
}>()
const themeVars = useThemeVars()
const editorStore = useEditorStore()
const { parseSlices, syncSlices, insertSlice, moveSlice } = useTextBlockEditor()
const dtdRule = computed(() => getElementRule(props.node.tagName))
const dtdDescription = computed(() => dtdRule.value?.description || '')
const isTextOnly = computed(() => isTextOnlyElement(props.node.tagName))
const isMixed = computed(() => isMixedContentElement(props.node.tagName))
const isEmpty = computed(() => isEmptyElement(props.node.tagName))
const pureTextVal = ref('')
watch(() => props.node.id, () => {
pureTextVal.value = props.node.textContent || ''
}, { immediate: true })
function handleTextBlur(e: FocusEvent) {
const el = e.target as HTMLElement
const val = el.innerText || ''
if (val !== props.node.textContent) {
editorStore.updateSelectedNodeText(val)
}
}
const slices = ref<SliceItem[]>([])
watch(() => props.node.id, () => {
if (isMixed.value) {
slices.value = parseSlices(props.node)
}
}, { immediate: true })
function formatAttributes(attrs: Record<string, string>): string {
return Object.entries(attrs)
.map(([k, v]) => `${k}="${v}"`)
.join(' ')
}
// 同步切片,委托给 Hook
function syncMixedContent() {
syncSlices(props.node.id, slices.value)
}
function handleSliceTextBlur(index: number, e: FocusEvent) {
const el = e.target as HTMLElement
const newText = el.innerText || ''
if (slices.value[index].text !== newText) {
slices.value[index].text = newText
syncMixedContent()
}
}
const insertOptions = computed<DropdownOption[]>(() => {
const options: DropdownOption[] = [
{ label: '插入文本片段', key: 'insert-text' }
]
const allowed = getAllowedChildren(props.node.tagName)
if (allowed.length > 0) {
options.push({ type: 'divider', key: 'div1' })
allowed.forEach(tag => {
options.push({
label: `行内元素: ${tag}`,
key: `insert-element-${tag}`
})
})
}
return options
})
// 插入片段,委托给 Hook
function handleInsertSelect(key: string, index: number) {
slices.value = insertSlice(slices.value, key, index)
syncMixedContent()
}
function handleAddHeaderSlice() {
slices.value = insertSlice(slices.value, 'insert-text', 0)
syncMixedContent()
}
async function deleteSlice(index: number) {
try {
await window.$dialog.warning({
title: '敏感删除确认',
content: '确定要删除该片段吗?该操作会将该段文本或行内子节点及其所有关联属性彻底清除。'
})
slices.value.splice(index, 1)
syncMixedContent()
window.$message.success('删除片段成功')
} catch (e) {
// 取消删除
}
}
function handleMoveSlice(index: number, direction: 'up' | 'down') {
slices.value = moveSlice(slices.value, index, direction)
syncMixedContent()
}
</script>
<style scoped>
.content-editable-box:focus {
box-shadow: 0 0 0 2px var(--primary-color-hover);
border-color: var(--primary-color) !important;
}
.slice-item {
transition: all 0.2s cubic-bezier(0.25, 0.8, 0.25, 1);
}
.slice-item:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
</style>
/**
* XML 编辑器静态常量定义
*/
export const DEFAULT_FILE_NAME = 'AMEA-A282400-02-1_0_0_updated.xml'
// 文本与混合节点标签分类
export const TEXT_ONLY_TAGS = ['TITLE', 'TITLEC', 'ZONE']
export const MIXED_CONTENT_TAGS = ['PARA', 'PARAC', 'WARNING', 'CAUTION', 'NOTE']
// 列表节点标签定义
export const ORDERED_LIST_TAGS = ['LIST1', 'LIST2', 'LIST3']
export const ALL_LIST_TAGS = ['LIST1', 'LIST2', 'LIST3', 'UNLIST']
// 表格节点标签定义
export const CALS_TABLE_TAGS = ['TABLE', 'TGROUP']
import { useEditorStore } from '@/store/editor'
import { loadDtdSchema, getElementRule } from '@/utils/dtdManager'
import { parseXmlToTree, serializeTreeToXml } from '@/utils/xmlParser'
import type { XmlNode } from '@/types/xmlNode'
import { DEFAULT_FILE_NAME } from '../constants'
import { h } from 'vue'
// 静态导入 DTD JSON
import dtdJson from '@/assets/json/dtd.json'
// 导入原始 XML 文本 (?raw)
import xmlText from '@/assets/file/AMEA-A282400-02-1_0_0.xml?raw'
/**
* 工卡 XML 编辑器核心业务逻辑 Hook
*/
export function useXmlEditor() {
const store = useEditorStore()
function initialize(): void {
try {
loadDtdSchema(dtdJson as any)
const tree = parseXmlToTree(xmlText)
store.setXmlTree(tree)
window.$message.success('工卡 XML 数据与 DTD 规则加载成功')
} catch (e: any) {
window.$notification.error(e.message || '加载 XML 配置文件出错,请检查语法', {
title: '初始化失败'
})
}
}
function save(): void {
if (!store.xmlTree) return
const xml = serializeTreeToXml(store.xmlTree)
console.log('保存的 XML 数据:\n', xml)
window.$message.success('本地修改已保存(可查看浏览器控制台输出)')
}
function exportXml(): void {
if (!store.xmlTree) return
try {
const xml = serializeTreeToXml(store.xmlTree)
const blob = new Blob([xml], { type: 'application/xml;charset=utf-8;' })
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', DEFAULT_FILE_NAME)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.$message.success('工卡 XML 导出下载成功')
} catch (e: any) {
window.$message.error(`导出失败: ${e.message}`)
}
}
function getAllNodeKeys(): string[] {
if (!store.xmlTree) return []
const keys: string[] = []
const collect = (node: XmlNode) => {
keys.push(node.id)
node.children.forEach(collect)
}
collect(store.xmlTree)
return keys
}
function validate(): void {
if (!store.xmlTree) return
const warnings: string[] = []
const validateNode = (node: XmlNode) => {
const rule = getElementRule(node.tagName)
if (rule) {
for (const [attrName, attrDef] of Object.entries(rule.attributes)) {
if (attrDef.requirement === '#REQUIRED' && !node.attributes[attrName]) {
warnings.push(`节点 <${node.tagName}> 缺失必填属性: ${attrName}`)
}
}
for (const child of node.children) {
if (!rule.allowedChildren.includes(child.tagName)) {
warnings.push(`节点 <${node.tagName}> 包含不允许的子节点 <${child.tagName}>`)
}
}
} else {
warnings.push(`元素 <${node.tagName}> 在 DTD 中未定义`)
}
node.children.forEach(validateNode)
}
validateNode(store.xmlTree)
if (warnings.length === 0) {
window.$notification.success('未发现任何结构与属性异常,XML 结构完全合规!', {
title: 'DTD 验证成功'
})
} else {
window.$notification.warning(`发现 ${warnings.length} 处潜在的合规性问题。`, {
title: 'DTD 语法检测警告',
meta: () => h('div', { class: 'mt-2 max-h-48 overflow-auto space-y-1' },
warnings.slice(0, 10).map(w => h('div', { class: 'text-xs text-warning' }, w))
)
})
}
}
return {
initialize,
save,
exportXml,
getAllNodeKeys,
validate
}
}
<!-- 废弃,已迁移至 src/views/xmlEditor.vue -->
<template>
<div></div>
</template>
/** @type {import('tailwindcss').Config} */
export default {
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
darkMode: 'class',
theme: {
extend: {
colors: {
primary: {
DEFAULT: 'var(--primary-color)',
hover: 'var(--primary-color-hover)',
pressed: 'var(--primary-color-pressed)',
...Object.fromEntries([1, 2, 3, 4, 5, 6, 7].map((i) => [i, `var(--primary${i})`]))
},
success: {
DEFAULT: 'var(--success-color)',
hover: 'var(--success-color-hover)',
pressed: 'var(--success-color-pressed)',
...Object.fromEntries([1, 2, 3, 4, 5, 6, 7].map((i) => [i, `var(--success${i})`]))
},
warning: {
DEFAULT: 'var(--warning-color)',
hover: 'var(--warning-color-hover)',
pressed: 'var(--warning-color-pressed)',
...Object.fromEntries([1, 2, 3, 4, 5, 6, 7].map((i) => [i, `var(--warning${i})`]))
},
danger: {
DEFAULT: 'var(--error-color)',
...Object.fromEntries([1, 2, 3, 4, 5, 6, 7].map((i) => [i, `var(--danger${i})`]))
},
link: {
...Object.fromEntries([1, 2, 3, 4, 5, 6, 7].map((i) => [i, `var(--link${i})`]))
},
info: 'var(--info-color)',
body: 'var(--body-color)',
card: 'var(--card-color)',
fill: {
...Object.fromEntries([1, 2, 3, 4].map((i) => [i, `var(--colorFill${i})`]))
}
},
// 如果需要使用 bg-primary6 这种形式(中间没有横杠)
// 可以在此处添加额外的扁平化映射
extend: {
...Object.fromEntries(
['primary', 'success', 'warning', 'danger', 'link'].flatMap((c) =>
[1, 2, 3, 4, 5, 6, 7].map((i) => [`${c}${i}`, `var(--${c}${i})`])
)
)
},
textColor: {
primary: 'var(--text-color-1)',
regular: 'var(--text-color-2)',
secondary: 'var(--text-color-3)',
...Object.fromEntries([1, 2, 3, 4].map((i) => [`color${i}`, `var(--colorText${i})`]))
},
borderColor: {
divider: 'var(--divider-color)',
base: 'var(--border-color)',
...Object.fromEntries([1, 2, 3, 4].map((i) => [`color${i}`, `var(--colorBorder${i})`]))
}
}
},
plugins: []
}
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
"types": ["vite/client"]
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "src/auto-imports.d.ts", "src/components.d.ts"]
}
{
"files": [],
"references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
}
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import fs from 'fs'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
// https://vite.dev/config/
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
const proxyUrl = env.VITE_APP_PROXY_URL
return {
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
},
plugins: [
vue(),
AutoImport({
// 自动导入 Vue 相关函数,如: ref, h, computed, watch...
imports: [
'vue',
{
'vue-router': ['createRouter', 'createWebHashHistory', 'useRouter', 'useRoute']
},
'pinia',
{
'vue-i18n': ['useI18n']
},
{
'naive-ui': ['useLoadingBar', 'useThemeVars', 'NIcon', 'NTag', 'NButton', 'NSpace']
}
],
// 自动导入 hooks 和 utils 目录下的模块
dirs: ['src/hooks', 'src/utils', 'src/api'],
// 生成 auto-imports.d.ts 类型声明文件
dts: 'src/auto-imports.d.ts'
}),
Components({
resolvers: [
// 自动按需引入 Naive UI 组件
NaiveUiResolver()
],
// 生成 components.d.ts 类型声明文件
dts: 'src/components.d.ts'
}),
// 自定义中间件用于在开发阶段优先从本地 shtml 目录读取静态资源
{
name: 'serve-shtml-static',
configureServer(server) {
server.middlewares.use((req, res, next) => {
const shtmlRegex = /^\/(views|css|fonts|img|js|locales|plugins|scripts)\//
if (req.url && shtmlRegex.test(req.url)) {
const pathname = req.url.split('?')[0]
const localFilePath = path.join(__dirname, 'shtml/src/main/resources/static', pathname)
if (fs.existsSync(localFilePath) && fs.statSync(localFilePath).isFile()) {
const ext = path.extname(localFilePath).toLowerCase()
let contentType = 'text/plain'
if (ext === '.html' || ext === '.shtml') {
contentType = 'text/html; charset=utf-8'
res.setHeader('Content-Type', contentType)
const processSSI = (filePath: string): string => {
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
return `<!-- SSI Error: ${filePath} not found -->`
}
let text = fs.readFileSync(filePath, 'utf8')
const ssiRegex = /<!--#include\s+virtual="([^"]+)"\s*-->/g
return text.replace(ssiRegex, (_, includePath) => {
const resolvedPath = path.join(
__dirname,
'shtml/src/main/resources/static',
includePath.startsWith('/') ? includePath.slice(1) : includePath
)
return processSSI(resolvedPath)
})
}
res.end(processSSI(localFilePath))
return
}
if (ext === '.js') contentType = 'application/javascript; charset=utf-8'
else if (ext === '.css') contentType = 'text/css; charset=utf-8'
else if (ext === '.json') contentType = 'application/json; charset=utf-8'
else if (ext === '.png') contentType = 'image/png'
else if (ext === '.jpg' || ext === '.jpeg') contentType = 'image/jpeg'
else if (ext === '.gif') contentType = 'image/gif'
else if (ext === '.svg') contentType = 'image/svg+xml'
else if (ext === '.woff') contentType = 'font/woff'
else if (ext === '.woff2') contentType = 'font/woff2'
res.setHeader('Content-Type', contentType)
fs.createReadStream(localFilePath).pipe(res)
return
}
}
next()
})
}
}
],
base: './', // 打包路径
server: {
port: 5555,
host: true
}
}
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment