init
@@ -0,0 +1,9 @@
|
||||
[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}]
|
||||
charset = utf-8
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
end_of_line = lf
|
||||
max_line_length = 100
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
||||
"printWidth": 100,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"ignorePatterns": ["dist", "src/types/auto-imports.d.ts", "components.d.ts"]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# DCalc frontend-ui
|
||||
|
||||
`frontend-ui` is the progressive UI refactor of the current `frontend` application. The original
|
||||
project remains available as the behavior baseline.
|
||||
|
||||
## Foundation
|
||||
|
||||
- Vue 3, Vue Router, Pinia, Vite and TypeScript track the current stable release line.
|
||||
- Reka UI provides accessible interaction primitives.
|
||||
- Tailwind CSS 4 provides layout utilities without Preflight during the migration period.
|
||||
- CSS variables define the stable semantic contract between components and themes.
|
||||
- SCSS/CSS remains available for game textures, image borders and state effects.
|
||||
- `class-variance-authority` defines component variants in the local, source-owned UI layer.
|
||||
- Oxc (`oxlint` and `oxfmt`) handles linting and formatting.
|
||||
|
||||
## Theme model
|
||||
|
||||
Components consume `--ui-*` semantic variables. Each skin maps its own palette and effects to that
|
||||
contract under `[data-theme]`. Add a theme stylesheet and register it in `useTheme.ts`; component
|
||||
code should not need to change.
|
||||
|
||||
The initial themes are:
|
||||
|
||||
- `dcalc`: tokens captured from `design/src-web`.
|
||||
- `midnight`: a second skin used to verify that components are not coupled to the classic palette.
|
||||
|
||||
## Commands
|
||||
|
||||
```sh
|
||||
pnpm install
|
||||
pnpm dev
|
||||
pnpm check
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Open `/components` for the complete catalog of currently introduced components and their live
|
||||
interactions. `/ui-preview` remains the smaller foundation smoke-test page.
|
||||
|
||||
See [`docs/ui-migration-standard.md`](docs/ui-migration-standard.md) for the reusable component
|
||||
migration, theming, responsive implementation and browser QA standard.
|
||||
|
||||
The current catalog covers Button, Badge, Panel, Input, NumberField, Checkbox, RadioGroup, Switch,
|
||||
Rating, Select, Slider, Progress, Tabs, ItemSlot, DataTable, Tooltip, DropdownMenu and Dialog.
|
||||
|
||||
## Migration rule
|
||||
|
||||
New UI components live under `src/components/ui`. Route changes, keyboard shortcuts and DNF data
|
||||
mapping belong to pages, composables or domain components instead of generic primitives. Existing
|
||||
`calc-*` components remain only until their business call sites are migrated.
|
||||
@@ -0,0 +1,56 @@
|
||||
# Component fidelity QA
|
||||
|
||||
- Source visual truth: `/Users/xigua/.codex/visualizations/2026/08/04/019fcbb3-69c4-7363-81ba-fa53cab1424e/design-board-2-1920.png`
|
||||
- Implementation: `/Users/xigua/.codex/visualizations/2026/08/04/019fcbb3-69c4-7363-81ba-fa53cab1424e/catalog-fidelity-audit-final-1920.png`
|
||||
- Viewport: 1920 × 1200 CSS px
|
||||
- Source pixels: 1920 × 1200
|
||||
- Implementation pixels: 1920 × 1200
|
||||
- Density normalization: devicePixelRatio 1; no resampling required
|
||||
- State: default catalog with Button state matrix; interactive Dialog state captured separately at `catalog-audit-dialog-animated.png`
|
||||
|
||||
## Full-view comparison
|
||||
|
||||
The implementation keeps the source board's full-width dark surface, 46 px panel headings, brass dividers, DCalc typography, square corners, compact 30 px controls, and dense state matrices. The catalog intentionally adds a component index, theme controls, and implementation-specific copy; these are catalog navigation rather than component anatomy changes.
|
||||
|
||||
## Focused comparisons
|
||||
|
||||
- Button: three five-column state rows now match the source's Rest, Hover, Pressed, Disabled, and tone-family organization. Panel height is 281.5 px versus the 292 px source module target; the difference is the catalog's smaller bottom spacing and is visually non-material.
|
||||
- NumberField: 30 px field, 23 px spinner, visible up/down arrows, and two 14 px `+10`/`-10` fast-step buttons match the source anatomy.
|
||||
- Tabs: 40 px list, 30 px triggers, glow layers, divider line, animated 39 × 5 px arrow marker, and line-marker variant are present. Marker movement follows Reka selection state and respects reduced motion.
|
||||
- Dialog: the titlebar uses the exact 45 raster frames from `windowheadereffect.img`, advances every 66 ms while focused, and hides the effect in the unfocused/reduced-motion state.
|
||||
- Responsive: document overflow is absent at 1920, 1280, 768, and 390 widths. At 390 px, DataTable keeps a 328 px viewport with an intentional 560 px internal scroll surface.
|
||||
|
||||
## Required fidelity surfaces
|
||||
|
||||
- Fonts and typography: DCalc/SimSun control typography and Segoe UI/Chinese fallbacks retain the source hierarchy and compact line-height.
|
||||
- Spacing and layout rhythm: page gutters use `clamp()`, panels use container-relative padding, grids collapse at 1280/900/520 breakpoints, and atomic game-control sizes stay pixel-aligned.
|
||||
- Colors and tokens: blue action surfaces, brass edges, gold selection, blue focus, disabled gray, semantic status colors, and rarity colors map to DCalc CSS variables.
|
||||
- Image quality and assets: stars, rarity frames, item icons, select arrow, dialog close icon, and all 45 title-effect frames use original raster assets with zero broken images.
|
||||
- Copy and content: catalog copy differs only where it documents the Reka implementation or exposes live state; component labels and source examples remain representative.
|
||||
|
||||
## Interaction verification
|
||||
|
||||
- NumberField fast step: 1 → 11.
|
||||
- Tabs: line marker moved to `来源`; selected state became active.
|
||||
- Checkbox: unchecked → checked.
|
||||
- Switch: checked → unchecked.
|
||||
- Rating: 1 → 3.
|
||||
- Select: 装备 → 材料.
|
||||
- DropdownMenu: `导出结果` updated live status.
|
||||
- Dialog: title frame changed from `4.PNG` to `5.PNG`; cancel restored focus to `打开 WindowDialog`.
|
||||
|
||||
## Comparison history
|
||||
|
||||
- P1: NumberField step affordances were too small and FastStep was missing. Fixed with source-sized arrows and `+10`/`-10` controls.
|
||||
- P1: Tabs used a trigger-local pseudo marker and could not reproduce source movement. Fixed with a list-owned marker driven by active Reka tab geometry.
|
||||
- P1: Dialog titlebar had a static gradient only. Fixed with the exact focused 45-frame animation.
|
||||
- P2: Button showcase omitted source state rows. Fixed with the complete three-row state matrix.
|
||||
- P2: shadcn-like radii, generic focus rings, menu surface, and tooltip transitions drifted from DNF styling. Replaced with source-specific square surfaces and state treatments.
|
||||
- P2: layout relied on fixed page spacing. Replaced page-level spacing and grids with fluid/container-responsive rules while preserving atomic raster dimensions.
|
||||
|
||||
## Remaining intentional deviations
|
||||
|
||||
- The catalog header includes theme switching and a component index that are not present on Design board 2.
|
||||
- Components not yet migrated (CommandInput, TreeSelect, Cascader, navigation, ScrollArea, and complex product dialogs) are out of this comparison and must be covered in later migration batches.
|
||||
|
||||
final result: passed
|
||||
@@ -0,0 +1,257 @@
|
||||
# DCalc UI 统一迁移与实现标准
|
||||
|
||||
本文档用于在其他 Vue 项目中复用当前 `frontend-ui` 的组件库迁移方法。目标不是机械复制页面,而是在保持业务功能的前提下,统一交互底座、视觉语言、主题能力、响应式策略和验收流程。
|
||||
|
||||
## 1. 标准目标
|
||||
|
||||
- 以现有业务实现为功能基线,允许调整业务代码以适配新的组件组合方式。
|
||||
- 以 `design` 的组件结构、状态、动效、素材和交互为视觉事实来源。
|
||||
- 使用 Reka UI 承担无障碍、键盘、焦点、受控状态和 Portal 等交互语义。
|
||||
- 使用 Tailwind CSS 处理通用布局,关闭 Preflight,避免迁移期全局样式突变。
|
||||
- 使用 CSS Variables 建立 DCalc Token 和语义 Token,组件只消费语义 Token。
|
||||
- 使用 CSS/SCSS 实现游戏质感、像素素材、复杂渐变、边框和状态效果。
|
||||
- 参考 shadcn-vue 的目录、组合、表单和 variants 规范,不要求引入整套默认视觉。
|
||||
- 使用 Oxc 统一格式化和静态检查。
|
||||
|
||||
## 2. 事实来源与优先级
|
||||
|
||||
发生冲突时按以下顺序判断:
|
||||
|
||||
1. 业务功能、数据边界和用户可完成的操作不能丢失。
|
||||
2. `design` 中的组件结构、状态矩阵、动效和真实素材决定视觉结果。
|
||||
3. Reka UI 的可访问性与交互语义决定组件的基础 DOM 和状态模型。
|
||||
4. 组件 API、目录和组合方式遵循本标准。
|
||||
5. 仅在不影响以上约束时使用通用 Tailwind 工具类简化布局。
|
||||
|
||||
不要以静态截图代替业务与交互核对。设计中未展示的状态仍需覆盖 hover、pressed、focus-visible、disabled、open/closed、checked/unchecked 和 reduced-motion。
|
||||
|
||||
## 3. 推荐技术栈
|
||||
|
||||
| 层级 | 标准职责 |
|
||||
| ------------------------ | ----------------------------------------------------------- |
|
||||
| Vue 3 + TypeScript | 使用 Composition API、`<script setup>` 和类型化 Props/Emits |
|
||||
| Vue Router + Pinia | 保留现有路由和业务状态边界 |
|
||||
| Reka UI | Primitive、焦点管理、键盘交互、受控状态、Portal |
|
||||
| Tailwind CSS | 栅格、Flex、尺寸约束和简单响应式布局 |
|
||||
| CSS Variables | Primitive Token、Semantic Token、主题映射 |
|
||||
| CSS/SCSS | 游戏纹理、分层渐变、图片边框、复杂动效 |
|
||||
| class-variance-authority | Button、Badge 等离散 variants |
|
||||
| clsx + tailwind-merge | 条件 class 合并,统一由 `cn()` 暴露 |
|
||||
| oxlint + oxfmt | Lint、格式化和 CI 检查 |
|
||||
|
||||
## 4. 推荐目录
|
||||
|
||||
```text
|
||||
src/
|
||||
├── components/
|
||||
│ ├── ui/ # 无业务语义的基础组件
|
||||
│ │ └── button/
|
||||
│ │ ├── UiButton.vue
|
||||
│ │ ├── buttonVariants.ts
|
||||
│ │ └── index.ts
|
||||
│ ├── domain/ # 领域组合组件
|
||||
│ └── hooks/ # Dialog 等跨组件编排
|
||||
├── composables/
|
||||
│ └── useTheme.ts
|
||||
├── lib/
|
||||
│ └── utils.ts # cn()
|
||||
├── pages/
|
||||
│ └── ComponentCatalog/ # 全组件状态与交互目录
|
||||
└── style/
|
||||
├── tokens/primitives.css
|
||||
├── themes/dcalc.css
|
||||
├── themes/<skin>.css
|
||||
├── components/
|
||||
├── tailwind.css
|
||||
└── index.scss
|
||||
```
|
||||
|
||||
基础组件不得直接请求接口、读取业务 Store 或绑定具体页面路由。业务行为放在页面、领域组件、Store 或 composable 中。
|
||||
|
||||
## 5. Token 与换肤标准
|
||||
|
||||
Token 分成两层:
|
||||
|
||||
1. Primitive Token 保存 DCalc 的原始色值、品质颜色和基础素材色。
|
||||
2. Semantic Token 表达组件用途,如 `--ui-background`、`--ui-primary`、`--ui-border`、`--ui-ring`。
|
||||
|
||||
组件只使用 `--ui-*` 语义变量;主题文件在 `[data-theme='<name>']` 下完成映射。新增皮肤时:
|
||||
|
||||
1. 新建 `style/themes/<name>.css`。
|
||||
2. 完整实现现有 `--ui-*` 契约。
|
||||
3. 在 Tailwind 入口中导入主题文件。
|
||||
4. 在 `useTheme.ts` 注册主题名。
|
||||
5. 使用组件目录页逐状态验证,不修改组件内部代码。
|
||||
|
||||
禁止在组件中以 `data-theme` 分支写特定皮肤逻辑。确需固定的源素材颜色时,保留物理色值,并在代码注释或 QA 文档中注明它属于素材还原而非主题语义。
|
||||
|
||||
## 6. 单组件迁移流程
|
||||
|
||||
### 6.1 盘点功能
|
||||
|
||||
- 找出旧组件的所有调用点、Props、Emits、插槽、v-model、快捷键和禁用条件。
|
||||
- 沿调用链确认业务副作用,避免只复制组件外观。
|
||||
- 记录必须保持的功能和允许调整的旧 API。
|
||||
|
||||
### 6.2 拆解 design
|
||||
|
||||
- 对照组件结构、真实尺寸、边框层、渐变、素材、文本和间距。
|
||||
- 建立完整状态矩阵:rest、hover、pressed、focus、disabled、selected/open。
|
||||
- 动效需记录帧数、间隔、缓动、焦点条件和 reduced-motion 行为。
|
||||
- 优先复用原始 PNG、字体、图标和 rarity frame,不用近似图标替代。
|
||||
|
||||
### 6.3 选择 Reka Primitive
|
||||
|
||||
- 先使用 Reka 已有 Primitive,再包装视觉层。
|
||||
- 使用 Reka 提供的 `data-state`、`data-disabled`、ARIA 和键盘模型。
|
||||
- 不重复实现已有的焦点陷阱、Escape 关闭、方向键选择和焦点恢复。
|
||||
- 只有 design 明确增加的行为才在包装层补充,例如 NumberField 快速步进和 Tabs 移动标记。
|
||||
|
||||
### 6.4 实现包装组件
|
||||
|
||||
标准模式:
|
||||
|
||||
```ts
|
||||
const props = defineProps<RekaProps & { class?: HTMLAttributes['class'] }>()
|
||||
const emits = defineEmits<RekaEmits>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _class, ...delegated } = props
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
```
|
||||
|
||||
- 从转发对象中排除视觉包装专属 Props。
|
||||
- 通过 `cn(baseClass, props.class)` 合并调用方 class。
|
||||
- variants 较少且稳定时使用 CVA;动态几何或连续数值使用 computed/CSS Variable。
|
||||
- 保留清晰的 `Ui<Component>` 命名和目录级 `index.ts` 导出。
|
||||
|
||||
### 6.5 迁移业务调用
|
||||
|
||||
- 将旧组件 API 映射到新组件的受控状态和事件。
|
||||
- 允许调整页面或业务组件,使其符合 Reka 的组合方式。
|
||||
- 每次迁移一个可验证的业务切片,旧实现只在调用点迁完后删除。
|
||||
- 不在通用组件中加入仅为兼容单个旧页面的隐式逻辑。
|
||||
|
||||
## 7. 关键实现规则
|
||||
|
||||
### 7.1 Portal、Teleport 与层级
|
||||
|
||||
Reka 的 Select、Tooltip、DropdownMenu、Popover 等通常经过 Portal 和 Popper 包装层。Vue scoped CSS 可能只落到包装节点,而没有落到实际 Content 节点。
|
||||
|
||||
- 对 Portal Content 使用全局唯一的 BEM class,并采用非 scoped 样式或 `:global()`。
|
||||
- 同时给 Content 和 `[data-reka-popper-content-wrapper]:has(> .<content-class>)` 设置明确层级。
|
||||
- 配置 `collisionPadding`,验证视口四边的避让。
|
||||
- 不能只看 DOM 中存在 listbox/tooltip;必须检查背景、边框、尺寸和最上层命中元素。
|
||||
|
||||
推荐层级:普通浮层 80,Tooltip 90,Dialog/Overlay 按页面统一规划,禁止无上限叠加 z-index。
|
||||
|
||||
### 7.2 Slider 坐标一致性
|
||||
|
||||
`SliderRoot` 的宽度就是 Reka 计算 Range 和 Thumb 的坐标系。数值标签、单位和标题不得放进这个坐标容器并参与其宽度。
|
||||
|
||||
```text
|
||||
.slider-shell
|
||||
├── SliderRoot.slider-control
|
||||
│ ├── SliderTrack
|
||||
│ │ └── SliderRange
|
||||
│ └── SliderThumb
|
||||
└── .slider-value
|
||||
```
|
||||
|
||||
验收时检查 `rangeWidth / trackWidth` 是否等于当前百分比,并确认 Thumb 中心与 Range 终点一致。
|
||||
|
||||
### 7.3 Tabs 动态标记
|
||||
|
||||
- 标记归 TabsList 所有,不绑定在某个 Trigger 的伪元素上。
|
||||
- 根据 active Trigger 与 List 的实际矩形计算中心位置。
|
||||
- 监听 `data-state`、容器 Resize 和字体加载完成事件。
|
||||
- 初次定位不播放位移动画,后续选择变化才动画。
|
||||
- 在 `prefers-reduced-motion: reduce` 下移除过渡。
|
||||
|
||||
### 7.4 Dialog 标题动效
|
||||
|
||||
- 焦点态使用 design 提供的完整帧序列,不用单个渐变假装动画。
|
||||
- 使用确定的数字顺序加载帧,避免字符串排序造成 `10` 排在 `2` 前。
|
||||
- 仅在 focused 状态推进和展示动效;unfocused 状态使用静态标题栏。
|
||||
- 组件卸载时清理计时器,reduced-motion 下隐藏或静态化动效。
|
||||
|
||||
### 7.5 NumberField 扩展操作
|
||||
|
||||
- 基础加减由 Reka NumberField 处理。
|
||||
- 快速步进作为包装层显式按钮,遵守 min/max 边界并发出标准 `update:modelValue`。
|
||||
- 所有图形按钮必须有可读的 `aria-label`。
|
||||
|
||||
### 7.6 页面滚动契约
|
||||
|
||||
- 从应用根到页面滚动容器的 Flex/Grid 链路必须包含 `min-height: 0`。
|
||||
- 页面只能有一个主要纵向滚动所有者。
|
||||
- 若 `html/body` 被旧应用设为 `overflow:hidden`,路由视图容器必须明确 `height:100%`、`min-height:0` 和 `overflow-y:auto`。
|
||||
- Portal 不得渲染到会裁剪内容的局部 `overflow:hidden` 容器。
|
||||
|
||||
## 8. 响应式标准
|
||||
|
||||
区分“必须像素对齐的组件原子”和“必须流式变化的页面布局”:
|
||||
|
||||
- 保留 px:1px 边框、位图原始尺寸、30px 控件高度、像素箭头、帧动画和精细纹理。
|
||||
- 使用流式单位:页面 gutter、列宽、面板间距、内容最大宽度和字号层级。
|
||||
- 推荐 `clamp()`、`minmax(0, 1fr)`、`auto-fit`、Flex wrap 和少量语义断点。
|
||||
- 宽表格和完整状态矩阵允许组件内部横向滚动,但页面本身不得产生横向溢出。
|
||||
- 至少验证 1920、1280、768、390 CSS px;同时检查 200% 缩放与键盘访问。
|
||||
|
||||
不要为了“去除 px”而破坏游戏素材的像素精度。响应式的重点是容器、组合与内容流,而不是把所有物理尺寸换成 rem。
|
||||
|
||||
## 9. 组件目录页要求
|
||||
|
||||
每引入一个组件,都必须同步加入 `/components` 或等价目录页,并展示:
|
||||
|
||||
- 默认、hover、pressed、focus、disabled 等状态。
|
||||
- 受控值变化和可见的结果反馈。
|
||||
- 键盘操作、Escape、焦点恢复和 Portal 展示。
|
||||
- 主题切换后的同一状态。
|
||||
- 窄屏组合和必要的内部滚动。
|
||||
- 真实业务素材,而非临时占位图。
|
||||
|
||||
目录页既是文档,也是运行态回归面。静态状态展示不能替代真实可交互实例。
|
||||
|
||||
## 10. 验证流程
|
||||
|
||||
### 10.1 静态检查
|
||||
|
||||
```sh
|
||||
pnpm run format:check
|
||||
pnpm run lint
|
||||
pnpm run type-check
|
||||
pnpm run build-only
|
||||
git diff --check
|
||||
```
|
||||
|
||||
### 10.2 浏览器检查
|
||||
|
||||
逐个组件验证:
|
||||
|
||||
1. 默认视觉和 design 对比。
|
||||
2. 鼠标 hover/pressed 和键盘 focus-visible。
|
||||
3. v-model、禁用态和边界值。
|
||||
4. Select、Tooltip、DropdownMenu、Dialog 的层级、避让和关闭行为。
|
||||
5. Slider、Tabs 等动态几何的实际端点。
|
||||
6. 页面顶部到底部可滚动,且无意外横向滚动。
|
||||
7. 主题切换后组件无需修改即可工作。
|
||||
8. 浏览器控制台无新增 error/warning。
|
||||
|
||||
构建通过不是浏览器交互通过。涉及滚动、焦点、Portal、拖拽、动效或响应式的问题必须保留运行态证据。
|
||||
|
||||
## 11. 完成定义
|
||||
|
||||
一个组件只有同时满足以下条件才算迁移完成:
|
||||
|
||||
- 旧调用点所需功能已覆盖,业务链路可完成。
|
||||
- design 的结构、状态、动效和素材已经逐项核对。
|
||||
- Reka 的键盘、ARIA、焦点和受控状态没有被视觉包装破坏。
|
||||
- 组件只消费语义 Token,并通过至少两个主题验证。
|
||||
- 组件目录页包含真实交互和状态矩阵。
|
||||
- 桌面、平板、手机宽度无页面级溢出。
|
||||
- format、lint、type-check、build 和浏览器 QA 均通过。
|
||||
- 尚未迁移或有意保留的差异在 QA 文档中明确记录。
|
||||
@@ -0,0 +1,6 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.PNG' {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<!doctype html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Expires" content="0" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<meta http-equiv="Cache-control" content="no-cache" />
|
||||
<meta http-equiv="Cache" content="no-cache" />
|
||||
<meta name="baidu-site-verification" content="codeva-hGziQg28WN" />
|
||||
<title>DNF计算器</title>
|
||||
<script
|
||||
defer
|
||||
src="https://umami.dnftools.com/script.js"
|
||||
data-website-id="f30ce285-0a71-4992-b26c-894f0eab34ac"
|
||||
></script>
|
||||
<script>
|
||||
var _hmt = _hmt || []
|
||||
;(function () {
|
||||
var hm = document.createElement('script')
|
||||
hm.src = 'https://hm.baidu.com/hm.js?472775d2edd6fb31b00828eef60ba388'
|
||||
var s = document.getElementsByTagName('script')[0]
|
||||
s.parentNode.insertBefore(hm, s)
|
||||
})()
|
||||
</script>
|
||||
<!-- <script async src="https://www.googletagmanager.com/gtag/js?id=G-J0NPDEYQJS"></script>
|
||||
<script>
|
||||
// 百度统计
|
||||
// eslint-disable-next-line vars-on-top, no-var
|
||||
var _hmt = _hmt || [];
|
||||
(function() {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?472775d2edd6fb31b00828eef60ba388";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
// Google 分析
|
||||
window.dataLayer = window.dataLayer || []
|
||||
function gtag(...args) {
|
||||
window.dataLayer.push(args)
|
||||
}
|
||||
gtag('js', new Date())
|
||||
gtag('config', 'G-J0NPDEYQJS')
|
||||
</script> -->
|
||||
</head>
|
||||
<body>
|
||||
<!-- <script>
|
||||
window.flag
|
||||
= navigator.userAgent.match(/(phone|pad|pod|iphone|ipod|ios|ipad|android|mobile|blackberry|iemobile|mqqbrowser|juc|fennec|wosbrowser|browserng|webos|symbian|windows phone)/i)
|
||||
</script> -->
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "frontend-ui",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "run-p type-check build-only",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --build",
|
||||
"check": "run-s format:check lint type-check",
|
||||
"lint": "oxlint --deny-warnings .",
|
||||
"lint:fix": "oxlint --fix .",
|
||||
"format": "oxfmt --write .",
|
||||
"format:check": "oxfmt --check ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@vueuse/core": "^14.4.0",
|
||||
"axios": "^1.8.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"hoci": "^0.6.0",
|
||||
"pako": "^2.1.0",
|
||||
"pinia": "^4.0.2",
|
||||
"query-string": "^9.1.1",
|
||||
"reka-ui": "^2.10.1",
|
||||
"tailwind-merge": "^3.6.0",
|
||||
"vue": "^3.5.40",
|
||||
"vue-router": "^5.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@tsconfig/node24": "^24.0.4",
|
||||
"@types/node": "^26.1.2",
|
||||
"@types/pako": "^2.0.3",
|
||||
"@vitejs/plugin-vue": "^6.0.8",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.6",
|
||||
"@vue/tsconfig": "^0.9.1",
|
||||
"jiti": "^2.7.0",
|
||||
"npm-run-all2": "^9.0.3",
|
||||
"oxfmt": "^0.62.0",
|
||||
"oxlint": "^1.77.0",
|
||||
"sass": "^1.102.0",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"typescript": "~6.0.3",
|
||||
"unplugin-auto-import": "^21.1.0",
|
||||
"unplugin-vue-components": "^32.1.0",
|
||||
"vite": "^8.2.0",
|
||||
"vue-tsc": "^3.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.12.0"
|
||||
},
|
||||
"packageManager": "pnpm@10.25.0"
|
||||
}
|
||||
|
After Width: | Height: | Size: 264 KiB |
@@ -0,0 +1,12 @@
|
||||
import type { IConfig } from '@/stores'
|
||||
import { defineRequest } from '../common'
|
||||
import type { IResult } from './type'
|
||||
export default defineRequest((request) => {
|
||||
return {
|
||||
async calc(config: IConfig) {
|
||||
const res = await request.post<IResult>('/calc', config)
|
||||
return res.data
|
||||
},
|
||||
}
|
||||
})
|
||||
// Compare this snippet from frontend/src/api/common/index.ts:
|
||||
@@ -0,0 +1,56 @@
|
||||
export interface IResult {
|
||||
uuid: string
|
||||
skills: IResultSkill[]
|
||||
info: IResultUserInfo[]
|
||||
suits: {
|
||||
suit?: IResultSuit
|
||||
oath?: IResultSuit
|
||||
correction?: number
|
||||
}
|
||||
buffer: boolean
|
||||
}
|
||||
|
||||
export interface IResultSkill {
|
||||
name: string
|
||||
icon: string
|
||||
lv: number
|
||||
data: number
|
||||
ratio: number
|
||||
cd: number
|
||||
damage: number
|
||||
mode: string
|
||||
buff: number[]
|
||||
learnLv: number
|
||||
type: string
|
||||
}
|
||||
|
||||
export interface IResultUserInfo {
|
||||
id: number
|
||||
name: string
|
||||
value: string | number
|
||||
}
|
||||
|
||||
export interface IResultSuit {
|
||||
id: string
|
||||
count: number
|
||||
imageUrl: string
|
||||
level: number
|
||||
name: string
|
||||
point: number
|
||||
rarity: string
|
||||
value: string
|
||||
suitId: string
|
||||
skills: {
|
||||
id: string
|
||||
skillId: string
|
||||
name: string
|
||||
imageUrl: string
|
||||
enable: boolean
|
||||
}[]
|
||||
}
|
||||
|
||||
export interface IResultSkillCount {
|
||||
count: number
|
||||
mode: string
|
||||
name: string
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import type { AxiosInstance, AxiosResponse } from 'axios'
|
||||
import axios from 'axios'
|
||||
import { h } from 'vue'
|
||||
import { ungzip } from 'pako'
|
||||
import { useInfoStore } from '@/stores'
|
||||
import { useDialog } from '@/components/hooks/dialog'
|
||||
// import { useAppStore, useConfigStore } from '@/store'
|
||||
// import { useDialog } from '@/components/hooks/dialog'
|
||||
|
||||
export interface HttpResponse<T = unknown> extends AxiosResponse {
|
||||
code?: number
|
||||
message: string
|
||||
data: T
|
||||
state?: number
|
||||
}
|
||||
|
||||
let instance: AxiosInstance | null = null
|
||||
|
||||
let baseURL = '/api'
|
||||
|
||||
// 避免远程开发的时候冲突
|
||||
if (process.env.NODE_ENV == 'development') {
|
||||
baseURL = '/api'
|
||||
}
|
||||
|
||||
if (import.meta.env.MODE == 'web' || import.meta.env.MODE == 'show') {
|
||||
baseURL = 'https://api.dnftools.com/'
|
||||
}
|
||||
|
||||
if (import.meta.env.MODE == 'test') {
|
||||
baseURL = 'https://testapi.dnftools.com/'
|
||||
}
|
||||
|
||||
// 解压
|
||||
function unzip(key: string) {
|
||||
// 将二进制字符串转换为字符数组
|
||||
|
||||
const charData = key.split('').map((x) => {
|
||||
return x?.charCodeAt(0)
|
||||
})
|
||||
|
||||
// 将数字数组转换成字节数组
|
||||
const binData = new Uint8Array(charData)
|
||||
|
||||
// 解压
|
||||
const data = ungzip(binData)
|
||||
|
||||
const decoder = new TextDecoder()
|
||||
const str = decoder.decode(data)
|
||||
|
||||
// unescape(str) --->解压后解码,防止中午乱码
|
||||
return str
|
||||
}
|
||||
|
||||
export function defineRequest<T>(fn: (ax: AxiosInstance) => T) {
|
||||
if (!instance) {
|
||||
instance = axios.create({
|
||||
baseURL,
|
||||
})
|
||||
|
||||
instance.interceptors.request.use(
|
||||
(request) => {
|
||||
// const configStore = useConfigStore()
|
||||
// const token = configStore.token
|
||||
// const uid = useAppStore().uid
|
||||
|
||||
const token = useInfoStore().alter_token
|
||||
|
||||
if (token) {
|
||||
request.headers['Alter-Token'] = token
|
||||
}
|
||||
|
||||
return request
|
||||
},
|
||||
(error) => {
|
||||
// do something
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
instance.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'
|
||||
// instance.defaults.headers["Accept-Encoding"] = "gzip"
|
||||
// instance.defaults.headers["Content-Encoding"] = "gzip"
|
||||
// add response interceptors
|
||||
instance.interceptors.response.use(
|
||||
async (response: AxiosResponse<HttpResponse>) => {
|
||||
if (response.data.code === 500) {
|
||||
const { alert } = useDialog()
|
||||
await alert({
|
||||
content: () => {
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
class: 'justify-center text-#d4d6b6 text-center',
|
||||
style: 'white-space:pre-wrap;width:250px;margin:10px',
|
||||
},
|
||||
`出现错误,可加群反馈:183439472\n${response.data.message}`,
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
// 解密数据
|
||||
if (typeof response.data.data == 'string') {
|
||||
try {
|
||||
const data = unzip(atob(response.data.data as string))
|
||||
response.data.data = JSON.parse(data)
|
||||
} catch {}
|
||||
}
|
||||
return response.data
|
||||
},
|
||||
async (error: Error) => {
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return fn(Object.create(instance))
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import info from './info'
|
||||
import calc from './calc'
|
||||
|
||||
export default { ...info, ...calc }
|
||||
@@ -0,0 +1,48 @@
|
||||
import { defineRequest } from '../common'
|
||||
import type { IAdventureInfo, IAlterInfo, ICharacterInfo, IHelperInfo, ISkillInfo } from './type'
|
||||
|
||||
export default defineRequest((request) => {
|
||||
return {
|
||||
async adventures() {
|
||||
const res = await request.get<IAdventureInfo[]>('/adventure')
|
||||
return res.data
|
||||
},
|
||||
async alterByName(name: string) {
|
||||
const res = await request.get<IAlterInfo>('/adventure', { params: { name } })
|
||||
return res.data
|
||||
},
|
||||
async createToken(alter: string, equVersion?: string) {
|
||||
const res = await request.get<string>(`/token/get/${alter}`, { params: { equVersion } })
|
||||
return res.data
|
||||
},
|
||||
async characterInfo() {
|
||||
const res = await request.get<ICharacterInfo>('/character')
|
||||
return res.data
|
||||
},
|
||||
async skillDetail(skillId: string, level: number) {
|
||||
const res = await request.get<ISkillInfo>(`/skill/${skillId}/${level}`)
|
||||
return res.data
|
||||
},
|
||||
async getConfigFromHelper(uid: string) {
|
||||
const res = await request.get(`/dnfhelper/${uid}/`)
|
||||
return res.data
|
||||
},
|
||||
async getConfigFromHelperAlter(uid: string, alter: string) {
|
||||
const res = await request.get(`/dnfhelper/${uid}/${alter}/`)
|
||||
return res.data
|
||||
},
|
||||
async getSkillFromCode(code: string) {
|
||||
const res = await request.post(`/skillTree/`, { code })
|
||||
return res.data
|
||||
},
|
||||
async getOathInfo(suitId: string, skillId: string, rarity: string, lv: number) {
|
||||
const res = await request.get(`/info/oath/${suitId}/${skillId}`, { params: { rarity, lv } })
|
||||
return res.data
|
||||
},
|
||||
async getHelperList(id: string) {
|
||||
const res = await request.get<IHelperInfo>(`/dnfhelper/${id}/list`)
|
||||
await new Promise((resolve) => setTimeout(resolve, 30000))
|
||||
return res.data
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,232 @@
|
||||
export interface IAdventureInfo {
|
||||
title: string
|
||||
name: string
|
||||
children: IAlterInfo[]
|
||||
}
|
||||
export interface IAlterOption {
|
||||
name: string
|
||||
title: string
|
||||
value?: string
|
||||
}
|
||||
|
||||
export interface IAlterInfo {
|
||||
name: string
|
||||
url?: string
|
||||
title: string
|
||||
default_value: string
|
||||
options?: IAlterOption[]
|
||||
open?: boolean
|
||||
comment?: string
|
||||
value?: string
|
||||
}
|
||||
|
||||
export interface ICharacterInfo {
|
||||
role: string
|
||||
alter: string
|
||||
name: string
|
||||
buffer: boolean
|
||||
equVersion: string
|
||||
equips: IEquipment[]
|
||||
properties: string[]
|
||||
stones: IEquipment[]
|
||||
enchants: IEnchantingInfo[]
|
||||
emblems: IEmblemInfo[]
|
||||
oaths: IEquipment[]
|
||||
weapons: string[]
|
||||
subweapons: string[]
|
||||
skills: ISkill[]
|
||||
suits: ISuit[]
|
||||
avatar: Record<string, IAvatarInfo[]>
|
||||
jades: IJade[]
|
||||
sundry: Record<string, ISundry>
|
||||
options: { id: number; name: string; options: string[] }[]
|
||||
oaths_skills: Record<
|
||||
string,
|
||||
{
|
||||
suitInfo: {
|
||||
name: string
|
||||
rarity: string
|
||||
SkillAttack: number
|
||||
Buffer: number
|
||||
Attack: number
|
||||
}[]
|
||||
skills: { skillId: string; name: string; icon: string }[]
|
||||
}
|
||||
>
|
||||
}
|
||||
|
||||
export interface ISundry {
|
||||
id: number
|
||||
options: {
|
||||
name: string
|
||||
value: number
|
||||
}[]
|
||||
}
|
||||
|
||||
export interface IEquipment {
|
||||
id: string
|
||||
imageUrl: string
|
||||
itemType: string
|
||||
itemDetailType: string
|
||||
name: string
|
||||
categorize: string
|
||||
suit: string[]
|
||||
rarity: string
|
||||
max_adaptation: number
|
||||
STR: number[]
|
||||
INT: number[]
|
||||
Vitality: number[]
|
||||
Spirit: number[]
|
||||
AtkP: number[]
|
||||
AtkM: number[]
|
||||
AtkI: number[]
|
||||
SkillAttack: number[]
|
||||
Attack: number[]
|
||||
Buffer: number[]
|
||||
detail: string
|
||||
bufferDetail: string
|
||||
position: string[]
|
||||
wearUrl: string[]
|
||||
oathPos: string
|
||||
displayUrl?: string
|
||||
}
|
||||
|
||||
export interface ISkill {
|
||||
icon: string
|
||||
name: string
|
||||
id: number
|
||||
learnLv: number
|
||||
position: number
|
||||
line: number
|
||||
type: string
|
||||
maxLearnLv: number
|
||||
maxLv: number
|
||||
bind: boolean
|
||||
hasVP: boolean
|
||||
hasUP: boolean
|
||||
vps: {
|
||||
name: string
|
||||
desc: string
|
||||
}[]
|
||||
uuid: string
|
||||
upType: 'damage' | 'heal'
|
||||
}
|
||||
|
||||
export interface ISuit {
|
||||
id: number
|
||||
name: string
|
||||
imageUrl: string
|
||||
suitId: number
|
||||
suitName: string
|
||||
point: number
|
||||
rarity: string
|
||||
level: number
|
||||
SkillAttack: number
|
||||
Buffer: number
|
||||
value: string
|
||||
bufferValue: string
|
||||
}
|
||||
|
||||
export interface IEnchantingInfo {
|
||||
id: string | number
|
||||
fame: number | undefined
|
||||
position: string[]
|
||||
detail: string
|
||||
categorize: string[]
|
||||
rarity?: string
|
||||
type?: string
|
||||
}
|
||||
|
||||
export interface IEmblemInfo {
|
||||
fame: number
|
||||
id: string
|
||||
rarity: string
|
||||
type: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface IAvatarInfo {
|
||||
id: number
|
||||
options: string[]
|
||||
part: string
|
||||
rarity: string
|
||||
suit?: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface IJade {
|
||||
name: string
|
||||
id: number
|
||||
max: number
|
||||
min: number
|
||||
pre: number
|
||||
unit: string
|
||||
}
|
||||
|
||||
export interface ISkillInfo {
|
||||
name: string
|
||||
type: string
|
||||
desc: string
|
||||
descDetail: string
|
||||
consumeItem: ConsumeItem
|
||||
descSpecial: string[]
|
||||
maxLevel: number
|
||||
requiredLevel: number
|
||||
requiredLevelRange: number
|
||||
preRequiredSkill: any
|
||||
jobId: string
|
||||
jobName: string
|
||||
jobGrowLevel: any[]
|
||||
evolution: Evolution[]
|
||||
enhancement: Enhancement[]
|
||||
attribute: Attribute
|
||||
}
|
||||
|
||||
export interface ConsumeItem {
|
||||
itemId: string
|
||||
itemName: string
|
||||
value: number
|
||||
}
|
||||
|
||||
export interface Evolution {
|
||||
type: number
|
||||
name: string
|
||||
desc: string
|
||||
descDetail: string
|
||||
skills?: string[]
|
||||
}
|
||||
|
||||
export interface Enhancement {
|
||||
type: number
|
||||
status: Status[]
|
||||
}
|
||||
|
||||
export interface Status {
|
||||
name: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface Attribute {
|
||||
optionDesc: string
|
||||
level: number
|
||||
consumeMp: number
|
||||
coolTime: number
|
||||
castingTime?: number
|
||||
optionValue: Record<`value${string}`, string>
|
||||
detail: string
|
||||
}
|
||||
|
||||
export interface IHelperRoleInfo {
|
||||
roleId: string
|
||||
roleName: string
|
||||
roleJob: string
|
||||
avatarLink: string
|
||||
fame: number
|
||||
}
|
||||
|
||||
export interface IHelperInfo {
|
||||
id: string
|
||||
avatar: string
|
||||
name: string
|
||||
roleList: IHelperRoleInfo[]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface IDataType<T = any> {
|
||||
code?: number
|
||||
message: string
|
||||
data: T
|
||||
state?: number
|
||||
}
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 928 B |
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 322 B |
|
After Width: | Height: | Size: 355 B |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 711 B |
|
After Width: | Height: | Size: 617 B |
|
After Width: | Height: | Size: 637 B |
|
After Width: | Height: | Size: 595 B |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 324 B |
|
After Width: | Height: | Size: 763 B |
|
After Width: | Height: | Size: 767 B |
|
After Width: | Height: | Size: 335 B |
|
After Width: | Height: | Size: 336 B |
|
After Width: | Height: | Size: 336 B |
|
After Width: | Height: | Size: 323 B |
|
After Width: | Height: | Size: 326 B |
|
After Width: | Height: | Size: 325 B |
|
After Width: | Height: | Size: 325 B |
|
After Width: | Height: | Size: 312 B |
|
After Width: | Height: | Size: 410 B |
|
After Width: | Height: | Size: 411 B |
|
After Width: | Height: | Size: 412 B |
|
After Width: | Height: | Size: 386 B |
|
After Width: | Height: | Size: 391 B |
|
After Width: | Height: | Size: 369 B |
|
After Width: | Height: | Size: 590 B |
|
After Width: | Height: | Size: 395 B |
|
After Width: | Height: | Size: 536 B |
|
After Width: | Height: | Size: 737 B |
|
After Width: | Height: | Size: 433 B |
|
After Width: | Height: | Size: 475 B |
|
After Width: | Height: | Size: 471 B |
|
After Width: | Height: | Size: 373 B |
|
After Width: | Height: | Size: 763 B |
|
After Width: | Height: | Size: 767 B |
|
After Width: | Height: | Size: 759 B |
|
After Width: | Height: | Size: 756 B |
|
After Width: | Height: | Size: 201 B |
|
After Width: | Height: | Size: 200 B |
|
After Width: | Height: | Size: 200 B |
|
After Width: | Height: | Size: 196 B |
|
After Width: | Height: | Size: 242 B |
|
After Width: | Height: | Size: 195 B |
|
After Width: | Height: | Size: 245 B |
|
After Width: | Height: | Size: 243 B |
|
After Width: | Height: | Size: 243 B |
|
After Width: | Height: | Size: 230 B |
|
After Width: | Height: | Size: 726 B |
|
After Width: | Height: | Size: 333 B |
|
After Width: | Height: | Size: 317 B |
|
After Width: | Height: | Size: 317 B |
|
After Width: | Height: | Size: 304 B |
|
After Width: | Height: | Size: 803 B |
|
After Width: | Height: | Size: 782 B |
|
After Width: | Height: | Size: 944 B |
|
After Width: | Height: | Size: 933 B |
|
After Width: | Height: | Size: 905 B |
|
After Width: | Height: | Size: 934 B |
|
After Width: | Height: | Size: 205 B |
|
After Width: | Height: | Size: 398 B |
|
After Width: | Height: | Size: 393 B |
|
After Width: | Height: | Size: 362 B |
|
After Width: | Height: | Size: 402 B |
|
After Width: | Height: | Size: 816 B |
@@ -0,0 +1,39 @@
|
||||
<svg width="22" height="20" viewBox="0 0 22 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_i_135_176)">
|
||||
<rect x="1" y="1" width="20" height="18" fill="url(#paint0_linear_135_176)"/>
|
||||
</g>
|
||||
<rect x="0.5" y="0.5" width="21" height="19" stroke="#49453F"/>
|
||||
<g filter="url(#filter1_d_135_176)">
|
||||
<path d="M11.1514 14.3975V12H6V11H16L11.1514 14.3975ZM10.8486 8.39746H16V9.39746H6L10.8486 6V8.39746Z" fill="url(#paint1_linear_135_176)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_i_135_176" x="0" y="0" width="22" height="20" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_135_176"/>
|
||||
</filter>
|
||||
<filter id="filter1_d_135_176" x="2" y="2" width="18" height="16.3975" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.44 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_135_176"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_135_176" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_135_176" x1="11" y1="1" x2="11" y2="19" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0D3969"/>
|
||||
<stop offset="1" stop-color="#0A2244"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_135_176" x1="11" y1="6" x2="11" y2="14.3975" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D6B17D"/>
|
||||
<stop offset="1" stop-color="#96763A"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 815 B |
@@ -0,0 +1,39 @@
|
||||
<svg width="22" height="18" viewBox="0 0 22 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_i_135_173)">
|
||||
<rect x="1" y="1" width="20" height="16" fill="url(#paint0_linear_135_173)"/>
|
||||
</g>
|
||||
<rect x="0.5" y="0.5" width="21" height="17" stroke="#49453F"/>
|
||||
<g filter="url(#filter1_d_135_173)">
|
||||
<path d="M11.1514 12.3975V10H6V9H16L11.1514 12.3975ZM10.8486 6.39746H16V7.39746H6L10.8486 4V6.39746Z" fill="url(#paint1_linear_135_173)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_i_135_173" x="0" y="0" width="22" height="18" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_135_173"/>
|
||||
</filter>
|
||||
<filter id="filter1_d_135_173" x="2" y="0" width="18" height="16.3975" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.44 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_135_173"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_135_173" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_135_173" x1="11" y1="1" x2="11" y2="17" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#306EB3"/>
|
||||
<stop offset="1" stop-color="#0A2244"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_135_173" x1="11" y1="4" x2="11" y2="12.3975" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D6B17D"/>
|
||||
<stop offset="1" stop-color="#96763A"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 731 B |
@@ -0,0 +1,39 @@
|
||||
<svg width="22" height="20" viewBox="0 0 22 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_i_135_172)">
|
||||
<rect x="1" y="1" width="20" height="18" fill="url(#paint0_linear_135_172)"/>
|
||||
</g>
|
||||
<rect x="0.5" y="0.5" width="21" height="19" stroke="#49453F"/>
|
||||
<g filter="url(#filter1_d_135_172)">
|
||||
<path d="M11.1514 14.3975V12H6V11H16L11.1514 14.3975ZM10.8486 8.39746H16V9.39746H6L10.8486 6V8.39746Z" fill="url(#paint1_linear_135_172)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_i_135_172" x="0" y="0" width="22" height="20" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_135_172"/>
|
||||
</filter>
|
||||
<filter id="filter1_d_135_172" x="2" y="2" width="18" height="16.3975" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.44 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_135_172"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_135_172" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_135_172" x1="11" y1="1" x2="11" y2="19" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#626262"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_135_172" x1="11" y1="6" x2="11" y2="14.3975" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D1D1D1"/>
|
||||
<stop offset="1" stop-color="#989898"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 905 B |