diff --git a/frontend-pc/.agents/README.md b/frontend-pc/.agents/README.md index 65b0e12..858fd4a 100644 --- a/frontend-pc/.agents/README.md +++ b/frontend-pc/.agents/README.md @@ -2,20 +2,25 @@ 本目录为 AI 编码助手规范的**唯一来源**(Single Source of Truth),适用于 Cursor、OpenCode、Codex 等工具。 +> ⚠️ **2026-10 已整体重写**:PC 端从 **Gi Admin Pro + Arco Design Vue** 换成 **纯 Vue 3 + Naive UI**。 +> 上一版 skills 里的 Arco 组件名(`a-table` / `a-form` / `a-modal`)、`v-hasPerm`、auto-import、 +> Stylelint、Gi 的 `useTable/usePagination` **全部不再适用** —— 照着写会编译不过或静默失效。 +> 唯一保留原样的是 `git-commit`(与框架无关)。 + ## 结构 ``` .agents/ ├── rules/ -│ └── agent.md # 唯一常驻规则(Cursor 经 AGENTS.md 内联注入) +│ └── agent.md # 唯一常驻规则(AGENTS.md 内联注入) └── skills/ # 按需加载 - ├── code-ts/SKILL.md - ├── code-vue/SKILL.md - ├── code-css/SKILL.md - ├── project-guide/SKILL.md - ├── project-crud/SKILL.md - ├── project-api/SKILL.md - └── git-commit/SKILL.md + ├── code-ts/SKILL.md # TS 命名 / 导入 / 显式 import + ├── code-vue/SKILL.md # SFC 写法与模板约定 + ├── code-css/SKILL.md # SCSS 与样式边界 + ├── project-guide/SKILL.md # 技术栈 / 目录 / 七条最容易写错的约定 + ├── project-crud/SKILL.md # 列表 + 表单的标准写法 + ├── project-api/SKILL.md # 接口调用(没有 Mock) + └── git-commit/SKILL.md # 提交规范(与框架无关) ``` ## 工具适配 @@ -23,7 +28,5 @@ | 工具 | 适配方式 | |------|----------| | Cursor | `AGENTS.md`(内联 behavior 规则 + 技能索引)+ `.agents/skills/`(按需) | -| OpenCode | `AGENTS.md` + `opencode.json` + `.agents/rules/` + `.agents/skills/` | +| OpenCode | `AGENTS.md` + `.agents/rules/` + `.agents/skills/` | | Codex | `AGENTS.md` + `.agents/skills/` | - -修改 `agent.md` 后请同步 `AGENTS.md` 的「常驻规则」一节;编码规范改对应 skill,不要重复写入 `AGENTS.md`。 diff --git a/frontend-pc/.agents/rules/agent.md b/frontend-pc/.agents/rules/agent.md index 01550ae..19fe9d6 100644 --- a/frontend-pc/.agents/rules/agent.md +++ b/frontend-pc/.agents/rules/agent.md @@ -1,6 +1,6 @@ # Agent 行为准则 -> 适用于 Cursor、OpenCode、Codex 等 AI 编码助手。Cursor 经 `AGENTS.md` 内联本节每轮注入。 +> 适用于 Cursor、OpenCode、Codex 等 AI 编码助手。AGENTS.md 内联本节每轮注入。 ## 通用要求 @@ -11,26 +11,27 @@ ## 应当 -- 改代码前先阅读项目既有实现,保持风格一致。 -- 优先复用项目内已有组件、composables、工具函数与 API 封装。 -- 任务完成后,视情况运行 lint / typecheck / test(与改动相关时)。 -- **按需读取** skills,不要预加载全部;写代码前按任务类型加载对应 skill(见 `AGENTS.md` 技能索引)。 +- 改代码前先阅读项目既有实现(`src/views/` 里同类页面就是最好的模板),保持风格一致。 +- 优先复用已有组件、composables(`src/composables/`)、工具函数(`src/utils/`)与 API 封装(`src/apis/xt/`)。 +- 任务完成后运行 `pnpm typecheck`(**当前 0 错,有报错就是新引入的**)与 `pnpm lint`。 +- **按需读取** skills,不要预加载全部。 ## 不应 -- 为已自动导入的 API / 组件写冗余 import。 -- 引入与项目技术栈不符的 UI 库或全新抽象层。 -- 猜测不存在的 API 路径、响应结构或配置项;以项目代码与类型定义为准。 -- 在用户未要求时添加测试、文档或无关注释。 +- ❌ 写 **Arco Design** 的组件与属性(`a-table` / `a-form` / `a-modal` / `arcoblue` …)—— 项目已不用 Arco。 +- ❌ 用 `v-hasPerm` 指令或指望 **auto-import**:本仓库组件与 API **全部显式 import**。 +- ❌ 把 `apis/xt/*.ts` 里映射的 **Arco 色词**直接塞给 `n-tag`(会静默变灰,见 `utils/tag.ts`)。 +- ❌ 猜测不存在的接口路径、响应结构或配置项;以 `src/apis/xt/` 与后端契约为准。 +- ❌ 在用户未要求时添加测试、文档或无关注释。 ## 编码前必读 skill | 任务 | 技能 | |------|------| -| 写/改 `.vue` | `code-vue` | -| 写/改样式 | `code-css` | -| 写/改 `.ts` / composable | `code-ts` | -| 项目约定、Gi 组件 | `project-guide` | -| CRUD 列表/表单 | `project-crud` | -| API / Mock | `project-api` | -| 提交代码 | `git-commit` | +| 写/改 `.vue` | `skills/code-vue/SKILL.md` | +| 写/改样式 | `skills/code-css/SKILL.md` | +| 写/改 `.ts` / composable | `skills/code-ts/SKILL.md` | +| 项目约定、Naive UI 组件与目录 | `skills/project-guide/SKILL.md` | +| CRUD 列表/表单 | `skills/project-crud/SKILL.md` | +| 接口调用 | `skills/project-api/SKILL.md` | +| git commit | `skills/git-commit/SKILL.md` | diff --git a/frontend-pc/.agents/skills/code-css/SKILL.md b/frontend-pc/.agents/skills/code-css/SKILL.md index b9bcffa..3a0ab74 100644 --- a/frontend-pc/.agents/skills/code-css/SKILL.md +++ b/frontend-pc/.agents/skills/code-css/SKILL.md @@ -1,36 +1,112 @@ --- name: code-css -description: Vue SFC 样式块、BEM 命名、Stylelint 与 scoped 约定。编写或修改组件样式(.vue style、scss、less、css)时加载本技能。 +description: SCSS 写法、样式作用域与全局样式的边界。编写或修改 .vue 的 style 块、.scss 文件时加载本技能。 --- -# CSS 规范(Vue 3) +# 样式规范(SCSS + Naive UI) -## SFC 样式块 +> ⚠️ **Stylelint 已随 2026-10 重搭移除**(`pnpm lint:style` 不存在了)。样式只受 ESLint 与人工约定约束。 -- 位于 ` -``` - -## useTable 用法 - -```ts -import type * as T from '@/apis/system/user' -import { baseAPI } from '@/apis/system/user' -import { useDict, useTable } from '@/hooks' - -const { dictData } = useDict(['STATUS'] as const) - -const queryParams = reactive({ status: '', username: '' }) - -const { - loading, - tableData, - pagination, - selectedKeys, - search, - select, - selectAll, - fixed, // 操作列 fixed: 'right'(小屏自动取消) - onDelete, - onBatchDelete, - refresh -} = useTable({ - listAPI: (page) => baseAPI.getList({ ...page, ...queryParams }), - deleteAPI: (ids) => baseAPI.delete({ ids }), - immediate: true, - rowKey: 'id', // 默认 id - crossPageSelect: false // 跨页多选时设为 true -}) -``` - -要点: - -- `search()` 会重置到第 1 页并清空选中 -- `refresh()` 保持当前页刷新 -- `listAPI` 参数为 `{ page, size }`,需展开到接口 -- 空字符串查询条件建议转 `undefined` 再传 API - -## GiButton 预设类型 - -`add` | `edit` | `delete` | `search` | `reset` | `upload` | `import` | `export` - -列表工具栏与操作列统一使用 `GiButton`,操作列常用 `size="mini"`。 - -## 表格列(TSX) - -使用 `lang="tsx"`,从 `@arco-design/web-vue` 引入 `TableColumnData`: - -```tsx -const tableColumns: TableColumnData[] = [ - { - title: '序号', - width: 68, - align: 'center', - render: ({ rowIndex }) => {rowIndex + 1} - }, - { title: '名称', dataIndex: 'name', ellipsis: true, tooltip: true }, - { - title: '状态', - width: 100, - align: 'center', - render: ({ record }) => ( - - ) - }, - { - title: '操作', - width: 160, - align: 'center', - fixed: 'right', // 或绑定 useTable 的 fixed - render: ({ record }) => ( - - onEdit(record as T.ListItem)} /> - onDelete(record as T.ListItem)}> - - - - ) +async function load() { + loading.value = true + try { + const res = await xxxApi.list({ ...query }) + list.value = res.list + total.value = res.total + } finally { + loading.value = false } +} + +const columns: DataTableColumns = [ + { title: '名称', key: 'name' }, + { title: '状态', key: 'status', render: (row) => h(NTag, { type: tagOf(row.status), size: 'small' }, { default: () => STATUS_NAMES[row.status] }) }, + { title: '操作', key: 'actions', render: (row) => h(NSpace, null, { default: () => [ + h(NButton, { size: 'small', onClick: () => openEdit(row) }, { default: () => '编辑' }) + ] }) } ] + +onMounted(load) + + + ``` -常用单元格组件:`GiCellAvatar`、`GiCellGender`、`GiCellStatus`(从 `@/components/GiCell` 引入或在 TSX 中直接使用)。 +### 三个必须记住的点 -## GiPageLayout +1. **远程分页要处理两个事件**:`@update:page` **和** `@update:page-size`, + 且都要**重新取数**。naive 的表格在 `pagination` 非 false 时会自己再切一次数据 + (内部 `flattenData`)—— 只改本地每页条数不重新取数,会出现"分页器说 50 条/页、表里还是 20 条"。 +2. **分页器容器用 `.page-card` + `.page-card__body`**(`styles/index.scss`): + 否则卡片变高会把分页器顶出可视区、怎么滚都看不到(本项目反复踩过)。 +3. **`listPagination()` 而不是手写对象**:手写时 `prefix` 的参数少一个 `?` 就报 TS2322。 -| 属性/插槽 | 说明 | -|-----------|------| -| `#left` | 左侧栏(部门树、字典类型列表等) | -| `#header` | 顶栏(少用) | -| `margin` | 外边距模式 | -| `collapsed` | 显示折叠按钮 | -| `size` | 左侧宽度,如 `"280px"` | +### 版面(2026-10 卡片式基线) -左栏选中后通过事件同步右侧列表,参考 `dict/index.vue` 的 `onDictTypeRefresh`。 +- **卡片外观是全局给的**(白底 + 描边 + 轻阴影 + 卡片头浅灰色带),页面**不要**再自己写边框/阴影; + 卡片之所以"浮"得起来,靠的是 `layout` 那一层的 `--xt-bg` 灰底。 +- 卡片**内部分区**用 `.section-title`(左侧蓝色小竖条)+ 浅灰小格子,**不要卡里套卡**。 +- 工具条用 `.toolbar`(自带下分隔线,把"筛选区"和"表格区"切开)。 +- 详细规则见 [code-css](../code-css/SKILL.md) 的「设计基线」「卡片式的三条规矩」「按钮的可见性」。 -## 表单弹窗(Modal + GiForm) +## 表单弹窗 -```vue - - - -``` +- `NModal` + `NForm` + `NFormItem`,`ref` 拿实例,提交前 `await formRef.value?.validate()`。 +- 校验规则用 `FormRules`,与后端约束对齐(**但服务端才是权威**,前端只减少无效提交)。 +- 成功后 `message.success(...)` + 关弹窗 + `load()`。 +- 危险动作(删除/停用/重置密码)用 `useDialog()` 二次确认,文案写明后果。 -```ts -import type { FormColumnItem } from '@/components/index' -import { useResetReactive } from '@/hooks' +## 权限与展示 -const [form, resetForm] = useResetReactive({ id: '', name: '', status: '1' as Status }) -const GiFormRef = useTemplateRef>('GiFormRef') - -const add = () => { - detailId.value = '' - visible.value = true -} -const edit = async (id: string) => { - visible.value = true - detailId.value = id - Object.assign(form, (await baseAPI.getDetail({ id })).data) -} -const close = () => { - GiFormRef.value?.formRef?.resetFields() - resetForm() -} -const save = async () => { - const valid = await GiFormRef.value?.formRef?.validate() - if (valid) return false - // await baseAPI.add(form) 或 baseAPI.update(form) - emit('save-success') - return true -} - -defineExpose({ add, edit }) -``` - -`formColumns` 常用 `type`:`input`、`select`、`tree-select`、`radio-group`、`switch`、`textarea`。校验可用 `required`、`rules`;正则见 `@/utils/regexp`。 - -## 字典下拉 - -```ts -const { dictData } = useDict(['STATUS'] as const) -// 模板: -``` - -## 表格增强方案选型 - -| 需求 | 方案 | -|------|------| -| 内置斑马纹/全屏/列设置 | `GiTable` | -| 继续用 `a-table` + 工具栏/列设置 | `GiTableSetting` 包裹(见 `views/table/custom3`) | -| 简单列表 | `a-table` + `useTable`(系统模块主流) | - -## 检查清单 - -- [ ] `defineOptions({ name })` 已设置 -- [ ] `row-key="id"` 与 `useTable` 的 `rowKey` 一致 -- [ ] 删除走 `onDelete` / `onBatchDelete` 或自定义 `Popconfirm` + API -- [ ] 弹窗 `defineExpose({ add, edit })`,父组件 `useTemplateRef` 调用 -- [ ] 保存成功后 `emit('save-success')` 并触发 `search` +- 按钮显隐:`computed(() => user.has('xxx:manage'))`;**不要**用 `v-hasPerm`。 +- 状态色:`tagOf(row.status)`;状态文案映射放 `apis/xt/<域>.ts` 里与接口同处。 +- 敏感字段(手机号/证件号)后端只回**脱敏值** → 编辑时**无法回填原文**,要改就得重填(照抄账号页的写法)。 diff --git a/frontend-pc/.agents/skills/project-guide/SKILL.md b/frontend-pc/.agents/skills/project-guide/SKILL.md index c75edd1..df39480 100644 --- a/frontend-pc/.agents/skills/project-guide/SKILL.md +++ b/frontend-pc/.agents/skills/project-guide/SKILL.md @@ -1,171 +1,72 @@ --- name: project-guide -description: 后台管理模板的项目总览与前端强制规范。涵盖 Vue 3 + Arco Design + Gi 组件库、目录结构、自动导入、CRUD/API 约定与代码风格。在当前仓库内开发、新增页面、查阅架构或不确定项目规范时使用。 +description: PC 管理端的技术栈、目录结构、菜单与权限模型,以及七条最容易写错的约定。开始任何 PC 端改动前加载本技能。 --- -# 项目开发指南 - -> **适用范围**:本仓库 `src/**`、`mock/**` -> **规范来源**:`AGENTS.md` 常驻行为准则 + 本 skill(项目细节);编码规范按需读 `code-vue` / `code-css` / `code-ts` skill - -## 本项目 Agent 约定 -- 优先复用 `Gi*`、`hooks`、`getBaseApi`、现有 mock 工具;新增页面/接口对齐参考页与 `getBaseApi` 约定。 -- API / Mock 以 `src/apis`、`mock` 与类型定义为准,不要猜测路径。 -- 涉及 CRUD、API、Mock 时按需读取对应 skill;写 `.vue` / 样式 / TS 前读取 [code-vue](../code-vue/SKILL.md)、[code-css](../code-css/SKILL.md)、[code-ts](../code-ts/SKILL.md)。 -- 改动后视情况运行 `pnpm typecheck`、`pnpm lint`、`pnpm lint:style`。 +# 项目总览(PC 管理端) ## 技术栈 -| 类别 | 选型 | -|------|------| -| 框架 | Vue 3.5 + TypeScript + ` -``` - -```vue - -``` - -```vue - -``` - -```vue - -``` - -#### 写法技巧 - -尽量使用三元表达式 - -```js -// 优化前 -let isEdit = true -let title = '' -if (isEdit) { - title = '编辑' -} else { - title = '新增' -} - -// 优化后 -let title = isEdit ? '编辑' : '新增' -``` - -善用 includes 方法 - -```js -// 优化前 -if (type === 1 || type === 2 || type === 3) { -} - -// 优化后,此种方式在vue模板也可使用 -if ([1, 2, 3].includes(type)) { -} -``` - -使用箭头函数简化函数 - -```js -// 优化前 -function add(num1, num2) { - return num1 + num2 -} - -// 优化后 -const add = (num1, num2) => num1 + num2 -``` - -尽量减少 if else if - -```vue - -``` - -```js -// 优化前 -const status = 200 -const message = '' -if (status === 200) { - message = '请求成功' -} else if (status === 404) { - message = '请求出错' -} else if (status === 500) { - message = '服务器错误' -} - -// 优化后 -const status = 200 -const messageMap = { - 200: '请求成功', - 404: '请求出错', - 500: '服务器错误' -} -const message = messageMap[status] -``` - -如果函数参数超过两个,建议优化 - -```vue - -``` - -#### 接口调用书写 - -**写法一** - -不需要 loading,不需要错误打印的 情况 - -```vue - - -// getUserListApi 是一个 Promise 异步函数,Promise 最后只有 成功 / 失败 两种状态 // getUserListApi 是基于 axios -封装的,在 axios 响应拦截器做了处理 // 当 res.success === false 的时候 Promise.reject(), -也就异步失败,异步失败不会往下执行 -``` - -**写法二** - -需要 loading,需要错误打印的情况 - -```vue - -``` - -**写法三** - -需要 loading, 不需要错误打印(不进行错误处理)的情况 - -```vue - - -// catch 可以省略 -``` - -#### 全局组件--命名规范 - -组件命名:**单文件组件的文件名应该要么始终是单词大写开头 (PascalCase),要么始终是横线连接 (kebab-case)** - -可参考 Vue2 官网-风格指南: https://v2.cn.vuejs.org/v2/style-guide/ - -``` -GiForm.vue -GiTable.vue -GiPageLayout.vue -``` - -#### 局部组件--命名规范 - -组件命名:**单文件组件的文件名应该要么始终是单词大写开头 (PascalCase),要么始终是横线连接 (kebab-case)** - -可参考 Vue2 官网-风格指南: https://v2.cn.vuejs.org/v2/style-guide/ - -``` -Pane1.vue -Pane2.vue -PaneQuota1.vue -PaneQuota2.vue -Step1.vue -Step2.vue -AddModal.vue -EditDrawer.vue -DetailModal.vue -``` - -#### 文件夹命名--命名规范 (采用中划线-) - -1、文件名建议只使用小写字母,不使用大写字母 - -2、名称较长时采用半角连接符(-)分隔 - -``` -home/index.vue -user/index.vue -user-detail/index.vue -``` - -#### 弹窗组件 Modal、抽屉组件 Drawer 的一般封装 - -```vue - - - -``` - -使用 - -**`模板里使用自定义组件:使用大写开头驼峰,双击好复制,对于搜索便利`** - -```vue - - - -``` - -#### CSS 命名规范 - -建议采用全小写,多单词使用-连接符(参考大部分网站,包括掘金,码云等,都是采用这个规则) - -或者采用`BEM`命名规范 [BEM 命名规范](https://getbem.com/naming/) - -**BEM 命名规范** - -```html -
-
- - -
-
-``` - - -其他: - -位置 1: 使用 var(--margin) 全局 css 变量 - -位置 2:使用 var(--padding) 全局 css 变量 - -建议尽量使用全局 css 变量来开发,可以有效提高效率和团队协作 - - - - - -#### 其他规范 - -可参考 Vue2 官网-风格指南: https://v2.cn.vuejs.org/v2/style-guide/ , 其中一些规范也可借鉴 - -可参考 **Gi Admin Pro** 源码,如有更好的规范建议,可以联系作者本人 - -## Vue 相关 - -Vue3 官网 - -Vue-Router - -Vite - -Pinia - -## 插件推荐 - -Arco Design 组件库 - -Day.js 一个极简的 JavaScript 库,可以为现代浏览器解析、验证、操作和显示日期和时间 2K 大小 - -Lodash 一个一致性、模块化、高性能的 JavaScript 实用工具库 - -Xe-utils 一个 JavaScript 函数库、工具类 - -VueUse 一个 Vue3 Hooks 库 - -VueRequest 一个 Vue 请求库 - -V-Viewer 基于 viewer.js 的 vue 图像查看器组件,支持旋转、缩放等操作 - -Vue-Color-Kit 一个 vue3 颜色选择器组件 - -Vxe-Table - -vue3-request 一个专为 Vue 3 设计的异步请求处理库,它通过统一的 useRequest Hook 和强大的插件生态,彻底简化了异步操作和 API 调用的复杂性 - -**其他** - -VCalendar 日历组件 - -Vue Cal 日历组件 - -VueDraggablePlus 支持 Vue2 和 Vue3 的拖拽组件 - -## 推荐书籍 - -Vue 入门指南与实战案例 - -深入理解 TypeScript - -前端宝典 - -Web 开发人员需要知道的 CSS 技巧 - -阮一峰 ES6 - -阮一峰 flex 布局 - -## 开源项目集合 - -Vue3 开源项目集合 - -React 开源项目集合 - -## 工具 - -菜鸟工具 - -代码生成图片 - -JSON-TO-ANY 对象转 ts 类型 - -在线三角形样式生成器 - -iTab - -## 捐赠 - - +1. **没有 auto-import** —— 组件与 API 全部显式 `import`; +2. **没有 `v-hasPerm`** —— 权限判断写 `useUserStore().has('码')`,字符串与后端 `PermissionDef` 逐字一致; +3. **`n-tag` 的 `type` 必须过 `@/utils/tag.ts`** —— Arco 色词直接透传会静默变灰; +4. **图标集升级后重跑** `scripts/extract-pc-icons.py` —— 缺图标是空占位、不报错。 diff --git a/frontend-pc/index.html b/frontend-pc/index.html index 4466c27..7fb4798 100644 --- a/frontend-pc/index.html +++ b/frontend-pc/index.html @@ -1,35 +1,37 @@ - + - - - - - - - - - - 填报通 · 管理端 - - - -
- -
-
-
-
-
-
-
-
-
-
- - + + + + + 深学在线 + + + + +
+
正在加载…
+
+ + + diff --git a/frontend-pc/package.json b/frontend-pc/package.json index 3bb942b..26ee672 100644 --- a/frontend-pc/package.json +++ b/frontend-pc/package.json @@ -1,7 +1,9 @@ { "name": "xt-frontend-pc", "type": "module", - "version": "1.0.0", + "version": "2.0.0", + "private": true, + "description": "深学在线 · PC 管理端(Vue 3 + Naive UI)", "scripts": { "dev": "vite", "build": "vite build", @@ -9,59 +11,30 @@ "preview": "vite preview --port 5050", "typecheck": "vue-tsc --noEmit", "lint": "eslint .", - "lint:fix": "eslint . --fix", - "lint:style": "stylelint \"**/*.{vue,scss,css}\" --cache", - "lint:style:fix": "stylelint \"**/*.{vue,scss,css}\" --cache --fix" + "lint:fix": "eslint . --fix" }, "dependencies": { - "@arco-design/color": "^0.4.0", + "@iconify-json/icon-park-outline": "^1.2.4", "@iconify/vue": "^5.0.0", "@vueuse/core": "^13.6.0", - "animate.css": "^4.1.1", - "axios": "^0.27.2", + "axios": "^1.7.9", "dayjs": "^1.11.4", - "echarts": "^6.0.0", "jsencrypt": "^3.5.4", - "lodash-es": "^4.17.21", - "mitt": "^3.0.1", - "nprogress": "^0.2.0", + "naive-ui": "^2.42.0", "pinia": "^3.0.3", "pinia-plugin-persistedstate": "^4.4.1", - "sortablejs": "^1.15.6", "vue": "^3.5.18", - "vue-color-kit": "^1.0.6", - "vue-draggable-plus": "^0.6.0", - "vue-router": "^4.5.1", - "xe-utils": "^3.7.8" + "vue-router": "^4.5.1" }, "devDependencies": { "@antfu/eslint-config": "^7.0.1", - "@arco-design/web-vue": "^2.58.0", - "@iconify-json/icon-park-outline": "^1.2.4", - "@types/lodash-es": "^4.17.12", "@types/node": "^24.2.0", "@vitejs/plugin-vue": "^6.0.1", - "@vitejs/plugin-vue-jsx": "^5.0.1", "@vue/tsconfig": "^0.7.0", "eslint": "^9.5.0", - "less": "^4.4.0", - "postcss": "^8.5.6", - "postcss-html": "^1.8.0", "sass": "^1.62.1", - "stylelint": "^16.25.0", - "stylelint-config-recess-order": "^7.4.0", - "stylelint-config-recommended-vue": "^1.6.1", - "stylelint-config-standard": "^39.0.1", - "stylelint-config-standard-scss": "^16.0.0", - "stylelint-scss": "^6.12.1", "typescript": "^5.9.2", - "unplugin-auto-import": "^20.0.0", - "unplugin-icons": "^22.5.0", - "unplugin-vue-components": "^29.0.0", "vite": "^7.0.6", - "vite-plugin-compression": "^0.5.1", - "vite-plugin-vue-devtools": "^8.0.5", "vue-tsc": "^3.0.5" - }, - "description": "数据信息填报系统 · PC 管理端(管理员 / 班主任)" + } } diff --git a/frontend-pc/pnpm-lock.yaml b/frontend-pc/pnpm-lock.yaml index 8c67240..94ec0de 100644 --- a/frontend-pc/pnpm-lock.yaml +++ b/frontend-pc/pnpm-lock.yaml @@ -8,142 +8,64 @@ importers: .: dependencies: - '@arco-design/color': - specifier: ^0.4.0 - version: 0.4.0 + '@iconify-json/icon-park-outline': + specifier: ^1.2.4 + version: 1.2.4 '@iconify/vue': specifier: ^5.0.0 version: 5.0.3(vue@3.5.23(typescript@5.9.3)) '@vueuse/core': specifier: ^13.6.0 version: 13.9.0(vue@3.5.23(typescript@5.9.3)) - animate.css: - specifier: ^4.1.1 - version: 4.1.1 axios: - specifier: ^0.27.2 - version: 0.27.2 + specifier: ^1.7.9 + version: 1.20.0 dayjs: specifier: ^1.11.4 version: 1.11.19 - echarts: - specifier: ^6.0.0 - version: 6.1.0 jsencrypt: specifier: ^3.5.4 version: 3.5.4 - lodash-es: - specifier: ^4.17.21 - version: 4.17.21 - mitt: - specifier: ^3.0.1 - version: 3.0.1 - nprogress: - specifier: ^0.2.0 - version: 0.2.0 + naive-ui: + specifier: ^2.42.0 + version: 2.45.3(vue@3.5.23(typescript@5.9.3)) pinia: specifier: ^3.0.3 version: 3.0.4(typescript@5.9.3)(vue@3.5.23(typescript@5.9.3)) pinia-plugin-persistedstate: specifier: ^4.4.1 version: 4.7.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.23(typescript@5.9.3))) - sortablejs: - specifier: ^1.15.6 - version: 1.15.6 vue: specifier: ^3.5.18 version: 3.5.23(typescript@5.9.3) - vue-color-kit: - specifier: ^1.0.6 - version: 1.0.6(vue@3.5.23(typescript@5.9.3)) - vue-draggable-plus: - specifier: ^0.6.0 - version: 0.6.0(@types/sortablejs@1.15.9) vue-router: specifier: ^4.5.1 version: 4.6.3(vue@3.5.23(typescript@5.9.3)) - xe-utils: - specifier: ^3.7.8 - version: 3.7.9 devDependencies: '@antfu/eslint-config': specifier: ^7.0.1 version: 7.7.3(@typescript-eslint/typescript-estree@8.70.1(typescript@5.9.3))(@typescript-eslint/utils@8.70.1(eslint@9.39.1)(typescript@5.9.3))(@vue/compiler-sfc@3.5.23)(eslint@9.39.1)(typescript@5.9.3) - '@arco-design/web-vue': - specifier: ^2.58.0 - version: 2.58.0(vue@3.5.23(typescript@5.9.3)) - '@iconify-json/icon-park-outline': - specifier: ^1.2.4 - version: 1.2.4 - '@types/lodash-es': - specifier: ^4.17.12 - version: 4.17.12 '@types/node': specifier: ^24.2.0 version: 24.10.0 '@vitejs/plugin-vue': specifier: ^6.0.1 version: 6.0.1(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3)) - '@vitejs/plugin-vue-jsx': - specifier: ^5.0.1 - version: 5.1.1(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3)) '@vue/tsconfig': specifier: ^0.7.0 version: 0.7.0(typescript@5.9.3)(vue@3.5.23(typescript@5.9.3)) eslint: specifier: ^9.5.0 version: 9.39.1 - less: - specifier: ^4.4.0 - version: 4.4.2 - postcss: - specifier: ^8.5.6 - version: 8.5.6 - postcss-html: - specifier: ^1.8.0 - version: 1.8.0 sass: specifier: ^1.62.1 version: 1.93.3 - stylelint: - specifier: ^16.25.0 - version: 16.25.0(typescript@5.9.3) - stylelint-config-recess-order: - specifier: ^7.4.0 - version: 7.4.0(stylelint-order@7.0.0(stylelint@16.25.0(typescript@5.9.3)))(stylelint@16.25.0(typescript@5.9.3)) - stylelint-config-recommended-vue: - specifier: ^1.6.1 - version: 1.6.1(postcss-html@1.8.0)(stylelint@16.25.0(typescript@5.9.3)) - stylelint-config-standard: - specifier: ^39.0.1 - version: 39.0.1(stylelint@16.25.0(typescript@5.9.3)) - stylelint-config-standard-scss: - specifier: ^16.0.0 - version: 16.0.0(postcss@8.5.6)(stylelint@16.25.0(typescript@5.9.3)) - stylelint-scss: - specifier: ^6.12.1 - version: 6.12.1(stylelint@16.25.0(typescript@5.9.3)) typescript: specifier: ^5.9.2 version: 5.9.3 - unplugin-auto-import: - specifier: ^20.0.0 - version: 20.2.0(@vueuse/core@13.9.0(vue@3.5.23(typescript@5.9.3))) - unplugin-icons: - specifier: ^22.5.0 - version: 22.5.0(@vue/compiler-sfc@3.5.23) - unplugin-vue-components: - specifier: ^29.0.0 - version: 29.2.0(@babel/parser@7.28.5)(vue@3.5.23(typescript@5.9.3)) vite: specifier: ^7.0.6 version: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1) - vite-plugin-compression: - specifier: ^0.5.1 - version: 0.5.1(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)) - vite-plugin-vue-devtools: - specifier: ^8.0.5 - version: 8.0.5(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3)) vue-tsc: specifier: ^3.0.5 version: 3.1.3(typescript@5.9.3) @@ -217,83 +139,6 @@ packages: '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - '@antfu/install-pkg@2.1.0': - resolution: {integrity: sha512-sdg9NxU3zR4Mnawfbc/x6GB5Wf17WYud5qOuEuxXjaKpYpMkISSJEjItGebXJ2bQ4DIcly4NYH23mtkGJjvKUw==} - - '@arco-design/color@0.4.0': - resolution: {integrity: sha512-s7p9MSwJgHeL8DwcATaXvWT3m2SigKpxx4JA1BGPHL4gfvaQsmQfrLBDpjOJFJuJ2jG2dMt3R3P8Pm9E65q18g==} - - '@arco-design/web-vue@2.58.0': - resolution: {integrity: sha512-b1vdPYOmjG5VAkVa7jlVwCb+WynBK+rnKN8zH3yKohpZObZbostRd3HgYNtjjZjGVU3OqR0Yy2FX7ftgF0bcOw==} - peerDependencies: - vue: '>=3.1.0' - - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.28.5': - resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.28.5': - resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.28.5': - resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-annotate-as-pure@7.27.3': - resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.27.2': - resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-create-class-features-plugin@7.28.5': - resolution: {integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-member-expression-to-functions@7.28.5': - resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.27.1': - resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.28.3': - resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-optimise-call-expression@7.27.1': - resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-plugin-utils@7.27.1': - resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-replace-supers@7.27.1': - resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} - engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -302,81 +147,15 @@ packages: resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.28.4': - resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} - engines: {node: '>=6.9.0'} - '@babel/parser@7.28.5': resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-proposal-decorators@7.28.0': - resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-decorators@7.27.1': - resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-attributes@7.27.1': - resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-jsx@7.27.1': - resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.27.1': - resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-typescript@7.28.5': - resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/template@7.27.2': - resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.28.5': - resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} - engines: {node: '>=6.9.0'} - '@babel/types@7.28.5': resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} - '@cacheable/memoize@2.0.3': - resolution: {integrity: sha512-hl9wfQgpiydhQEIv7fkjEzTGE+tcosCXLKFDO707wYJ/78FVOlowb36djex5GdbSyeHnG62pomYLMuV/OT8Pbw==} - - '@cacheable/memory@2.0.4': - resolution: {integrity: sha512-cCmJKCKlT1t7hNBI1+gFCwmKFd9I4pS3zqBeNGXTSODnpa0EeDmORHY8oEMTuozfdg3cgsVh8ojLaPYb6eC7Cg==} - - '@cacheable/utils@2.2.0': - resolution: {integrity: sha512-7xaQayO3msdVcxXLYcLU5wDqJBNdQcPPPHr6mdTEIQI7N7TbtSVVTpWOTfjyhg0L6AQwQdq7miKdWtTDBoBldQ==} - '@clack/core@1.5.1': resolution: {integrity: sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ==} engines: {node: '>= 20.12.0'} @@ -385,31 +164,15 @@ packages: resolution: {integrity: sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ==} engines: {node: '>= 20.12.0'} - '@csstools/css-parser-algorithms@3.0.5': - resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} - engines: {node: '>=18'} + '@css-render/plugin-bem@0.15.14': + resolution: {integrity: sha512-QK513CJ7yEQxm/P3EwsI+d+ha8kSOcjGvD6SevM41neEMxdULE+18iuQK6tEChAWMOQNQPLG/Rw3Khb69r5neg==} peerDependencies: - '@csstools/css-tokenizer': ^3.0.4 + css-render: ~0.15.14 - '@csstools/css-tokenizer@3.0.4': - resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} - engines: {node: '>=18'} - - '@csstools/media-query-list-parser@4.0.3': - resolution: {integrity: sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==} - engines: {node: '>=18'} + '@css-render/vue3-ssr@0.15.14': + resolution: {integrity: sha512-//8027GSbxE9n3QlD73xFY6z4ZbHbvrOVB7AO6hsmrEzGbg+h2A09HboUyDgu+xsmj7JnvJD39Irt+2D0+iV8g==} peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.5 - '@csstools/css-tokenizer': ^3.0.4 - - '@csstools/selector-specificity@5.0.0': - resolution: {integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==} - engines: {node: '>=18'} - peerDependencies: - postcss-selector-parser: ^7.0.0 - - '@dual-bundle/import-meta-resolve@4.2.1': - resolution: {integrity: sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==} + vue: ^3.0.11 '@e18e/eslint-plugin@0.2.0': resolution: {integrity: sha512-mXgODVwhuDjTJ+UT+XSvmMmCidtGKfrV5nMIv1UtpWex2pYLsIM3RSpT8HWIMAebS9qANbXPKlSX4BE7ZvuCgA==} @@ -422,6 +185,9 @@ packages: oxlint: optional: true + '@emotion/hash@0.8.0': + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + '@es-joy/jsdoccomment@0.86.0': resolution: {integrity: sha512-ukZmRQ81WiTpDWO6D/cTBM7XbrNtutHKvAVnZN/8pldAwLoJArGOvkNyxPTBGsPjsoaQBJxlH+tE2TNA/92Qgw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -687,50 +453,16 @@ packages: '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - '@iconify/utils@3.1.7': - resolution: {integrity: sha512-JZHlwdID+dy+lTgbYC8NEC4zeugqeYsc6jewvzb4c58kHauJn+X7rNwQjxz5p2qSjqaEeQoLkCIQ9v/H4PK0/w==} - '@iconify/vue@5.0.3': resolution: {integrity: sha512-e9x6QZe2/HyeHUog0jzZJSZts7m/YntPYp/p4OqtZEXpHznxWnQpcX30YpuaUH43h69l3YeR1FCJ8V1t3qd7qA==} peerDependencies: vue: '>=3.0.0' - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - - '@keyv/bigmap@1.1.0': - resolution: {integrity: sha512-MX7XIUNwVRK+hjZcAbNJ0Z8DREo+Weu9vinBOjGU1thEi9F6vPhICzBbk4CCf3eEefKRz7n6TfZXwUFZTSgj8Q==} - engines: {node: '>= 18'} - peerDependencies: - keyv: ^5.5.3 - - '@keyv/serialize@1.1.1': - resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@juggle/resize-observer@3.4.0': + resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} '@ota-meshi/ast-token-store@0.3.0': resolution: {integrity: sha512-XRO0zi2NIUKq2lUk3T1ecFSld1fMWRKE6naRFGkgkdeosx7IslyUKNv5Dcb5PJTja9tHJoFu0v/7yEpAkrkrTg==} @@ -828,15 +560,9 @@ packages: resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} engines: {node: ^14.18.0 || >=16.0.0} - '@polka/url@1.0.0-next.29': - resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@rolldown/pluginutils@1.0.0-beta.29': resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==} - '@rolldown/pluginutils@1.0.0-beta.47': - resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==} - '@rollup/rollup-android-arm-eabi@4.52.5': resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} cpu: [arm] @@ -998,9 +724,6 @@ packages: '@types/node@26.6.3': resolution: {integrity: sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg==} - '@types/sortablejs@1.15.9': - resolution: {integrity: sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ==} - '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -1066,13 +789,6 @@ packages: resolution: {integrity: sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitejs/plugin-vue-jsx@5.1.1': - resolution: {integrity: sha512-uQkfxzlF8SGHJJVH966lFTdjM/lGcwJGzwAHpVqAPDD/QcsqoUGa+q31ox1BrUfi+FLP2ChVp7uLXE3DkHyDdQ==} - engines: {node: ^20.19.0 || >=22.12.0} - peerDependencies: - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - vue: ^3.0.0 - '@vitejs/plugin-vue@6.0.1': resolution: {integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1105,22 +821,6 @@ packages: '@volar/typescript@2.4.23': resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==} - '@vue/babel-helper-vue-transform-on@1.5.0': - resolution: {integrity: sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==} - - '@vue/babel-plugin-jsx@1.5.0': - resolution: {integrity: sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - peerDependenciesMeta: - '@babel/core': - optional: true - - '@vue/babel-plugin-resolve-type@1.5.0': - resolution: {integrity: sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@vue/compiler-core@3.5.23': resolution: {integrity: sha512-nW7THWj5HOp085ROk65LwaoxuzDsjIxr485F4iu63BoxsXoSqKqmsUUoP4A7Gl67DgIgi0zJ8JFgHfvny/74MA==} @@ -1139,23 +839,12 @@ packages: '@vue/devtools-api@7.7.7': resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==} - '@vue/devtools-core@8.0.5': - resolution: {integrity: sha512-dpCw8nl0GDBuiL9SaY0mtDxoGIEmU38w+TQiYEPOLhW03VDC0lfNMYXS/qhl4I0YlysGp04NLY4UNn6xgD0VIQ==} - peerDependencies: - vue: ^3.0.0 - '@vue/devtools-kit@7.7.7': resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==} - '@vue/devtools-kit@8.0.5': - resolution: {integrity: sha512-q2VV6x1U3KJMTQPUlRMyWEKVbcHuxhqJdSr6Jtjz5uAThAIrfJ6WVZdGZm5cuO63ZnSUz0RCsVwiUUb0mDV0Yg==} - '@vue/devtools-shared@7.7.7': resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==} - '@vue/devtools-shared@8.0.5': - resolution: {integrity: sha512-bRLn6/spxpmgLk+iwOrR29KrYnJjG9DGpHGkDFG82UM21ZpJ39ztUT9OXX3g+usW7/b2z+h46I9ZiYyB07XMXg==} - '@vue/language-core@3.1.3': resolution: {integrity: sha512-KpR1F/eGAG9D1RZ0/T6zWJs6dh/pRLfY5WupecyYKJ1fjVmDMgTPw9wXmKv2rBjo4zCJiOSiyB8BDP1OUwpMEA==} peerDependencies: @@ -1220,22 +909,16 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - alien-signals@3.0.6: resolution: {integrity: sha512-gCs0YqC1mkYGC6IRXsSrA62ShOSv1FlVN5tRp/Cs2vRWLK/BAeluWIdfsl253pFQPznKEvRmHhfep7crWfyfWQ==} - animate.css@4.1.1: - resolution: {integrity: sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -1244,10 +927,6 @@ packages: resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} engines: {node: '>=14'} - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - are-docs-informative@0.0.2: resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} engines: {node: '>=14'} @@ -1255,32 +934,18 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} + async-validator@4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - axios@0.27.2: - resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} - - b-tween@0.3.3: - resolution: {integrity: sha512-oEHegcRpA7fAuc9KC4nktucuZn2aS8htymCPcP3qkEGPqiBH+GfqtqoG2l7LxHngg6O0HFM7hOeOYExl1Oz4ZA==} - - b-validate@1.5.3: - resolution: {integrity: sha512-iCvCkGFskbaYtfQ0a3GmcQCHl/Sv1GufXFGuUQ+FE+WJa7A/espLOuFIn09B944V8/ImPj71T4+rTASxO2PAuA==} + axios@1.20.0: + resolution: {integrity: sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - balanced-match@2.0.0: - resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} - balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} @@ -1289,10 +954,6 @@ packages: resolution: {integrity: sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==} hasBin: true - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - birpc@2.7.0: resolution: {integrity: sha512-tub/wFGH49vNCm0xraykcY3TcRgX/3JsALYq/Lwrtti+bTyFHkCUAWF5wgYoie8P41wYwig2mIKiqoocr1EkEQ==} @@ -1319,17 +980,10 @@ packages: resolution: {integrity: sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==} engines: {node: '>=18.20'} - bundle-name@4.1.0: - resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} - engines: {node: '>=18'} - cac@7.0.0: resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} engines: {node: '>=20.19.0'} - cacheable@2.1.1: - resolution: {integrity: sha512-LmF4AXiSNdiRbI2UjH8pAp9NIXxeQsTotpEaegPiDcnN0YPygDJDV3l/Urc0mL72JWdATEorKqIHEx55nDlONg==} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -1354,10 +1008,6 @@ packages: character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -1370,28 +1020,13 @@ packages: resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} engines: {node: '>=4'} - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - - color@3.2.1: - resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} - - colord@2.9.3: - resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} - combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -1408,9 +1043,6 @@ packages: resolution: {integrity: sha512-0h+uSNtQGW3D98eQt3jJ8L06Fves8hncB4V/PKdw/Qb8Hnk19VaKuTr55UNRYiSoVa7WwrFls+rh3ux9agmkeQ==} engines: {node: '>= 12.0.0'} - compute-scroll-into-view@1.0.20: - resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -1420,9 +1052,6 @@ packages: confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - copy-anything@2.0.6: resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} @@ -1433,35 +1062,32 @@ packages: core-js-compat@3.46.0: resolution: {integrity: sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==} - cosmiconfig@9.0.0: - resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - css-functions-list@3.2.3: - resolution: {integrity: sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==} - engines: {node: '>=12 || >=16'} - - css-tree@3.1.0: - resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} - engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-render@0.15.14: + resolution: {integrity: sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==} cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} hasBin: true + csstype@3.0.11: + resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==} + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + date-fns-tz@3.2.0: + resolution: {integrity: sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==} + peerDependencies: + date-fns: ^3.0.0 || ^4.0.0 + + date-fns@4.4.0: + resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==} + dayjs@1.11.19: resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} @@ -1480,18 +1106,6 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - default-browser-id@5.0.1: - resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} - engines: {node: '>=18'} - - default-browser@5.4.0: - resolution: {integrity: sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==} - engines: {node: '>=18'} - - define-lazy-prop@3.0.0: - resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} - engines: {node: '>=12'} - defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} @@ -1515,36 +1129,13 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - - domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - - domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} - engines: {node: '>= 4'} - - domutils@3.2.2: - resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} - dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - echarts@6.1.0: - resolution: {integrity: sha512-q0yaFPggC9FUdsWH4blavRWFmxdrIodbkoKNAjJudAI6CA9gNPxHtV2RcZNEepZVlk4yvBYkOkbk6HIVpIyHZA==} - electron-to-chromium@1.5.245: resolution: {integrity: sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ==} - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - empathic@2.0.0: resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} engines: {node: '>=14'} @@ -1561,20 +1152,10 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} - env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - errno@0.1.8: resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} hasBin: true - error-ex@1.3.4: - resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - - error-stack-parser-es@1.0.5: - resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -1813,23 +1394,19 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + evtd@0.2.4: + resolution: {integrity: sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==} + exsolve@1.0.7: resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -1842,19 +1419,9 @@ packages: fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} - fastest-levenshtein@1.0.16: - resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} - engines: {node: '>= 4.9.1'} - - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - fault@2.0.1: resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} @@ -1867,9 +1434,6 @@ packages: picomatch: optional: true - file-entry-cache@10.1.4: - resolution: {integrity: sha512-5XRUFc0WTtUbjfGzEwXc42tiGxQHBmtbUG1h9L2apu4SulCGN3Hqm//9D6FAolf8MYNL7f/YlJl9vy08pj5JuA==} - file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -1890,14 +1454,11 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flat-cache@6.1.18: - resolution: {integrity: sha512-JUPnFgHMuAVmLmoH9/zoZ6RHOt5n9NlUw/sDXsTbROJ2SFoS2DS4s+swAV6UTeTbGH/CAsZIE6M8TaG/3jVxgQ==} - flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + follow-redirects@1.16.1: + resolution: {integrity: sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -1905,18 +1466,14 @@ packages: debug: optional: true - form-data@4.0.4: - resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} engines: {node: '>= 6'} format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} - fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1925,10 +1482,6 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -1943,22 +1496,10 @@ packages: github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - global-modules@2.0.0: - resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} - engines: {node: '>=6'} - - global-prefix@3.0.0: - resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} - engines: {node: '>=6'} - globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -1975,13 +1516,6 @@ packages: resolution: {integrity: sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==} engines: {node: '>=18'} - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - globjoin@0.1.4: - resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} - globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} @@ -2004,25 +1538,23 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} + highlight.js@11.12.0: + resolution: {integrity: sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==} + engines: {node: '>=12.0.0'} + hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} - hookified@1.12.2: - resolution: {integrity: sha512-aokUX1VdTpI0DUsndvW+OiwmBpKCu/NgRsSSkuSY0zq8PY6Q6a+lmOfAFDXAAOtBqJELvcWY9L1EVtzjbQcMdg==} - html-entities@2.6.0: resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} - html-tags@3.3.1: - resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} - engines: {node: '>=8'} - - htmlparser2@8.0.2: - resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} @@ -2048,9 +1580,6 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - import-meta-resolve@4.2.0: - resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} - imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -2059,53 +1588,22 @@ packages: resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} engines: {node: '>=12'} - ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - is-arrayish@0.3.4: - resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==} - - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - is-builtin-module@5.0.0: resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==} engines: {node: '>=18.20'} - is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-plain-object@5.0.0: - resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} - engines: {node: '>=0.10.0'} - is-what@3.14.1: resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} @@ -2113,19 +1611,9 @@ packages: resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} engines: {node: '>=18'} - is-wsl@3.1.0: - resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} - engines: {node: '>=16'} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-tokens@9.0.1: - resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -2149,49 +1637,19 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - jsonc-eslint-parser@3.3.0: resolution: {integrity: sha512-hYTGkHGNRZnXOFZ1urhINADoqDrGfpy53cjw+dxk84QE0pUDujQzeUeamNs6Mz44/TKD49z2x6/GVSu4ZrtA+Q==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - jsonfile@6.2.0: - resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - keyv@5.5.3: - resolution: {integrity: sha512-h0Un1ieD+HUrzBH6dJXhod3ifSghk5Hw/2Y4/KHBziPlZecrFyE9YOTPU6eOs0V9pYl8gOs86fkr/KN8lUX39A==} - - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - - known-css-properties@0.36.0: - resolution: {integrity: sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA==} - - known-css-properties@0.37.0: - resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} - - kolorist@1.8.0: - resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - less@4.4.2: resolution: {integrity: sha512-j1n1IuTX1VQjIy3tT7cyGbX7nvQOsFLoIqobZv4ttI5axP923gA44zUj6miiA6R5Aoms4sEGVIIcucXUbRI14g==} engines: {node: '>=14'} @@ -2201,9 +1659,6 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - local-pkg@1.1.2: resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} engines: {node: '>=14'} @@ -2218,15 +1673,12 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash.truncate@4.4.2: - resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -2241,9 +1693,6 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - mathml-tag-names@2.1.3: - resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} - mdast-util-find-and-replace@3.0.2: resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} @@ -2280,20 +1729,6 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - mdn-data@2.12.2: - resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} - - mdn-data@2.25.0: - resolution: {integrity: sha512-T2LPsjgUE/tgMmRXREVmwsux89DwWfNjiynOeXuLd2mX6jphGQ2YE3Ukz7LQ2VOFKiVZU/Ee1GqzHiipZCjymw==} - - meow@13.2.0: - resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} - engines: {node: '>=18'} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -2414,26 +1849,23 @@ packages: module-replacements@2.11.0: resolution: {integrity: sha512-j5sNQm3VCpQQ7nTqGeOZtoJtV3uKERgCBm9QRhmGRiXiqkf7iRFOkfxdJRZWLkqYY8PNf4cDQF/WfXUYLENrRA==} - mrmime@2.0.1: - resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} - engines: {node: '>=10'} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + naive-ui@2.45.3: + resolution: {integrity: sha512-D83gJ+6zUdkQ5DuUbmlfyaM1t8pV5WMPa6KmL9mByqQuiL12qBdA0n/iEIth4dQw1x/duNGpOS2anpt5FKO1kw==} + engines: {node: '>=20'} + peerDependencies: + vue: ^3.0.0 + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@5.1.6: - resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} - engines: {node: ^18 || >=20} - hasBin: true - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -2452,29 +1884,12 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - nprogress@0.2.0: - resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} - nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - number-precision@1.6.0: - resolution: {integrity: sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ==} - object-deep-merge@2.0.1: resolution: {integrity: sha512-aKttDKcU3pyZqKcCkDhsMn70WmZFG2JGDQLP9EcLyTSIFQRCPWLAmBZRLJnrVUrhPG1jETEEbfdgbNtJf1LyMg==} - ohash@2.0.11: - resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} - - open@10.2.0: - resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} - engines: {node: '>=18'} - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -2490,9 +1905,6 @@ packages: package-manager-detector@1.5.0: resolution: {integrity: sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==} - package-manager-detector@1.8.0: - resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -2504,10 +1916,6 @@ packages: parse-imports-exports@0.2.4: resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==} - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - parse-node-version@1.0.1: resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} engines: {node: '>= 0.10'} @@ -2526,19 +1934,12 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} - perfect-debounce@2.0.0: - resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2594,50 +1995,10 @@ packages: pnpm-workspace-yaml@1.9.1: resolution: {integrity: sha512-Xj/T2X6DNAWbtk/9UQ0/TJRswltiHZevmcMjqoL7WrpJi67lu3qaslU4+I+zJ8wtwqmuvAE0KkB8rbv2ZdogBg==} - postcss-html@1.8.0: - resolution: {integrity: sha512-5mMeb1TgLWoRKxZ0Xh9RZDfwUUIqRrcxO2uXO+Ezl1N5lqpCiSU5Gk6+1kZediBfBHFtPCdopr2UZ2SgUsKcgQ==} - engines: {node: ^12 || >=14} - - postcss-media-query-parser@0.2.3: - resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} - - postcss-resolve-nested-selector@0.1.6: - resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} - - postcss-safe-parser@6.0.0: - resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.3.3 - - postcss-safe-parser@7.0.1: - resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} - engines: {node: '>=18.0'} - peerDependencies: - postcss: ^8.4.31 - - postcss-scss@4.0.9: - resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.4.29 - - postcss-selector-parser@7.1.0: - resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} - engines: {node: '>=4'} - postcss-selector-parser@7.1.6: resolution: {integrity: sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==} engines: {node: '>=4'} - postcss-sorting@9.1.0: - resolution: {integrity: sha512-Mn8KJ45HNNG6JBpBizXcyf6LqY/qyqetGcou/nprDnFwBFBLGj0j/sNKV2lj2KMOVOwdXu14aEzqJv8CIV6e8g==} - peerDependencies: - postcss: ^8.4.20 - - postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -2646,6 +2007,10 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} @@ -2653,20 +2018,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qified@0.5.1: - resolution: {integrity: sha512-+BtFN3dCP+IaFA6IYNOu/f/uK1B8xD2QWyOeCse0rjtAebBmkzgd2d1OAXi3ikAzJMIBSdzZDNZ3wZKEUDQs5w==} - engines: {node: '>=20'} - quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -2687,32 +2041,17 @@ packages: resolution: {integrity: sha512-ycwFAS14Jw4mppvmK4GR/J6u3WpWpjkEApehuHtLc/8VpPNpDMbQ4WjqwplXifGeyKOzHSFLmSPqzksDQE2Sfg==} hasBin: true - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - reserved-identifiers@1.2.0: resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} engines: {node: '>=18'} - resize-observer-polyfill@1.5.1: - resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} - resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} @@ -2721,13 +2060,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-applescript@7.1.0: - resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} - engines: {node: '>=18'} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -2739,24 +2071,17 @@ packages: sax@1.4.3: resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==} - scroll-into-view-if-needed@2.2.31: - resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} - scslre@0.3.0: resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} engines: {node: ^14.0.0 || >=16.0.0} - scule@1.3.0: - resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + seemly@0.3.10: + resolution: {integrity: sha512-2+SMxtG1PcsL0uyhkumlOU6Qo9TAQ/WyH7tthnPIOQB05/12jz9naq6GZ6iZ6ApVsO3rr2gsnTf3++OV63kE1Q==} semver@5.7.2: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - semver@7.7.3: resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} @@ -2775,31 +2100,9 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - simple-swizzle@0.2.4: - resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==} - - sirv@3.0.2: - resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} - engines: {node: '>=18'} - sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - slice-ansi@4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} - - sortablejs@1.15.6: - resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -2821,14 +2124,6 @@ packages: resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} engines: {node: '>=0.10.0'} - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - strip-indent@4.1.1: resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} engines: {node: '>=12'} @@ -2837,78 +2132,6 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - strip-literal@3.1.0: - resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} - - stylelint-config-html@1.1.0: - resolution: {integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==} - engines: {node: ^12 || >=14} - peerDependencies: - postcss-html: ^1.0.0 - stylelint: '>=14.0.0' - - stylelint-config-recess-order@7.4.0: - resolution: {integrity: sha512-W3G517cBaMDYRX5Fzhro4fhRkkLafLgVSPfQnhxbiLyMnbLq47RMF/NRaOJ4OQsKWYhsIHOIc2Q3VL0X3Q7oPg==} - peerDependencies: - stylelint: '>=16.18' - stylelint-order: '>=7' - - stylelint-config-recommended-scss@16.0.2: - resolution: {integrity: sha512-aUTHhPPWCvFyWaxtckJlCPaXTDFsp4pKO8evXNCsW9OwsaUWyMd6jvcUhSmfGWPrTddvzNqK4rS/UuSLcbVGdQ==} - engines: {node: '>=20'} - peerDependencies: - postcss: ^8.3.3 - stylelint: ^16.24.0 - peerDependenciesMeta: - postcss: - optional: true - - stylelint-config-recommended-vue@1.6.1: - resolution: {integrity: sha512-lLW7hTIMBiTfjenGuDq2kyHA6fBWd/+Df7MO4/AWOxiFeXP9clbpKgg27kHfwA3H7UNMGC7aeP3mNlZB5LMmEQ==} - engines: {node: ^12 || >=14} - peerDependencies: - postcss-html: ^1.0.0 - stylelint: '>=14.0.0' - - stylelint-config-recommended@17.0.0: - resolution: {integrity: sha512-WaMSdEiPfZTSFVoYmJbxorJfA610O0tlYuU2aEwY33UQhSPgFbClrVJYWvy3jGJx+XW37O+LyNLiZOEXhKhJmA==} - engines: {node: '>=18.12.0'} - peerDependencies: - stylelint: ^16.23.0 - - stylelint-config-standard-scss@16.0.0: - resolution: {integrity: sha512-/FHECLUu+med/e6OaPFpprG86ShC4SYT7Tzb2PTVdDjJsehhFBOioSlWqYFqJxmGPIwO3AMBxNo+kY3dxrbczA==} - engines: {node: '>=20'} - peerDependencies: - postcss: ^8.3.3 - stylelint: ^16.23.1 - peerDependenciesMeta: - postcss: - optional: true - - stylelint-config-standard@39.0.1: - resolution: {integrity: sha512-b7Fja59EYHRNOTa3aXiuWnhUWXFU2Nfg6h61bLfAb5GS5fX3LMUD0U5t4S8N/4tpHQg3Acs2UVPR9jy2l1g/3A==} - engines: {node: '>=18.12.0'} - peerDependencies: - stylelint: ^16.23.0 - - stylelint-order@7.0.0: - resolution: {integrity: sha512-rSWxx0KscYfxU02wEskKXES9lkRzuuONMMNkZ7SUc6uiF3tDKm7e+sE0Ax/SBlG4TUf1sp1R6f3/SlsPGmzthg==} - engines: {node: '>=20.19.0'} - peerDependencies: - stylelint: ^16.18.0 - - stylelint-scss@6.12.1: - resolution: {integrity: sha512-UJUfBFIvXfly8WKIgmqfmkGKPilKB4L5j38JfsDd+OCg2GBdU0vGUV08Uw82tsRZzd4TbsUURVVNGeOhJVF7pA==} - engines: {node: '>=18.12.0'} - peerDependencies: - stylelint: ^16.0.2 - - stylelint@16.25.0: - resolution: {integrity: sha512-Li0avYWV4nfv1zPbdnxLYBGq4z8DVZxbRgx4Kn6V+Uftz1rMoF1qiEI3oL4kgWqyYgCgs7gT5maHNZ82Gk03vQ==} - engines: {node: '>=18.12.0'} - hasBin: true - superjson@2.2.5: resolution: {integrity: sha512-zWPTX96LVsA/eVYnqOM2+ofcdPqdS1dAF1LN4TS2/MWuUpfitd9ctTa87wt4xrYnZnkLtS69xpBdSxVBP5Rm6w==} engines: {node: '>=16'} @@ -2917,21 +2140,10 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-hyperlinks@3.2.0: - resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} - engines: {node: '>=14.18'} - - svg-tags@1.0.0: - resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} - synckit@0.11.13: resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} engines: {node: ^14.18.0 || >=16.0.0} - table@6.9.0: - resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} - engines: {node: '>=10.0.0'} - tapable@2.3.0: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} @@ -2940,10 +2152,6 @@ packages: resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} - tinyexec@1.3.1: - resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} - engines: {node: '>=18'} - tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -2964,9 +2172,8 @@ packages: resolution: {integrity: sha512-A5F0cM6+mDleacLIEUkmfpkBbnHJFV1d2rprHU2MXNk7mlxHq2zGojA+SRvQD1RoMo9gqjZPWEaKG4v1BQ48lw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} - engines: {node: '>=6'} + treemate@0.3.11: + resolution: {integrity: sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==} ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} @@ -2979,9 +2186,6 @@ packages: peerDependencies: typescript: '>=4.0.0' - tslib@2.3.0: - resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -3003,10 +2207,6 @@ packages: undici-types@8.9.0: resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} - unimport@5.5.0: - resolution: {integrity: sha512-/JpWMG9s1nBSlXJAQ8EREFTFy3oy6USFd8T6AoBaw1q2GGcF4R9yp3ofg32UODZlYEO5VD0EWE1RpI9XDWyPYg==} - engines: {node: '>=18.12.0'} - unist-util-is@6.0.1: resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} @@ -3019,66 +2219,6 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - - unplugin-auto-import@20.2.0: - resolution: {integrity: sha512-vfBI/SvD9hJqYNinipVOAj5n8dS8DJXFlCKFR5iLDp2SaQwsfdnfLXgZ+34Kd3YY3YEY9omk8XQg0bwos3Q8ug==} - engines: {node: '>=14'} - peerDependencies: - '@nuxt/kit': ^4.0.0 - '@vueuse/core': '*' - peerDependenciesMeta: - '@nuxt/kit': - optional: true - '@vueuse/core': - optional: true - - unplugin-icons@22.5.0: - resolution: {integrity: sha512-MBlMtT5RuMYZy4TZgqUL2OTtOdTUVsS1Mhj6G1pEzMlFJlEnq6mhUfoIt45gBWxHcsOdXJDWLg3pRZ+YmvAVWQ==} - peerDependencies: - '@svgr/core': '>=7.0.0' - '@svgx/core': ^1.0.1 - '@vue/compiler-sfc': ^3.0.2 || ^2.7.0 - svelte: ^3.0.0 || ^4.0.0 || ^5.0.0 - vue-template-compiler: ^2.6.12 - vue-template-es2015-compiler: ^1.9.0 - peerDependenciesMeta: - '@svgr/core': - optional: true - '@svgx/core': - optional: true - '@vue/compiler-sfc': - optional: true - svelte: - optional: true - vue-template-compiler: - optional: true - vue-template-es2015-compiler: - optional: true - - unplugin-utils@0.3.1: - resolution: {integrity: sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==} - engines: {node: '>=20.19.0'} - - unplugin-vue-components@29.2.0: - resolution: {integrity: sha512-QxBeBdmEflgtJRgMQMc/z/JVV5lcwXN5nOy5ehX6CKDGylIu6Qn4Goy8X95S0qOxF7EdI+uNhdBd4v5i0bvzCw==} - engines: {node: '>=14'} - peerDependencies: - '@babel/parser': ^7.15.8 - '@nuxt/kit': ^3.2.2 || ^4.0.0 - vue: 2 || 3 - peerDependenciesMeta: - '@babel/parser': - optional: true - '@nuxt/kit': - optional: true - - unplugin@2.3.10: - resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==} - engines: {node: '>=18.12.0'} - update-browserslist-db@1.1.4: resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} hasBin: true @@ -3091,46 +2231,15 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vdirs@0.1.8: + resolution: {integrity: sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==} + peerDependencies: + vue: ^3.0.11 + verkit@0.3.2: resolution: {integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==} engines: {node: '>=18.12.0'} - vite-dev-rpc@1.1.0: - resolution: {integrity: sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==} - peerDependencies: - vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0 - - vite-hot-client@2.1.0: - resolution: {integrity: sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==} - peerDependencies: - vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 - - vite-plugin-compression@0.5.1: - resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==} - peerDependencies: - vite: '>=2.0.0' - - vite-plugin-inspect@11.3.3: - resolution: {integrity: sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==} - engines: {node: '>=14'} - peerDependencies: - '@nuxt/kit': '*' - vite: ^6.0.0 || ^7.0.0-0 - peerDependenciesMeta: - '@nuxt/kit': - optional: true - - vite-plugin-vue-devtools@8.0.5: - resolution: {integrity: sha512-p619BlKFOqQXJ6uDWS1vUPQzuJOD6xJTfftj57JXBGoBD/yeQCowR7pnWcr/FEX4/HVkFbreI6w2uuGBmQOh6A==} - engines: {node: '>=v14.21.3'} - peerDependencies: - vite: ^6.0.0 || ^7.0.0-0 - - vite-plugin-vue-inspector@5.3.2: - resolution: {integrity: sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==} - peerDependencies: - vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 - vite@7.2.1: resolution: {integrity: sha512-qTl3VF7BvOupTR85Zc561sPEgxyUSNSvTQ9fit7DEMP7yPgvvIGm5Zfa1dOM+kOwWGNviK9uFM9ra77+OjK7lQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3171,23 +2280,14 @@ packages: yaml: optional: true + vooks@0.2.12: + resolution: {integrity: sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==} + peerDependencies: + vue: ^3.0.0 + vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} - vue-color-kit@1.0.6: - resolution: {integrity: sha512-56oSUp8hGIZ3E4ayZLqTDAb2C7VzaWAC1EVUsZEvPCD2wiiiFd2L2BiEGN1ingfz538sYWKNXxMZQGslqbMiBg==} - peerDependencies: - vue: ^3.0.5 - - vue-draggable-plus@0.6.0: - resolution: {integrity: sha512-G5TSfHrt9tX9EjdG49InoFJbt2NYk0h3kgjgKxkFWr3ulIUays0oFObr5KZ8qzD4+QnhtALiRwIqY6qul4egqw==} - peerDependencies: - '@types/sortablejs': ^1.15.0 - '@vue/composition-api': '*' - peerDependenciesMeta: - '@vue/composition-api': - optional: true - vue-eslint-parser@10.4.1: resolution: {integrity: sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3213,12 +2313,10 @@ packages: typescript: optional: true - webpack-virtual-modules@0.6.2: - resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - - which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true + vueuc@0.4.66: + resolution: {integrity: sha512-hHrZl+0AsrUvqYPBXSXQ2QaSrO3razjeUseLAhJWlOS/Hdfw/Y2MU05fOTP1qn1A3121gMxsHLaEz2V6CkWdTg==} + peerDependencies: + vue: ^3.0.11 which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} @@ -3229,24 +2327,10 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - write-file-atomic@5.0.1: - resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - wsl-utils@0.1.0: - resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} - engines: {node: '>=18'} - - xe-utils@3.7.9: - resolution: {integrity: sha512-LWH6M7g+TKX8P2fqGxDPgJQygiULhbMtpmfxTMxJTUjuUv9y2+I45UCcVLh5AnmJSxDV1xTxsq7G5P9Eid06JQ==} - xml-name-validator@5.0.0: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml-eslint-parser@2.1.0: resolution: {integrity: sha512-1zo9KRfp6vIAXBEhWAz09ex3hUwh3T+/6dGbGteHvNseA0Uk4FgQnPES55klZ6cDzSy9FpgKS0D/CoLUvCCj4w==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -3265,9 +2349,6 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zrender@6.1.0: - resolution: {integrity: sha512-oEGMDB6pOP2S6OwRR4PdVv610zrjnA3Bh+JnSG12fYJlBKjtNAoEb5fSUoCOOINlH96I2fU38/A2UpRKs67xYQ==} - zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -3327,233 +2408,19 @@ snapshots: package-manager-detector: 1.5.0 tinyexec: 1.0.2 - '@antfu/install-pkg@2.1.0': - dependencies: - package-manager-detector: 1.8.0 - tinyexec: 1.3.1 - - '@arco-design/color@0.4.0': - dependencies: - color: 3.2.1 - - '@arco-design/web-vue@2.58.0(vue@3.5.23(typescript@5.9.3))': - dependencies: - '@arco-design/color': 0.4.0 - b-tween: 0.3.3 - b-validate: 1.5.3 - compute-scroll-into-view: 1.0.20 - dayjs: 1.11.19 - number-precision: 1.6.0 - resize-observer-polyfill: 1.5.1 - scroll-into-view-if-needed: 2.2.31 - vue: 3.5.23(typescript@5.9.3) - - '@babel/code-frame@7.27.1': - dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/compat-data@7.28.5': {} - - '@babel/core@7.28.5': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.28.5': - dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - - '@babel/helper-annotate-as-pure@7.27.3': - dependencies: - '@babel/types': 7.28.5 - - '@babel/helper-compilation-targets@7.27.2': - dependencies: - '@babel/compat-data': 7.28.5 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.27.0 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.5 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-globals@7.28.0': {} - - '@babel/helper-member-expression-to-functions@7.28.5': - dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-imports@7.27.1': - dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 - transitivePeerDependencies: - - supports-color - - '@babel/helper-optimise-call-expression@7.27.1': - dependencies: - '@babel/types': 7.28.5 - - '@babel/helper-plugin-utils@7.27.1': {} - - '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.5 - transitivePeerDependencies: - - supports-color - - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - transitivePeerDependencies: - - supports-color - '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.28.5': {} - '@babel/helper-validator-option@7.27.1': {} - - '@babel/helpers@7.28.4': - dependencies: - '@babel/template': 7.27.2 - '@babel/types': 7.28.5 - '@babel/parser@7.28.5': dependencies: '@babel/types': 7.28.5 - '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.5) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) - transitivePeerDependencies: - - supports-color - - '@babel/template@7.27.2': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - - '@babel/traverse@7.28.5': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/types': 7.28.5 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@cacheable/memoize@2.0.3': - dependencies: - '@cacheable/utils': 2.2.0 - - '@cacheable/memory@2.0.4': - dependencies: - '@cacheable/utils': 2.2.0 - '@keyv/bigmap': 1.1.0(keyv@5.5.3) - hookified: 1.12.2 - keyv: 5.5.3 - - '@cacheable/utils@2.2.0': - dependencies: - keyv: 5.5.3 - '@clack/core@1.5.1': dependencies: fast-wrap-ansi: 0.2.2 @@ -3566,22 +2433,13 @@ snapshots: fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 - '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + '@css-render/plugin-bem@0.15.14(css-render@0.15.14)': dependencies: - '@csstools/css-tokenizer': 3.0.4 + css-render: 0.15.14 - '@csstools/css-tokenizer@3.0.4': {} - - '@csstools/media-query-list-parser@4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + '@css-render/vue3-ssr@0.15.14(vue@3.5.23(typescript@5.9.3))': dependencies: - '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': 3.0.4 - - '@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.1.0)': - dependencies: - postcss-selector-parser: 7.1.0 - - '@dual-bundle/import-meta-resolve@4.2.1': {} + vue: 3.5.23(typescript@5.9.3) '@e18e/eslint-plugin@0.2.0(eslint@9.39.1)': dependencies: @@ -3589,6 +2447,8 @@ snapshots: optionalDependencies: eslint: 9.39.1 + '@emotion/hash@0.8.0': {} + '@es-joy/jsdoccomment@0.86.0': dependencies: '@types/estree': 1.0.8 @@ -3792,54 +2652,14 @@ snapshots: '@iconify/types@2.0.0': {} - '@iconify/utils@3.1.7': - dependencies: - '@antfu/install-pkg': 2.1.0 - '@iconify/types': 2.0.0 - import-meta-resolve: 4.2.0 - '@iconify/vue@5.0.3(vue@3.5.23(typescript@5.9.3))': dependencies: '@iconify/types': 2.0.0 vue: 3.5.23(typescript@5.9.3) - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/remapping@2.3.5': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@keyv/bigmap@1.1.0(keyv@5.5.3)': - dependencies: - hookified: 1.12.2 - keyv: 5.5.3 - - '@keyv/serialize@1.1.1': {} - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + '@juggle/resize-observer@3.4.0': {} '@ota-meshi/ast-token-store@0.3.0': {} @@ -3906,12 +2726,8 @@ snapshots: '@pkgr/core@0.3.6': {} - '@polka/url@1.0.0-next.29': {} - '@rolldown/pluginutils@1.0.0-beta.29': {} - '@rolldown/pluginutils@1.0.0-beta.47': {} - '@rollup/rollup-android-arm-eabi@4.52.5': optional: true @@ -4020,8 +2836,6 @@ snapshots: dependencies: undici-types: 8.9.0 - '@types/sortablejs@1.15.9': {} - '@types/unist@3.0.3': {} '@types/web-bluetooth@0.0.21': {} @@ -4117,18 +2931,6 @@ snapshots: '@typescript-eslint/types': 8.70.1 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-vue-jsx@5.1.1(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3))': - dependencies: - '@babel/core': 7.28.5 - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) - '@rolldown/pluginutils': 1.0.0-beta.47 - '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.5) - vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1) - vue: 3.5.23(typescript@5.9.3) - transitivePeerDependencies: - - supports-color - '@vitejs/plugin-vue@6.0.1(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.29 @@ -4158,35 +2960,6 @@ snapshots: path-browserify: 1.0.1 vscode-uri: 3.1.0 - '@vue/babel-helper-vue-transform-on@1.5.0': {} - - '@vue/babel-plugin-jsx@1.5.0(@babel/core@7.28.5)': - dependencies: - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - '@vue/babel-helper-vue-transform-on': 1.5.0 - '@vue/babel-plugin-resolve-type': 1.5.0(@babel/core@7.28.5) - '@vue/shared': 3.5.23 - optionalDependencies: - '@babel/core': 7.28.5 - transitivePeerDependencies: - - supports-color - - '@vue/babel-plugin-resolve-type@1.5.0(@babel/core@7.28.5)': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/parser': 7.28.5 - '@vue/compiler-sfc': 3.5.23 - transitivePeerDependencies: - - supports-color - '@vue/compiler-core@3.5.23': dependencies: '@babel/parser': 7.28.5 @@ -4223,18 +2996,6 @@ snapshots: dependencies: '@vue/devtools-kit': 7.7.7 - '@vue/devtools-core@8.0.5(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3))': - dependencies: - '@vue/devtools-kit': 8.0.5 - '@vue/devtools-shared': 8.0.5 - mitt: 3.0.1 - nanoid: 5.1.6 - pathe: 2.0.3 - vite-hot-client: 2.1.0(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)) - vue: 3.5.23(typescript@5.9.3) - transitivePeerDependencies: - - vite - '@vue/devtools-kit@7.7.7': dependencies: '@vue/devtools-shared': 7.7.7 @@ -4245,24 +3006,10 @@ snapshots: speakingurl: 14.0.1 superjson: 2.2.5 - '@vue/devtools-kit@8.0.5': - dependencies: - '@vue/devtools-shared': 8.0.5 - birpc: 2.7.0 - hookable: 5.5.3 - mitt: 3.0.1 - perfect-debounce: 2.0.0 - speakingurl: 14.0.1 - superjson: 2.2.5 - '@vue/devtools-shared@7.7.7': dependencies: rfdc: 1.4.1 - '@vue/devtools-shared@8.0.5': - dependencies: - rfdc: 1.4.1 - '@vue/language-core@3.1.3(typescript@5.9.3)': dependencies: '@volar/language-core': 2.4.23 @@ -4329,6 +3076,12 @@ snapshots: acorn@8.18.0: {} + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -4336,61 +3089,38 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.17.1: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - alien-signals@3.0.6: {} - animate.css@4.1.1: {} - - ansi-regex@5.0.1: {} - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansis@4.2.0: {} - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - are-docs-informative@0.0.2: {} argparse@2.0.1: {} - array-union@2.1.0: {} - - astral-regex@2.0.0: {} + async-validator@4.2.5: {} asynckit@0.4.0: {} - axios@0.27.2: + axios@1.20.0: dependencies: - follow-redirects: 1.15.11 - form-data: 4.0.4 + follow-redirects: 1.16.1 + form-data: 4.0.6 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 transitivePeerDependencies: - debug - - b-tween@0.3.3: {} - - b-validate@1.5.3: {} + - supports-color balanced-match@1.0.2: {} - balanced-match@2.0.0: {} - balanced-match@4.0.4: {} baseline-browser-mapping@2.8.25: {} - binary-extensions@2.3.0: {} - birpc@2.7.0: {} boolbase@1.0.0: {} @@ -4407,6 +3137,7 @@ snapshots: braces@3.0.3: dependencies: fill-range: 7.1.1 + optional: true browserslist@4.27.0: dependencies: @@ -4418,21 +3149,8 @@ snapshots: builtin-modules@5.0.0: {} - bundle-name@4.1.0: - dependencies: - run-applescript: 7.1.0 - cac@7.0.0: {} - cacheable@2.1.1: - dependencies: - '@cacheable/memoize': 2.0.3 - '@cacheable/memory': 2.0.4 - '@cacheable/utils': 2.2.0 - hookified: 1.12.2 - keyv: 5.5.3 - qified: 0.5.1 - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -4453,18 +3171,6 @@ snapshots: character-entities@2.0.2: {} - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -4475,30 +3181,12 @@ snapshots: dependencies: escape-string-regexp: 1.0.5 - color-convert@1.9.3: - dependencies: - color-name: 1.1.3 - color-convert@2.0.1: dependencies: color-name: 1.1.4 - color-name@1.1.3: {} - color-name@1.1.4: {} - color-string@1.9.1: - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.4 - - color@3.2.1: - dependencies: - color-convert: 1.9.3 - color-string: 1.9.1 - - colord@2.9.3: {} - combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -4509,19 +3197,16 @@ snapshots: comment-parser@1.4.7: {} - compute-scroll-into-view@1.0.20: {} - concat-map@0.0.1: {} confbox@0.1.8: {} confbox@0.2.2: {} - convert-source-map@2.0.0: {} - copy-anything@2.0.6: dependencies: is-what: 3.14.1 + optional: true copy-anything@4.0.5: dependencies: @@ -4531,32 +3216,29 @@ snapshots: dependencies: browserslist: 4.27.0 - cosmiconfig@9.0.0(typescript@5.9.3): - dependencies: - env-paths: 2.2.1 - import-fresh: 3.3.1 - js-yaml: 4.1.0 - parse-json: 5.2.0 - optionalDependencies: - typescript: 5.9.3 - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - css-functions-list@3.2.3: {} - - css-tree@3.1.0: + css-render@0.15.14: dependencies: - mdn-data: 2.12.2 - source-map-js: 1.2.1 + '@emotion/hash': 0.8.0 + csstype: 3.0.11 cssesc@3.0.0: {} + csstype@3.0.11: {} + csstype@3.1.3: {} + date-fns-tz@3.2.0(date-fns@4.4.0): + dependencies: + date-fns: 4.4.0 + + date-fns@4.4.0: {} + dayjs@1.11.19: {} debug@4.4.3: @@ -4569,15 +3251,6 @@ snapshots: deep-is@0.1.4: {} - default-browser-id@5.0.1: {} - - default-browser@5.4.0: - dependencies: - bundle-name: 4.1.0 - default-browser-id: 5.0.1 - - define-lazy-prop@3.0.0: {} - defu@6.1.4: {} delayed-stream@1.0.0: {} @@ -4593,43 +3266,14 @@ snapshots: diff-sequences@29.6.3: {} - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - dom-serializer@2.0.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - entities: 4.5.0 - - domelementtype@2.3.0: {} - - domhandler@5.0.3: - dependencies: - domelementtype: 2.3.0 - - domutils@3.2.2: - dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 gopd: 1.2.0 - echarts@6.1.0: - dependencies: - tslib: 2.3.0 - zrender: 6.1.0 - electron-to-chromium@1.5.245: {} - emoji-regex@8.0.0: {} - empathic@2.0.0: {} empathic@2.1.0: {} @@ -4641,19 +3285,11 @@ snapshots: entities@4.5.0: {} - env-paths@2.2.1: {} - errno@0.1.8: dependencies: prr: 1.0.1 optional: true - error-ex@1.3.4: - dependencies: - is-arrayish: 0.2.1 - - error-stack-parser-es@1.0.5: {} - es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -4667,7 +3303,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 esbuild@0.25.12: optionalDependencies: @@ -4993,24 +3629,14 @@ snapshots: estree-walker@2.0.2: {} - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.8 - esutils@2.0.3: {} + evtd@0.2.4: {} + exsolve@1.0.7: {} fast-deep-equal@3.1.3: {} - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} @@ -5021,18 +3647,10 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-uri@3.1.0: {} - fast-wrap-ansi@0.2.2: dependencies: fast-string-width: 3.0.2 - fastest-levenshtein@1.0.16: {} - - fastq@1.19.1: - dependencies: - reusify: 1.1.0 - fault@2.0.1: dependencies: format: 0.2.2 @@ -5045,10 +3663,6 @@ snapshots: optionalDependencies: picomatch: 4.0.7 - file-entry-cache@10.1.4: - dependencies: - flat-cache: 6.1.18 - file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -5056,6 +3670,7 @@ snapshots: fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 + optional: true find-up-simple@1.0.1: {} @@ -5069,39 +3684,25 @@ snapshots: flatted: 3.3.3 keyv: 4.5.4 - flat-cache@6.1.18: - dependencies: - cacheable: 2.1.1 - flatted: 3.3.3 - hookified: 1.12.2 - flatted@3.3.3: {} - follow-redirects@1.15.11: {} + follow-redirects@1.16.1: {} - form-data@4.0.4: + form-data@4.0.6: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 - hasown: 2.0.2 + hasown: 2.0.4 mime-types: 2.1.35 format@0.2.2: {} - fs-extra@10.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.0 - universalify: 2.0.1 - fsevents@2.3.3: optional: true function-bind@1.1.2: {} - gensync@1.0.0-beta.2: {} - get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -5112,7 +3713,7 @@ snapshots: get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.2 + hasown: 2.0.4 math-intrinsics: 1.1.0 get-proto@1.0.1: @@ -5126,24 +3727,10 @@ snapshots: github-slugger@2.0.0: {} - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - glob-parent@6.0.2: dependencies: is-glob: 4.0.3 - global-modules@2.0.0: - dependencies: - global-prefix: 3.0.0 - - global-prefix@3.0.0: - dependencies: - ini: 1.3.8 - kind-of: 6.0.3 - which: 1.3.1 - globals@14.0.0: {} globals@15.15.0: {} @@ -5152,17 +3739,6 @@ snapshots: globals@17.12.0: {} - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - - globjoin@0.1.4: {} - globrex@0.1.2: {} gopd@1.2.0: {} @@ -5177,24 +3753,22 @@ snapshots: dependencies: has-symbols: 1.1.0 - hasown@2.0.2: + hasown@2.0.4: dependencies: function-bind: 1.1.2 - hookable@5.5.3: {} + highlight.js@11.12.0: {} - hookified@1.12.2: {} + hookable@5.5.3: {} html-entities@2.6.0: {} - html-tags@3.3.1: {} - - htmlparser2@8.0.2: + https-proxy-agent@5.0.1: dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - entities: 4.5.0 + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color iconv-lite@0.6.3: dependencies: @@ -5215,58 +3789,30 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - import-meta-resolve@4.2.0: {} - imurmurhash@0.1.4: {} indent-string@5.0.0: {} - ini@1.3.8: {} - - is-arrayish@0.2.1: {} - - is-arrayish@0.3.4: {} - - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - is-builtin-module@5.0.0: dependencies: builtin-modules: 5.0.0 - is-docker@3.0.0: {} - is-extglob@2.1.1: {} - is-fullwidth-code-point@3.0.0: {} - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - is-inside-container@1.0.0: - dependencies: - is-docker: 3.0.0 + is-number@7.0.0: + optional: true - is-number@7.0.0: {} - - is-plain-object@5.0.0: {} - - is-what@3.14.1: {} + is-what@3.14.1: + optional: true is-what@5.5.0: {} - is-wsl@3.1.0: - dependencies: - is-inside-container: 1.0.0 - isexe@2.0.0: {} - js-tokens@4.0.0: {} - - js-tokens@9.0.1: {} - js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -5284,44 +3830,20 @@ snapshots: json-buffer@3.0.1: {} - json-parse-even-better-errors@2.3.1: {} - json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: {} - json-stable-stringify-without-jsonify@1.0.1: {} - json5@2.2.3: {} - jsonc-eslint-parser@3.3.0: dependencies: acorn: 8.15.0 eslint-visitor-keys: 5.0.1 verkit: 0.3.2 - jsonfile@6.2.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - keyv@4.5.4: dependencies: json-buffer: 3.0.1 - keyv@5.5.3: - dependencies: - '@keyv/serialize': 1.1.1 - - kind-of@6.0.3: {} - - known-css-properties@0.36.0: {} - - known-css-properties@0.37.0: {} - - kolorist@1.8.0: {} - less@4.4.2: dependencies: copy-anything: 2.0.6 @@ -5335,14 +3857,13 @@ snapshots: mime: 1.6.0 needle: 3.3.1 source-map: 0.6.1 + optional: true levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 - lines-and-columns@1.2.4: {} - local-pkg@1.1.2: dependencies: mlly: 1.8.0 @@ -5357,14 +3878,10 @@ snapshots: lodash.merge@4.6.2: {} - lodash.truncate@4.4.2: {} + lodash@4.18.1: {} longest-streak@3.1.0: {} - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -5379,8 +3896,6 @@ snapshots: math-intrinsics@1.1.0: {} - mathml-tag-names@2.1.3: {} - mdast-util-find-and-replace@3.0.2: dependencies: '@types/mdast': 4.0.4 @@ -5494,14 +4009,6 @@ snapshots: dependencies: '@types/mdast': 4.0.4 - mdn-data@2.12.2: {} - - mdn-data@2.25.0: {} - - meow@13.2.0: {} - - merge2@1.4.1: {} - micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.2.0 @@ -5704,6 +4211,7 @@ snapshots: dependencies: braces: 3.0.3 picomatch: 2.3.1 + optional: true mime-db@1.52.0: {} @@ -5733,15 +4241,33 @@ snapshots: module-replacements@2.11.0: {} - mrmime@2.0.1: {} - ms@2.1.3: {} muggle-string@0.4.1: {} - nanoid@3.3.11: {} + naive-ui@2.45.3(vue@3.5.23(typescript@5.9.3)): + dependencies: + '@css-render/plugin-bem': 0.15.14(css-render@0.15.14) + '@css-render/vue3-ssr': 0.15.14(vue@3.5.23(typescript@5.9.3)) + '@types/lodash': 4.17.20 + '@types/lodash-es': 4.17.12 + async-validator: 4.2.5 + css-render: 0.15.14 + csstype: 3.1.3 + date-fns: 4.4.0 + date-fns-tz: 3.2.0(date-fns@4.4.0) + evtd: 0.2.4 + highlight.js: 11.12.0 + lodash: 4.18.1 + lodash-es: 4.17.21 + seemly: 0.3.10 + treemate: 0.3.11 + vdirs: 0.1.8(vue@3.5.23(typescript@5.9.3)) + vooks: 0.2.12(vue@3.5.23(typescript@5.9.3)) + vue: 3.5.23(typescript@5.9.3) + vueuc: 0.4.66(vue@3.5.23(typescript@5.9.3)) - nanoid@5.1.6: {} + nanoid@3.3.11: {} natural-compare@1.4.0: {} @@ -5758,27 +4284,12 @@ snapshots: node-releases@2.0.27: {} - normalize-path@3.0.0: {} - - nprogress@0.2.0: {} - nth-check@2.1.1: dependencies: boolbase: 1.0.0 - number-precision@1.6.0: {} - object-deep-merge@2.0.1: {} - ohash@2.0.11: {} - - open@10.2.0: - dependencies: - default-browser: 5.4.0 - define-lazy-prop: 3.0.0 - is-inside-container: 1.0.0 - wsl-utils: 0.1.0 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -5798,8 +4309,6 @@ snapshots: package-manager-detector@1.5.0: {} - package-manager-detector@1.8.0: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -5810,14 +4319,8 @@ snapshots: dependencies: parse-statements: 1.0.11 - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.27.1 - error-ex: 1.3.4 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - - parse-node-version@1.0.1: {} + parse-node-version@1.0.1: + optional: true parse-statements@1.0.11: {} @@ -5827,17 +4330,14 @@ snapshots: path-key@3.1.1: {} - path-type@4.0.0: {} - pathe@2.0.3: {} perfect-debounce@1.0.0: {} - perfect-debounce@2.0.0: {} - picocolors@1.1.1: {} - picomatch@2.3.1: {} + picomatch@2.3.1: + optional: true picomatch@4.0.3: {} @@ -5877,45 +4377,11 @@ snapshots: dependencies: yaml: 2.9.1 - postcss-html@1.8.0: - dependencies: - htmlparser2: 8.0.2 - js-tokens: 9.0.1 - postcss: 8.5.6 - postcss-safe-parser: 6.0.0(postcss@8.5.6) - - postcss-media-query-parser@0.2.3: {} - - postcss-resolve-nested-selector@0.1.6: {} - - postcss-safe-parser@6.0.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - postcss-safe-parser@7.0.1(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - postcss-scss@4.0.9(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - postcss-selector-parser@7.1.0: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - postcss-selector-parser@7.1.6: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-sorting@9.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - postcss-value-parser@4.2.0: {} - postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -5924,23 +4390,15 @@ snapshots: prelude-ls@1.2.1: {} + proxy-from-env@2.1.0: {} + prr@1.0.1: optional: true punycode@2.3.1: {} - qified@0.5.1: - dependencies: - hookified: 1.12.2 - quansync@0.2.11: {} - queue-microtask@1.2.3: {} - - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - readdirp@4.1.2: {} refa@0.12.1: @@ -5958,20 +4416,12 @@ snapshots: dependencies: jsesc: 3.1.0 - require-from-string@2.0.2: {} - reserved-identifiers@1.2.0: {} - resize-observer-polyfill@1.5.1: {} - resolve-from@4.0.0: {} - resolve-from@5.0.0: {} - resolve-pkg-maps@1.0.0: {} - reusify@1.1.0: {} - rfdc@1.4.1: {} rollup@4.52.5: @@ -6002,12 +4452,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.52.5 fsevents: 2.3.3 - run-applescript@7.1.0: {} - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - safer-buffer@2.1.2: optional: true @@ -6022,23 +4466,17 @@ snapshots: sax@1.4.3: optional: true - scroll-into-view-if-needed@2.2.31: - dependencies: - compute-scroll-into-view: 1.0.20 - scslre@0.3.0: dependencies: '@eslint-community/regexpp': 4.12.2 refa: 0.12.1 regexp-ast-analysis: 0.7.1 - scule@1.3.0: {} + seemly@0.3.10: {} semver@5.7.2: optional: true - semver@6.3.1: {} - semver@7.7.3: {} semver@7.8.5: {} @@ -6049,30 +4487,8 @@ snapshots: shebang-regex@3.0.0: {} - signal-exit@4.1.0: {} - - simple-swizzle@0.2.4: - dependencies: - is-arrayish: 0.3.4 - - sirv@3.0.2: - dependencies: - '@polka/url': 1.0.0-next.29 - mrmime: 2.0.1 - totalist: 3.0.1 - sisteransi@1.0.5: {} - slash@3.0.0: {} - - slice-ansi@4.0.0: - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - - sortablejs@1.15.6: {} - source-map-js@1.2.1: {} source-map@0.6.1: @@ -6089,130 +4505,10 @@ snapshots: speakingurl@14.0.1: {} - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - strip-indent@4.1.1: {} strip-json-comments@3.1.1: {} - strip-literal@3.1.0: - dependencies: - js-tokens: 9.0.1 - - stylelint-config-html@1.1.0(postcss-html@1.8.0)(stylelint@16.25.0(typescript@5.9.3)): - dependencies: - postcss-html: 1.8.0 - stylelint: 16.25.0(typescript@5.9.3) - - stylelint-config-recess-order@7.4.0(stylelint-order@7.0.0(stylelint@16.25.0(typescript@5.9.3)))(stylelint@16.25.0(typescript@5.9.3)): - dependencies: - stylelint: 16.25.0(typescript@5.9.3) - stylelint-order: 7.0.0(stylelint@16.25.0(typescript@5.9.3)) - - stylelint-config-recommended-scss@16.0.2(postcss@8.5.6)(stylelint@16.25.0(typescript@5.9.3)): - dependencies: - postcss-scss: 4.0.9(postcss@8.5.6) - stylelint: 16.25.0(typescript@5.9.3) - stylelint-config-recommended: 17.0.0(stylelint@16.25.0(typescript@5.9.3)) - stylelint-scss: 6.12.1(stylelint@16.25.0(typescript@5.9.3)) - optionalDependencies: - postcss: 8.5.6 - - stylelint-config-recommended-vue@1.6.1(postcss-html@1.8.0)(stylelint@16.25.0(typescript@5.9.3)): - dependencies: - postcss-html: 1.8.0 - semver: 7.7.3 - stylelint: 16.25.0(typescript@5.9.3) - stylelint-config-html: 1.1.0(postcss-html@1.8.0)(stylelint@16.25.0(typescript@5.9.3)) - stylelint-config-recommended: 17.0.0(stylelint@16.25.0(typescript@5.9.3)) - - stylelint-config-recommended@17.0.0(stylelint@16.25.0(typescript@5.9.3)): - dependencies: - stylelint: 16.25.0(typescript@5.9.3) - - stylelint-config-standard-scss@16.0.0(postcss@8.5.6)(stylelint@16.25.0(typescript@5.9.3)): - dependencies: - stylelint: 16.25.0(typescript@5.9.3) - stylelint-config-recommended-scss: 16.0.2(postcss@8.5.6)(stylelint@16.25.0(typescript@5.9.3)) - stylelint-config-standard: 39.0.1(stylelint@16.25.0(typescript@5.9.3)) - optionalDependencies: - postcss: 8.5.6 - - stylelint-config-standard@39.0.1(stylelint@16.25.0(typescript@5.9.3)): - dependencies: - stylelint: 16.25.0(typescript@5.9.3) - stylelint-config-recommended: 17.0.0(stylelint@16.25.0(typescript@5.9.3)) - - stylelint-order@7.0.0(stylelint@16.25.0(typescript@5.9.3)): - dependencies: - postcss: 8.5.6 - postcss-sorting: 9.1.0(postcss@8.5.6) - stylelint: 16.25.0(typescript@5.9.3) - - stylelint-scss@6.12.1(stylelint@16.25.0(typescript@5.9.3)): - dependencies: - css-tree: 3.1.0 - is-plain-object: 5.0.0 - known-css-properties: 0.36.0 - mdn-data: 2.25.0 - postcss-media-query-parser: 0.2.3 - postcss-resolve-nested-selector: 0.1.6 - postcss-selector-parser: 7.1.0 - postcss-value-parser: 4.2.0 - stylelint: 16.25.0(typescript@5.9.3) - - stylelint@16.25.0(typescript@5.9.3): - dependencies: - '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': 3.0.4 - '@csstools/media-query-list-parser': 4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0) - '@dual-bundle/import-meta-resolve': 4.2.1 - balanced-match: 2.0.0 - colord: 2.9.3 - cosmiconfig: 9.0.0(typescript@5.9.3) - css-functions-list: 3.2.3 - css-tree: 3.1.0 - debug: 4.4.3 - fast-glob: 3.3.3 - fastest-levenshtein: 1.0.16 - file-entry-cache: 10.1.4 - global-modules: 2.0.0 - globby: 11.1.0 - globjoin: 0.1.4 - html-tags: 3.3.1 - ignore: 7.0.5 - imurmurhash: 0.1.4 - is-plain-object: 5.0.0 - known-css-properties: 0.37.0 - mathml-tag-names: 2.1.3 - meow: 13.2.0 - micromatch: 4.0.8 - normalize-path: 3.0.0 - picocolors: 1.1.1 - postcss: 8.5.6 - postcss-resolve-nested-selector: 0.1.6 - postcss-safe-parser: 7.0.1(postcss@8.5.6) - postcss-selector-parser: 7.1.0 - postcss-value-parser: 4.2.0 - resolve-from: 5.0.0 - string-width: 4.2.3 - supports-hyperlinks: 3.2.0 - svg-tags: 1.0.0 - table: 6.9.0 - write-file-atomic: 5.0.1 - transitivePeerDependencies: - - supports-color - - typescript - superjson@2.2.5: dependencies: copy-anything: 4.0.5 @@ -6221,31 +4517,14 @@ snapshots: dependencies: has-flag: 4.0.0 - supports-hyperlinks@3.2.0: - dependencies: - has-flag: 4.0.0 - supports-color: 7.2.0 - - svg-tags@1.0.0: {} - synckit@0.11.13: dependencies: '@pkgr/core': 0.3.6 - table@6.9.0: - dependencies: - ajv: 8.17.1 - lodash.truncate: 4.4.2 - slice-ansi: 4.0.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - tapable@2.3.0: {} tinyexec@1.0.2: {} - tinyexec@1.3.1: {} - tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) @@ -6259,6 +4538,7 @@ snapshots: to-regex-range@5.0.1: dependencies: is-number: 7.0.0 + optional: true to-valid-identifier@1.0.0: dependencies: @@ -6269,7 +4549,7 @@ snapshots: dependencies: eslint-visitor-keys: 5.0.1 - totalist@3.0.1: {} + treemate@0.3.11: {} ts-api-utils@2.5.0(typescript@5.9.3): dependencies: @@ -6280,9 +4560,8 @@ snapshots: picomatch: 4.0.3 typescript: 5.9.3 - tslib@2.3.0: {} - - tslib@2.8.1: {} + tslib@2.8.1: + optional: true type-check@0.4.0: dependencies: @@ -6296,23 +4575,6 @@ snapshots: undici-types@8.9.0: {} - unimport@5.5.0: - dependencies: - acorn: 8.15.0 - escape-string-regexp: 5.0.0 - estree-walker: 3.0.3 - local-pkg: 1.1.2 - magic-string: 0.30.21 - mlly: 1.8.0 - pathe: 2.0.3 - picomatch: 4.0.3 - pkg-types: 2.3.0 - scule: 1.3.0 - strip-literal: 3.1.0 - tinyglobby: 0.2.15 - unplugin: 2.3.10 - unplugin-utils: 0.3.1 - unist-util-is@6.0.1: dependencies: '@types/unist': 3.0.3 @@ -6332,59 +4594,6 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - universalify@2.0.1: {} - - unplugin-auto-import@20.2.0(@vueuse/core@13.9.0(vue@3.5.23(typescript@5.9.3))): - dependencies: - local-pkg: 1.1.2 - magic-string: 0.30.21 - picomatch: 4.0.3 - unimport: 5.5.0 - unplugin: 2.3.10 - unplugin-utils: 0.3.1 - optionalDependencies: - '@vueuse/core': 13.9.0(vue@3.5.23(typescript@5.9.3)) - - unplugin-icons@22.5.0(@vue/compiler-sfc@3.5.23): - dependencies: - '@antfu/install-pkg': 1.1.0 - '@iconify/utils': 3.1.7 - debug: 4.4.3 - local-pkg: 1.1.2 - unplugin: 2.3.10 - optionalDependencies: - '@vue/compiler-sfc': 3.5.23 - transitivePeerDependencies: - - supports-color - - unplugin-utils@0.3.1: - dependencies: - pathe: 2.0.3 - picomatch: 4.0.3 - - unplugin-vue-components@29.2.0(@babel/parser@7.28.5)(vue@3.5.23(typescript@5.9.3)): - dependencies: - chokidar: 3.6.0 - debug: 4.4.3 - local-pkg: 1.1.2 - magic-string: 0.30.21 - mlly: 1.8.0 - tinyglobby: 0.2.15 - unplugin: 2.3.10 - unplugin-utils: 0.3.1 - vue: 3.5.23(typescript@5.9.3) - optionalDependencies: - '@babel/parser': 7.28.5 - transitivePeerDependencies: - - supports-color - - unplugin@2.3.10: - dependencies: - '@jridgewell/remapping': 2.3.5 - acorn: 8.15.0 - picomatch: 4.0.3 - webpack-virtual-modules: 0.6.2 - update-browserslist-db@1.1.4(browserslist@4.27.0): dependencies: browserslist: 4.27.0 @@ -6397,71 +4606,13 @@ snapshots: util-deprecate@1.0.2: {} + vdirs@0.1.8(vue@3.5.23(typescript@5.9.3)): + dependencies: + evtd: 0.2.4 + vue: 3.5.23(typescript@5.9.3) + verkit@0.3.2: {} - vite-dev-rpc@1.1.0(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)): - dependencies: - birpc: 2.7.0 - vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1) - vite-hot-client: 2.1.0(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)) - - vite-hot-client@2.1.0(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)): - dependencies: - vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1) - - vite-plugin-compression@0.5.1(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)): - dependencies: - chalk: 4.1.2 - debug: 4.4.3 - fs-extra: 10.1.0 - vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1) - transitivePeerDependencies: - - supports-color - - vite-plugin-inspect@11.3.3(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)): - dependencies: - ansis: 4.2.0 - debug: 4.4.3 - error-stack-parser-es: 1.0.5 - ohash: 2.0.11 - open: 10.2.0 - perfect-debounce: 2.0.0 - sirv: 3.0.2 - unplugin-utils: 0.3.1 - vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1) - vite-dev-rpc: 1.1.0(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)) - transitivePeerDependencies: - - supports-color - - vite-plugin-vue-devtools@8.0.5(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3)): - dependencies: - '@vue/devtools-core': 8.0.5(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1))(vue@3.5.23(typescript@5.9.3)) - '@vue/devtools-kit': 8.0.5 - '@vue/devtools-shared': 8.0.5 - sirv: 3.0.2 - vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1) - vite-plugin-inspect: 11.3.3(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)) - vite-plugin-vue-inspector: 5.3.2(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)) - transitivePeerDependencies: - - '@nuxt/kit' - - supports-color - - vue - - vite-plugin-vue-inspector@5.3.2(vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1)): - dependencies: - '@babel/core': 7.28.5 - '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.5) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.5) - '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) - '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.5) - '@vue/compiler-dom': 3.5.23 - kolorist: 1.8.0 - magic-string: 0.30.21 - vite: 7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1) - transitivePeerDependencies: - - supports-color - vite@7.2.1(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)(yaml@2.9.1): dependencies: esbuild: 0.25.12 @@ -6477,15 +4628,12 @@ snapshots: sass: 1.93.3 yaml: 2.9.1 - vscode-uri@3.1.0: {} - - vue-color-kit@1.0.6(vue@3.5.23(typescript@5.9.3)): + vooks@0.2.12(vue@3.5.23(typescript@5.9.3)): dependencies: + evtd: 0.2.4 vue: 3.5.23(typescript@5.9.3) - vue-draggable-plus@0.6.0(@types/sortablejs@1.15.9): - dependencies: - '@types/sortablejs': 1.15.9 + vscode-uri@3.1.0: {} vue-eslint-parser@10.4.1(eslint@9.39.1): dependencies: @@ -6520,11 +4668,16 @@ snapshots: optionalDependencies: typescript: 5.9.3 - webpack-virtual-modules@0.6.2: {} - - which@1.3.1: + vueuc@0.4.66(vue@3.5.23(typescript@5.9.3)): dependencies: - isexe: 2.0.0 + '@css-render/vue3-ssr': 0.15.14(vue@3.5.23(typescript@5.9.3)) + '@juggle/resize-observer': 3.4.0 + css-render: 0.15.14 + evtd: 0.2.4 + seemly: 0.3.10 + vdirs: 0.1.8(vue@3.5.23(typescript@5.9.3)) + vooks: 0.2.12(vue@3.5.23(typescript@5.9.3)) + vue: 3.5.23(typescript@5.9.3) which@2.0.2: dependencies: @@ -6532,21 +4685,8 @@ snapshots: word-wrap@1.2.5: {} - write-file-atomic@5.0.1: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 4.1.0 - - wsl-utils@0.1.0: - dependencies: - is-wsl: 3.1.0 - - xe-utils@3.7.9: {} - xml-name-validator@5.0.0: {} - yallist@3.1.1: {} - yaml-eslint-parser@2.1.0: dependencies: eslint-visitor-keys: 5.0.1 @@ -6558,8 +4698,4 @@ snapshots: yocto-queue@0.1.0: {} - zrender@6.1.0: - dependencies: - tslib: 2.3.0 - zwitch@2.0.4: {} diff --git a/frontend-pc/public/favicon.svg b/frontend-pc/public/favicon.svg index b7fd708..7b23aaa 100644 --- a/frontend-pc/public/favicon.svg +++ b/frontend-pc/public/favicon.svg @@ -1,19 +1,21 @@ - - 填 + + + + + + + diff --git a/frontend-pc/src/App.vue b/frontend-pc/src/App.vue index 7494249..09f7201 100644 --- a/frontend-pc/src/App.vue +++ b/frontend-pc/src/App.vue @@ -1,31 +1,74 @@ - - - + diff --git a/frontend-pc/src/apis/area/index.ts b/frontend-pc/src/apis/area/index.ts deleted file mode 100644 index f526bff..0000000 --- a/frontend-pc/src/apis/area/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type * as T from './type' -import http from '@/utils/http' - -export type * from './type' - -/** 获取地区列表 */ -export const getProvinceCityArea = (params: { type: 'province' | 'city' | 'area', code?: string }) => { - return http.get('/area/getProvinceCityArea', params) -} diff --git a/frontend-pc/src/apis/area/type.ts b/frontend-pc/src/apis/area/type.ts deleted file mode 100644 index f129e36..0000000 --- a/frontend-pc/src/apis/area/type.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface AreaItem { - label: string - code: string - children?: AreaItem[] -} diff --git a/frontend-pc/src/apis/base.ts b/frontend-pc/src/apis/base.ts deleted file mode 100644 index cfc05ec..0000000 --- a/frontend-pc/src/apis/base.ts +++ /dev/null @@ -1,39 +0,0 @@ -import http from '@/utils/http' - -interface DefaultP { - GetListParams?: Record - GetDetailParams?: { id: string } - AddParams?: Record - UpdateParams?: Record - DeleteParams?: { ids: string[] } -} - -// 获取基础接口 -export function getBaseApi(params: { baseUrl: string }) { - const { baseUrl } = params - - const baseApi = { - // 获取分页列表 - getList(params?: P['GetListParams'] & { page?: number, size?: number }) { - return http.get>(`${baseUrl}/getList`, params) - }, - // 获取详情 - getDetail(params: P['GetDetailParams']) { - return http.get(`${baseUrl}/getDetail`, params) - }, - // 新增 - add(params: P['AddParams']) { - return http.post(`${baseUrl}/add`, params) - }, - // 修改 - update(params: P['UpdateParams']) { - return http.post(`${baseUrl}/update`, params) - }, - // 删除 - delete(params: P['DeleteParams']) { - return http.post(`${baseUrl}/delete`, params) - } - } - - return baseApi -} diff --git a/frontend-pc/src/apis/cate/index.ts b/frontend-pc/src/apis/cate/index.ts deleted file mode 100644 index c1aa836..0000000 --- a/frontend-pc/src/apis/cate/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type * as T from './type' -import http from '@/utils/http' - -export type * from './type' - -/** 获取分类树 */ -export function getCateTree() { - return http.get('/cate/getCateTree') -} diff --git a/frontend-pc/src/apis/cate/type.ts b/frontend-pc/src/apis/cate/type.ts deleted file mode 100644 index 8d97442..0000000 --- a/frontend-pc/src/apis/cate/type.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface CateTreeItem { - id: string - name: string - pid: string | null - type: string | null - disabled: boolean - children: CateTreeItem[] -} diff --git a/frontend-pc/src/apis/file/index.ts b/frontend-pc/src/apis/file/index.ts deleted file mode 100644 index 31923bb..0000000 --- a/frontend-pc/src/apis/file/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type * as T from './type' -import http from '@/utils/http' - -export type * from './type' - -/** 获取文件列表 */ -export function getFileList(params: { fileType: string | number }) { - return http.get>('/file/getFileList', params) -} diff --git a/frontend-pc/src/apis/file/type.ts b/frontend-pc/src/apis/file/type.ts deleted file mode 100644 index fd58c7e..0000000 --- a/frontend-pc/src/apis/file/type.ts +++ /dev/null @@ -1,13 +0,0 @@ -// 文件 -export interface FileItem { - id: string - type: string - name: string - extendName: string - src: string - updateTime: string - isDir: boolean - filePath: string - size: number - [propName: string]: any // 一个 interface 中任意属性只能有一个 -} diff --git a/frontend-pc/src/apis/person/index.ts b/frontend-pc/src/apis/person/index.ts deleted file mode 100644 index 8b6959d..0000000 --- a/frontend-pc/src/apis/person/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type * as T from './type' -import { getBaseApi } from '@/apis/base' - -export type * from './type' - -/** 人物模块 */ -export const baseAPI = getBaseApi({ baseUrl: '/person' }) diff --git a/frontend-pc/src/apis/person/type.ts b/frontend-pc/src/apis/person/type.ts deleted file mode 100644 index bbfae6b..0000000 --- a/frontend-pc/src/apis/person/type.ts +++ /dev/null @@ -1,16 +0,0 @@ -// 表格项目 -export interface ListItem { - id: string - name: string - account: string - avatar: string - gender: Gender - phone: string - email: string - createTime: string - address: string - age: number - status: Status - hobby: string[] - remark: string -} diff --git a/frontend-pc/src/apis/system/dept.ts b/frontend-pc/src/apis/system/dept.ts deleted file mode 100644 index edba807..0000000 --- a/frontend-pc/src/apis/system/dept.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { getBaseApi } from '@/apis/base' - -export interface ListItem { - id: string - name: string - sort: number - status: Status - createTime: string - parentId: string - children?: ListItem[] - description: string -} - -/** 部门模块 */ -export const baseAPI = getBaseApi({ baseUrl: '/system/dept' }) diff --git a/frontend-pc/src/apis/system/dict.ts b/frontend-pc/src/apis/system/dict.ts deleted file mode 100644 index 53fc692..0000000 --- a/frontend-pc/src/apis/system/dict.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { SelectOptionData } from '@arco-design/web-vue' -import { getBaseApi } from '@/apis/base' -import http from '@/utils/http' - -export interface ListItem { - id: string - createUserString: string - createTime: string - name: string - code: string - sort: number - status: Status - description: string -} - -export type DictDetail = ListItem - -export type DictDataItem = { - id: string - name: string - value: string | number - sort: number - status: Status - remark?: string - createTime?: string -} - -/** 字典模块 */ -export const baseAPI = getBaseApi({ baseUrl: '/system/dict' }) - -/** 字典数据列表 */ -export function getDictDataList(params: { code: string, name?: string, status?: string } & Pagination) { - return http.get>('/system/dict/getDictDataList', params) -} - -/** 字典数据详情 */ -export function getDictDataDetail(params: { id: string, code: string }) { - return http.get('/system/dict/getDictDataDetail', params) -} - -/** 获取字典数据映射 */ -export function getDictData() { - return http.get>('/system/dict/getDictData') -} diff --git a/frontend-pc/src/apis/system/menu.ts b/frontend-pc/src/apis/system/menu.ts deleted file mode 100644 index 689e3f6..0000000 --- a/frontend-pc/src/apis/system/menu.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { getBaseApi } from '@/apis/base' -import http from '@/utils/http' - -export interface ListItem { - activeMenu: string - alwaysShow: boolean - breadcrumb: boolean - children: ListItem[] - component: string - hidden: boolean - icon: string - id: string - keepAlive: boolean - parentId: string - path: string - permission: string - redirect: string - roles: string[] - showInTabs: boolean - sort: number - status: Status - title: string - type: 1 | 2 | 3 - affix: boolean -} - -export interface MenuOptionsItem { - id: string - title: string - children: MenuOptionsItem[] -} - -/** 菜单模块 */ -export const baseAPI = getBaseApi({ baseUrl: '/system/menu' }) - -/** 获取角色分配权限菜单树 */ -export function getMenuOptions() { - return http.get('/system/menu/getMenuOptions') -} diff --git a/frontend-pc/src/apis/system/role.ts b/frontend-pc/src/apis/system/role.ts deleted file mode 100644 index 730d497..0000000 --- a/frontend-pc/src/apis/system/role.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { getBaseApi } from '@/apis/base' -import http from '@/utils/http' - -export interface ListItem { - id: string - createUserString: string - createTime: string - disabled: boolean - name: string - code: string - sort: number - status: Status - type: number - description: string -} - -/** 角色模块 */ -export const baseAPI = getBaseApi({ baseUrl: '/system/role' }) - -/** 获取角色权限 */ -export function getRoleMenuIds(params: { role: string }) { - return http.get('/system/role/getRoleMenuIds', params) -} diff --git a/frontend-pc/src/apis/system/user.ts b/frontend-pc/src/apis/system/user.ts deleted file mode 100644 index 1ec6607..0000000 --- a/frontend-pc/src/apis/system/user.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { getBaseApi } from '@/apis/base' - -export interface ListItem { - id: string - createUserString: string - createTime: string - disabled: boolean - deptId: string - deptName: string - username: string - nickname: string - gender: Gender - avatar: string - email: string - phone: string - status: Status - type: 1 | 2 - description: string - roleIds: string[] - roleNames: string[] -} - -/** 用户模块 */ -export const baseAPI = getBaseApi({ baseUrl: '/system/user' }) diff --git a/frontend-pc/src/apis/test/index.ts b/frontend-pc/src/apis/test/index.ts deleted file mode 100644 index 48b3a94..0000000 --- a/frontend-pc/src/apis/test/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import http from '@/utils/http' - -/** 请求成功的接口 */ -export function getTestSuccess() { - return http.get('/test/success') -} - -/** 请求失败的接口 */ -export function getTestFail() { - return http.get('/test/fail') -} diff --git a/frontend-pc/src/apis/types.ts b/frontend-pc/src/apis/types.ts new file mode 100644 index 0000000..b62da12 --- /dev/null +++ b/frontend-pc/src/apis/types.ts @@ -0,0 +1,20 @@ +/** + * 后端响应契约(《03-接口设计》§1.3)与分页形状。 + * + * ⚠️ 成功判定是 `code === 0`,**业务错误也是 HTTP 200**(只有 401/403/429/5xx 用真状态码)。 + * 这层适配在 `@/utils/http` 里做掉了:调用方拿到的直接是 `data`, + * 所以业务代码里几乎不再需要碰 `code`。 + */ + +/** 分页列表(后端恒为 { list, total }) */ +export interface PageData { + list: T[] + total: number +} + +/** 带业务码的错误(调用方按码分支时用,例如租户重名 40011) */ +export interface BizError extends Error { + bizCode?: number + bizData?: unknown + status?: number +} diff --git a/frontend-pc/src/apis/user/index.ts b/frontend-pc/src/apis/user/index.ts deleted file mode 100644 index ae6bd68..0000000 --- a/frontend-pc/src/apis/user/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type * as T from './type' -import http from '@/utils/http' - -export type * from './type' - -/** 登录 */ -export function login(data: { username: string, password: string }) { - return http.post('/user/login', data) -} - -/** 退出登录 */ -export function logout() { - return http.post('/user/logout') -} - -/** 获取用户信息 */ -export const getUserInfo = () => { - return http.get('/user/getUserInfo') -} - -/** 获取用户路由信息 */ -export const getUserRoutes = () => { - return http.get('/user/getUserRoutes') -} diff --git a/frontend-pc/src/apis/user/type.ts b/frontend-pc/src/apis/user/type.ts deleted file mode 100644 index 387384a..0000000 --- a/frontend-pc/src/apis/user/type.ts +++ /dev/null @@ -1,34 +0,0 @@ -export interface UserInfo { - id: string - nickname: string - avatar: string - roles: string[] - permissions: string[] -} - -export interface Login { - token: string -} - -export interface UserRouteItem { - activeMenu: string - alwaysShow: boolean - breadcrumb: boolean - children: UserRouteItem[] - component: string - hidden: boolean - icon: string - id: string - keepAlive: boolean - parentId: string - path: string - permission: string - redirect: string - roles: string[] - showInTabs: boolean - sort: number - status: Status - title: string - type: 1 | 2 | 3 - affix: boolean -} diff --git a/frontend-pc/src/apis/xt/audit.ts b/frontend-pc/src/apis/xt/audit.ts index 9389715..2406b57 100644 --- a/frontend-pc/src/apis/xt/audit.ts +++ b/frontend-pc/src/apis/xt/audit.ts @@ -17,11 +17,16 @@ * * ## 状态与分级(不要按直觉写分支) * - * - 填报单状态:`1` 草稿 / `2` 待初审 / `3` 待终审(初审已过)/ `4` 已驳回 / `5` 已通过 + * - **填报单状态**(`collect_submission.status`,权威见后端 `CollectSubmission` 的字段注释): + * `0` 草稿 / `1` 待初审 / `2` 待终审 / `3` 已通过 / `4` 已驳回 / `5` 已归档 + * - **名册行状态**(`collect_roster.submit_status`)是**另一套枚举**,别混用: + * `0` 未提交 / `1` 草稿 / `2` 待审 / `3` 已通过 / `4` 已驳回(见 `apis/xt/roster.ts`) * - 卡片分级 `level`:**只有 0 / 2 / 3**,没有 1(写了"信息级"的分支永远不会触发) * - * ⚠️ 上面这两个数字口径**以服务端返回的 `statusText` 为准**:界面优先显示 `statusText`, - * 数字只用于选色。这样即便枚举有调整,界面也不会说错话。 + * ⚠️ 两套枚举的**起点不同**(填报单从"草稿"起、名册从"未提交"起), + * 照抄对方必然整体错位一格 —— 本文件曾把填报单写成 `1 草稿 / 2 待初审 / 3 待终审 / 4 已驳回 / 5 已通过`, + * 结果是**状态文案被服务端 `statusText` 兜住了,但 tag 的颜色永远取自错的本地表** + * ("已通过"显示成待终审的蓝)。所以:文案优先用服务端的 `statusText`,本地表只负责选色。 */ import http from '@/utils/http' import { actingTenantConfig } from './acting-tenant' @@ -34,6 +39,22 @@ export function bizCodeOf(e: unknown): number | undefined { return (e as BizError)?.bizCode } +/** + * 填报单状态 → 文案与色词(`tagOf()` 会把它翻成 `n-tag` 的 type)。 + * + * ⚠️ 与 `roster.ts` 的 `SUBMIT_STATUS`(名册行口径)**不是同一套**:起点差一格。 + * 改这里之前先看文件头那段说明与后端 `CollectSubmission` 的字段注释。 + * 界面文案仍以服务端 `statusText` 为准,本表只负责**选色**与兜底文案。 + */ +export const SUBMISSION_STATUS: Record = { + 0: { text: '草稿', color: 'gray' }, + 1: { text: '待初审', color: 'orange' }, + 2: { text: '待终审', color: 'arcoblue' }, + 3: { text: '已通过', color: 'green' }, + 4: { text: '已驳回', color: 'red' }, + 5: { text: '已归档', color: 'gray' } +} + // ── 待办列表(§11.1 / §10.2)──────────────────── export interface AuditTodo { diff --git a/frontend-pc/src/apis/xt/cleanup.ts b/frontend-pc/src/apis/xt/cleanup.ts deleted file mode 100644 index c99be84..0000000 --- a/frontend-pc/src/apis/xt/cleanup.ts +++ /dev/null @@ -1,264 +0,0 @@ -/** - * 历史数据整理器(《03-接口设计》§13.2)。 - * - * ## 这个功能的"卖点"就是报告里的一句话 - * - * 契约里写明:**报告里最重要的一句话是 `autoHandled / needManual`** —— - * 「112 个文件里 90 个系统全自动处理,只有 1 个必须你亲自判断」。 - * 所以这一屏的核心不是"进度条",而是**那两个数字与重点清单**。 - * 进度只是等待时的安抚,报告才是客户付钱的理由。 - * - * ## 几个界面必须区分清楚的状态 - * - * | status | 界面该显示什么 | - * |---|---| - * | `0` 排队 / `1` 解析中 / `2` 处理中 | 进度条 + 已处理的文件数;**不给"下载"按钮**(还没有产物) | - * | `3` 已完成 | 报告 + 下载结果包 + 导入为正式任务 | - * | `4` 失败 | **`errorMsg` 必须显示出来** | - * - * ⚠️ 失败时 `errorMsg` 是唯一能让用户知道"为什么"的东西。常见原因是 - * "传了不是 zip 的文件"与"包里单个文件超过 50MB" —— 这两条都在服务端有明确文案, - * 界面要做的是**把它原样显示**,而不是笼统说"整理失败"。 - * - * ## 为什么 `rosterTaskId` 这个参数要在界面上强调 - * - * 契约 §13.2 的原话:**没有名册就只能做文件名规范化;有名单才能做证件号校验、 - * 疑似串档检测** —— 而"疑似串档 1 例"正是报告里最有价值的一条。 - * 也就是说:关联名册与不关联,"体检报告"是两份完全不同的东西。 - * 界面必须把这件事说在**上传之前**,而不是等报告出来发现那两项全是 0。 - */ - -import { getToken } from '@/utils/auth' -import http from '@/utils/http' - -/** 整理任务状态。 */ -export type CleanupStatus = 0 | 1 | 2 | 3 | 4 - -export interface CleanupJobVO { - id: number - jobName: string - /** `1` 压缩包 / `2` 文件夹 / `3` 云盘链接(一期只支持 `1`)。 */ - sourceType: number - /** 关联的已有名册任务。**有关联才有证件号校验与串档检测**。 */ - rosterTaskId?: number | null - status: CleanupStatus - statusText: string - progress: number - totalFiles?: number | null - processedFiles?: number | null - parsedStudents?: number | null - parsedMaterials?: number | null - rawSizeBytes?: number | null - finalSizeBytes?: number | null - /** 形如 `91.5%`;没算出来时是 `—`(**不是 0%** —— 0% 看起来像"没压缩")。 */ - compressRatio?: string - resultPath?: string | null - /** 导入成正式任务后的任务 id(有值就说明已导入,导入是幂等的)。 */ - targetTaskId?: number | null - errorMsg?: string | null - createTime?: string - finishTime?: string | null -} - -export interface CleanupJobPage { - list: CleanupJobVO[] - total: number -} - -/** 报告汇总。 */ -export interface ReportSummary { - totalFiles: number - nameInvalid: number - sizeExceeded: number - formatConverted: number - idcardMismatch: number - idcardAutoFixed: number - idcardNeedConfirm: number - /** 疑似串档:某人的**多项**材料证件号与名册均不同、且彼此一致 → 整个文件夹可能被换过。 */ - crossFileSuspected: number - lowResolution: number - orphanImages: number -} - -export interface Highlight { - type: string - /** `1` 提示 / `2` 警告 / `3` 必须人工。 */ - level: number - rosterId?: number | null - realName?: string | null - title: string - materialCode?: string | null - detail?: string | null - suggestion?: string | null -} - -export interface NameFixSample { - original: string - fixed: string - rule?: string -} - -export interface IdcardConflict { - rosterId?: number | null - realName?: string | null - rosterValue?: string | null - fileNameValue?: string | null - verdict: string - verdictText: string -} - -export interface CleanupReportVO { - jobId: number - summary: ReportSummary - /** 系统全自动处理了多少(**服务端算的**,不让前端去减)。 */ - autoHandled: number - /** 必须人工判断的(串档 + 无法归位 + 待确认的证件号冲突)。 */ - needManual: number - highlights: Highlight[] - nameFixedSamples: NameFixSample[] - idcardConflicts: IdcardConflict[] -} - -export interface CleanupImportVO { - taskId: number - rosterCount: number - fileCount: number -} - -export function getCleanupJobs(params: { - status?: number - keyword?: string - pageNum?: number - pageSize?: number -}) { - return http.get('/cleanup/jobs', params) -} - -export function getCleanupJob(id: number) { - return http.get(`/cleanup/jobs/${id}`) -} - -export function getCleanupReport(id: number) { - return http.get(`/cleanup/jobs/${id}/report`) -} - -/** - * 创建整理任务(multipart)。 - * - * ⚠️ 超时给到 600s:大包的解压 + 解析 + 重新打包都在服务端同步做, - * 而默认 30s 在几百 MB 的包上必然超时 —— 超时的表现是"点了没反应", - * 用户会重复点,于是同一个包被整理好几次。 - */ -export function createCleanupJob(file: File, opts: { - jobName: string - rosterTaskId?: number - namingTemplate?: string -}) { - const fd = new FormData() - fd.append('file', file) - fd.append('jobName', opts.jobName) - fd.append('sourceType', '1') - if (opts.rosterTaskId !== undefined) { - fd.append('rosterTaskId', String(opts.rosterTaskId)) - } - if (opts.namingTemplate) { - fd.append('namingTemplate', opts.namingTemplate) - } - // 不要手动设 Content-Type:axios 会带着 boundary 自己设 - return http.request<{ jobId: number, status: number, estimatedSeconds: number }>({ - method: 'post', - url: '/cleanup/jobs', - data: fd, - timeout: 600 * 1000 - }) -} - -/** - * 导入为正式任务。 - * - * ⚠️ 这条路**不能带 `importFiles`**:一期还做不到把整理结果里的文件自动挂到填报单, - * 而服务端会**如实拒绝**(`40044`)而不是安静忽略。界面因此不要给这个勾选项 —— - * 给了一个必然报错的选项,用户只会以为系统坏了。 - */ -export function importCleanupJob(id: number, data: { - taskName: string - collectMode?: number - batchNo: string - importRoster?: boolean - majorName?: string -}) { - return http.post(`/cleanup/jobs/${id}/import`, { - collectMode: 1, - importRoster: true, - ...data - }) -} - -/** - * 真正下载整理结果包。 - * - * ⚠️ 必须带 token 取 blob 再本地触发下载:`/cleanup/jobs/{id}/result` 是要登录的, - * 直接 `window.open` / `` 都不会带 `Authorization` 头, - * 用户会看到一个 401 页面(或"下载没反应")。 - * - * 放在 api 层而不是页面里,与导出中心的 `downloadExportFile` 保持同一写法 —— - * 同一件事在一个项目里有两处实现,改的时候一定会漏一处。 - */ -export async function downloadCleanupResult(id: number, jobName?: string): Promise { - const token = getToken() - const base = import.meta.env.VITE_API_PREFIX || '/api/v1' - const res = await fetch(`${base}/cleanup/jobs/${id}/result`, { - headers: token ? { Authorization: `Bearer ${token}` } : {} - }) - if (!res.ok) { - throw new Error(`下载失败(HTTP ${res.status})`) - } - const blob = await res.blob() - const href = URL.createObjectURL(blob) - const a = document.createElement('a') - a.href = href - a.download = `${jobName || '整理结果'}-${id}.zip` - document.body.appendChild(a) - a.click() - a.remove() - URL.revokeObjectURL(href) -} - -/** 状态 → Tag 颜色(Arco 的 status 只认 normal/warning/success/danger)。 */ -export function statusColor(status: CleanupStatus): 'normal' | 'warning' | 'success' | 'danger' { - switch (status) { - case 3: - return 'success' - case 4: - return 'danger' - case 1: - case 2: - return 'warning' - default: - return 'normal' - } -} - -/** 重点条目的级别 → Tag 颜色。`3` 是"必须人工",用红色。 */ -export function levelColor(level: number): 'normal' | 'warning' | 'danger' { - if (level >= 3) return 'danger' - if (level === 2) return 'warning' - return 'normal' -} - -/** - * 报告里"最有说服力的一句话"。 - * - * 契约的原话就是这个格式:**「112 个文件里 90 个系统全自动处理, - * 只有 1 个必须你亲自判断」**。所以这句话由前端按同一口径拼 —— - * 但它拼的是**服务端算好的两个数**,不是自己减出来的 - * (前端减出来的数字早晚会与服务端分叉)。 - */ -export function headlineOf(report: CleanupReportVO): string { - const total = report.summary.totalFiles - const need = report.needManual - return `${total} 个文件里,${report.autoHandled} 个系统全自动处理,` - // ⚠️ 不要在这里写 Markdown 的 `**粗体**`:它落在**文本节点**里, - // 页面上会原样显示两个星号(要粗体得让调用方用富文本渲染) - + `${need > 0 ? `只有 ${need} 个必须你亲自判断` : '没有需要你判断的'}` -} diff --git a/frontend-pc/src/apis/xt/export-task.ts b/frontend-pc/src/apis/xt/export-task.ts index 6097654..71cc6ed 100644 --- a/frontend-pc/src/apis/xt/export-task.ts +++ b/frontend-pc/src/apis/xt/export-task.ts @@ -191,7 +191,7 @@ export async function downloadExportFile(id: number, fileIndex = 0): Promise('/roster/join-requests', params) -} - -export interface JoinApproveReq { - /** 指定班级;不传则用学生的自述班级匹配组织名(匹配不上会要求手选) */ - orgId: number - /** 修正姓名(管理员核对后可改) */ - realName?: string - /** 修正证件号 */ - idCardNo?: string -} - -export interface JoinApproveResult { - requestId: number - status: number - rosterId: number - rosterCreated: boolean - /** ⚠️ 恒为 false:通知没发出去,需要人工告知 */ - notifySent: boolean - /** true = 已绑定,学生返回重新核验就能直接进 */ - canFillNow: boolean -} - -export function approveJoinRequest(id: number, data: JoinApproveReq) { - return http.post(`/roster/join-requests/${id}/approve`, data) -} - -export interface JoinRejectReq { - /** **必填** —— 会原样给学生看 */ - reason: string - reasonCode?: string -} - -export function rejectJoinRequest(id: number, data: JoinRejectReq) { - return http.post<{ requestId: number, status: number, notifySent: boolean }>( - `/roster/join-requests/${id}/reject`, - data - ) -} - -/** 申请状态。 */ -export const JOIN_STATUS: Record = { - 0: { text: '待审核', color: 'orange' }, - 1: { text: '已通过', color: 'green' }, - 2: { text: '已驳回', color: 'red' } -} - -/** 驳回快捷原因码(契约 §8.7.5)。 */ -export const REJECT_REASON_CODES = [ - { code: 'NOT_THIS_SCHOOL', label: '不是本校学生' }, - { code: 'INFO_MISMATCH', label: '信息与报名资料不符' }, - { code: 'NOT_IN_SCOPE', label: '不在本次认定范围' }, - { code: 'OTHER', label: '其他原因(请在说明里写清)' } -] - -/** 关键错误码 → 一句"接下来怎么办"。 */ -export const JOIN_ERROR_HINT: Record = { - 40040: '这条申请已经被别人处理过了(刷新列表即可看到最新状态)。', - 40041: '名册里已经有这个证件号了 —— 可能刚被老师手工加进去,去「名单管理」核对一下。', - 40042: '已通过的申请不能驳回。要撤销请到「名单管理」里删掉那条名册项。', - 40037: '该学生申请次数已达上限(每任务 2 次)。', - 40038: '该学生已有一条待审申请。', - 40039: '该证件号已在名册中 —— 学生只是姓名打错了,让他回去重新核验即可。' -} diff --git a/frontend-pc/src/apis/xt/log.ts b/frontend-pc/src/apis/xt/log.ts index 58c8f67..97edb3f 100644 --- a/frontend-pc/src/apis/xt/log.ts +++ b/frontend-pc/src/apis/xt/log.ts @@ -106,8 +106,14 @@ export const OPERATION_ACTIONS: Array<{ value: string, label: string }> = [ { value: 'PROXY_UPLOAD', label: '代传材料' }, { value: 'VIEW_IDCARD_FULL', label: '查看完整证件号' }, { value: 'CONFIG_COMPRESS', label: '改压缩配置' }, - { value: 'JOIN_APPROVE', label: '通过加入申请' }, - { value: 'JOIN_REJECT', label: '驳回加入申请' } + // ⚠️ 2026-10 需求收敛:`JOIN_APPROVE` / `JOIN_REJECT` 两个动作码已随「加入申请」模块删除, + // 这里不再列出(库里若有历史行,`actionText` 会原样回显那个码,不会显示成空白)。 + { value: 'CREATE_ROLE', label: '新建角色' }, + { value: 'UPDATE_ROLE', label: '改角色' }, + { value: 'DELETE_ROLE', label: '删除角色' }, + { value: 'GRANT_ROLE', label: '改角色授权' }, + { value: 'UPDATE_ROLE_MENU', label: '改角色菜单' }, + { value: 'TOGGLE_ROLE', label: '停用/启用角色' } ] export function actionText(v?: string) { diff --git a/frontend-pc/src/apis/xt/notify.ts b/frontend-pc/src/apis/xt/notify.ts index bc50f39..1869c70 100644 --- a/frontend-pc/src/apis/xt/notify.ts +++ b/frontend-pc/src/apis/xt/notify.ts @@ -99,8 +99,10 @@ export const BIZ_TYPES: Array<{ value: number, label: string }> = [ { value: 3, label: '驳回通知' }, { value: 4, label: '审核通过' }, { value: 5, label: '催报' }, - { value: 6, label: '导出完成' }, - { value: 7, label: '加入审批' } + { value: 6, label: '导出完成' } + // ⚠️ 2026-10 需求收敛:`7 加入审批` 已随「加入申请」模块删除。 + // 后端 `CollectMessage.BIZ_JOIN_AUDIT` 也删了 —— 这里留着它会让运营 + // 在下拉里选一个永远查不到记录的类型。 ] export function bizTypeText(v?: string | number) { diff --git a/frontend-pc/src/apis/xt/org.ts b/frontend-pc/src/apis/xt/org.ts index 56a4fa8..348dda7 100644 --- a/frontend-pc/src/apis/xt/org.ts +++ b/frontend-pc/src/apis/xt/org.ts @@ -34,8 +34,11 @@ export interface OrgUpsertReq { /** * 组织树。 - * @param withCount 是否附带每节点学生数(会多一次聚合查询,列表页按需开) - * @param tenantId **仅平台运营**:代管哪个学校的组织(见 `acting-tenant.ts`)。 + * + * @param params 查询条件(下面三个字段都可选;整体可省略) + * @param params.withCount 是否附带每节点学生数(会多一次聚合查询,列表页按需开) + * @param params.rootId 只取某个节点为根的子树 + * @param params.tenantId **仅平台运营**:代管哪个学校的组织(见 `acting-tenant.ts`)。 * 不传 = 不代管 —— 此时服务端只会返回平台自己租户的组织(空树), * **不会**再把所有租户的组织混在一起端出来。 */ diff --git a/frontend-pc/src/apis/xt/role.ts b/frontend-pc/src/apis/xt/role.ts new file mode 100644 index 0000000..100ba94 --- /dev/null +++ b/frontend-pc/src/apis/xt/role.ts @@ -0,0 +1,183 @@ +/** + * 角色与授权(二期)。 + * + * ## 三条口径(界面上要按它写,不要"看起来更合理"地改) + * + * 1. **内置角色只读**:`roleType === 1` 的角色可以看权限矩阵,但改不了 —— + * 它的权限由后端代码决定(`BuiltinRole`)。一次误操作就能把管理员锁在系统外, + * 而这类错误不可逆。所以界面上的正确做法是**新建一个自定义角色**,而不是改内置的。 + * 2. **看得见 ≠ 有权限**:`menus` 只决定左侧出现哪些入口;真正的访问控制是 + * 后端对权限码的校验(绕过前端只会拿到 `40301`)。 + * 3. **数据范围(本人/本班/本院系/全租户)本期只展示**:它目前由"拿到了哪些权限码 + + * 任务里被指派了哪些班"共同决定,做成可配要先把散在各处的判据收口(下一批)。 + * 所以这里的 `dataScope` 是只读展示 —— 让界面出现一个"改了不生效"的开关更糟。 + * + * ## 改完立即生效 + * + * 服务端在每次写授权后会清掉该角色的缓存,所以**不需要重新登录**: + * 改完刷新页面即可看到按钮/菜单的变化。 + */ +import http from '@/utils/http' +import { actingTenantConfig } from './acting-tenant' + +/** 角色(内置或自定义) */ +export interface RoleVO { + id: number + roleCode: string + roleName: string + /** 1 内置(只读)/ 2 自定义 */ + roleType: number + /** 1 平台运营 / 2 学校管理员 / 3 班主任 / 4 学生 —— 账号的用户类型由它推导 */ + userType: number + /** 1 全平台 / 2 本租户 / 3 本组织及下级 / 4 仅本人(本期只展示) */ + dataScope: number + /** 0 停用 / 1 正常 */ + status: number + sortNo?: number + remark?: string | null + /** 有多少账号在用(删除前的依据:大于 0 就删不掉) */ + userCount: number + /** 能否编辑 / 删除 / 停用(内置角色恒 false) */ + editable: boolean + /** + * 能否发给账号。 + * + * ⚠️ 与 `editable` 是两件事:内置的「平台运营」角色**看得见**(列表里列出来), + * 但在学校租户里**发不出去**(服务端 40301 —— 拿到它会直接越权到平台级)。 + * 所以账号页的角色选项要按它过滤,而不是让人选完再收到一个错误。 + */ + assignable: boolean + permCodes: string[] + menus: string[] +} + +/** 权限码(角色授权界面的勾选项) */ +export interface PermVO { + code: string + label: string + /** PC / MP / BOTH */ + platform: string + /** true = 平台级权限,学校租户不可授予(界面上要禁用勾选) */ + platformOnly: boolean +} + +/** 权限码按分组返回(30 条平铺成一张表没人看得懂) */ +export interface PermGroupVO { + group: string + items: PermVO[] +} + +export interface RoleCreateReq { + roleCode: string + roleName: string + userType: number + remark?: string + permCodes: string[] + menus: string[] +} + +export interface RoleUpdateReq { + roleName: string + userType: number + remark?: string +} + +/** 账号页用的角色选项(内置 + 自定义都在这份里) */ +export interface RoleOption { + value: string + label: string + userType: number + roleType: number + status: number +} + +export function getRoles(tenantId?: number | null) { + return http.get('/roles', undefined, actingTenantConfig(tenantId)) +} + +export function getPermissions() { + return http.get('/permissions') +} + +export function createRole(data: RoleCreateReq) { + return http.post('/roles', data) +} + +export function updateRole(id: number, data: RoleUpdateReq) { + return http.put(`/roles/${id}`, data) +} + +export function deleteRole(id: number) { + return http.delete(`/roles/${id}`) +} + +/** 覆盖式设置权限码(改完立即生效,不必重新登录) */ +export function setRolePermissions(id: number, permCodes: string[]) { + return http.put(`/roles/${id}/permissions`, { permCodes }) +} + +/** 覆盖式设置可见菜单 */ +export function setRoleMenus(id: number, menus: string[]) { + return http.put(`/roles/${id}/menus`, { menus }) +} + +export function setRoleStatus(id: number, status: number) { + return http.put(`/roles/${id}/status`, undefined, { params: { status } }) +} + +/** 角色列表 → 账号页的下拉/勾选项(只取**能发给账号**的启用角色) */ +export async function getRoleOptions(tenantId?: number | null): Promise { + const res = await getRoles(tenantId) + return (res ?? []) + .filter((r) => r.status === 1 && r.assignable !== false) + .map((r) => ({ + value: r.roleCode, + label: r.roleName, + userType: r.userType, + roleType: r.roleType, + status: r.status + })) +} + +/** + * 从所选角色推导用户类型。 + * + * 用户类型与角色是**一对一绑定**的(账号的 `userType` 由它决定),所以界面上不让用户手选: + * 分别选就会产生"学生 + 学校管理员"这种自相矛盾的账号,而服务端还会因此拒掉整个提交。 + * + * ⚠️ 判据来自**接口返回的角色**(`RoleVO.userType`),不是前端写死的映射表 —— + * 二期之后可选角色里还有租户自己建的那些,写死的表推不出它们的类型。 + * + * @returns 同一类时返回那个类型;跨类(如「班主任 + 审核员」)或选不出时返回 null + */ +export function deriveUserType(roleCodes: string[], options: RoleOption[]): number | null { + const types = new Set() + for (const code of roleCodes) { + const opt = options.find((o) => o.value === code) + if (opt) { + types.add(opt.userType) + } + } + return types.size === 1 ? [...types][0] : null +} + +/** 用户类型中文名(与契约 §2.7 / 后端 BuiltinRole.UserType 一致) */ +export const ROLE_USER_TYPE_NAMES: Record = { + 1: '平台运营', + 2: '学校管理员', + 3: '班主任', + 4: '学生' +} + +/** 数据范围中文名(本期只展示) */ +export const DATA_SCOPE_NAMES: Record = { + 1: '全平台', + 2: '本租户', + 3: '本组织及下级', + 4: '仅本人' +} + +/** 错误码 → 人话(后端 msg 已经说清了,这里只补"我该做什么") */ +export const ROLE_ERROR_HINT: Record = { + 40045: '该角色还有账号在用:请先给这些账号换一个角色,再回来删' +} diff --git a/frontend-pc/src/apis/xt/task.ts b/frontend-pc/src/apis/xt/task.ts index 246214e..706b15f 100644 --- a/frontend-pc/src/apis/xt/task.ts +++ b/frontend-pc/src/apis/xt/task.ts @@ -121,6 +121,7 @@ export interface TaskListQuery { /** * 任务分页列表。班主任只会拿到自己带的班级的任务 * + * @param params 分页与筛选(`orgId` / `status` / `keyword` / `pageNum` / `pageSize`) * @param tenantId **仅平台运营**:看哪所学校的任务(见 `./acting-tenant`)。 * 不传 = 平台自己租户(空列表)—— 不再把各租户的任务混在一起。 */ @@ -254,8 +255,6 @@ export interface TaskOrgVO { */ headUserId: number | null headUserName: string | null - deputyUserIds: number[] - deputyUserNames: string[] /** 应填人数(期望值)。null = 未设置,与 0(预期 0 人)含义不同 */ expectedCount: number | null /** 恒为 1(只返回生效行) */ @@ -266,7 +265,6 @@ export interface TaskOrgVO { export interface TaskOrgItem { orgId: number headUserId?: number | null - deputyUserIds?: number[] /** 不传或传 null 表示"未设置",**不要传 0** —— 那是"预期 0 人" */ expectedCount?: number | null } @@ -281,23 +279,23 @@ export function saveTaskOrgs(taskId: number, orgs: TaskOrgItem[]) { } /** - * 可被指定为初审人的账号。 + * 可被指定为初审人的账号(只能是**班主任**)。 * - * 拉两种角色再合并:后端要求初审人持有班主任**或**副班主任角色, - * 所以两边的账号都是合法选项。只取启用状态 —— 停用的账号会被后端拒掉, - * 列在选项里只会让人白选一次。 + * ⚠️ 2026-10 需求收敛:删掉副班主任角色后,初审人只剩班主任一种。 + * 这里**不能**再顺手带一个 `roleCode=DEPUTY_TEACHER` 的查询 —— + * 那个角色码已经不在后端的 `BuiltinRole` 里,而老版本的后端对 + * "不认识的角色码"是**静默忽略过滤条件**(等于返回全校账号,含学生)。 + * 后端已改成"失败关闭"(返回空列表),前端这边也不再传它。 + * + * 只取启用状态 —— 停用的账号会被后端拒掉,列在选项里只会让人白选一次。 */ export async function getTeacherOptions(): Promise> { - const [head, deputy] = await Promise.all([ - getUserList({ roleCode: 'HEAD_TEACHER', status: 1, pageSize: 200 }), - getUserList({ roleCode: 'DEPUTY_TEACHER', status: 1, pageSize: 200 }) - ]) - const merged = new Map() - for (const u of [...(head.data?.list ?? []), ...(deputy.data?.list ?? [])]) { + const page = await getUserList({ roleCode: 'HEAD_TEACHER', status: 1, pageSize: 200 }) + return (page.list ?? []).map((u) => ({ + id: u.id, // 带账号:学校里同名同姓很常见,只显示姓名选错了就是把别人的班交给了错误的人 - merged.set(u.id, u.username ? `${u.realName}(${u.username})` : u.realName) - } - return Array.from(merged.entries(), ([id, label]) => ({ id, label })) + label: u.username ? `${u.realName}(${u.username})` : u.realName + })) } // ── 任务统计(§7.11,工作台看板用)───────────────── diff --git a/frontend-pc/src/apis/xt/user.ts b/frontend-pc/src/apis/xt/user.ts index 7f8f7c4..de586c0 100644 --- a/frontend-pc/src/apis/xt/user.ts +++ b/frontend-pc/src/apis/xt/user.ts @@ -152,30 +152,17 @@ export const USER_TYPE_NAMES: Record = { } /** - * 角色 → 用户类型。 + * 角色选项、角色 → 用户类型、角色名的映射**都不在这里了**。 * - * 二者本来就是一对一绑定的,所以: - * · 表单里**不让用户选**用户类型,而是从角色推导出来(避免选出矛盾组合) - * · 服务端也会用同一套映射反推校验(`UserService.assertRoleMatchesUserType`) + * 二期(角色与授权)之后,可选角色不再只有 6 个内置角色 —— 租户自己建的角色也要能发出去, + * 而它们的名字与用户类型只存在于库里。所以这三件事统一走 `@/apis/xt/role`: + * · 选项:`getRoleOptions()`(`GET /roles`) + * · 用户类型:`RoleVO.userType`(`deriveUserType(roleCodes, options)`) + * · 角色名:`UserVO.roleNames`(后端已按 `sys_role.role_name` 装配) + * + * ⚠️ 留一份前端写死的映射表会**静默过期**:自定义角色推不出类型, + * 表现是"账号建不出来,但看不出为什么"(服务端 40001:用户类型与所选角色不符)。 */ -export const ROLE_TO_USER_TYPE: Record = { - PLATFORM_ADMIN: 1, - SCHOOL_ADMIN: 2, - AUDITOR: 2, - HEAD_TEACHER: 3, - DEPUTY_TEACHER: 3, - STUDENT: 4 -} - -/** 从所选角色推导用户类型;角色为空或跨类时返回 `null`(调用方据此拦提交) */ -export function deriveUserType(roleCodes: string[]): number | null { - const types = new Set() - for (const c of roleCodes) { - const t = ROLE_TO_USER_TYPE[c] - if (t) types.add(t) - } - return types.size === 1 ? [...types][0] : null -} /** 账号状态(对应 sys_user.status) */ export const USER_STATUS: Record = { @@ -184,41 +171,6 @@ export const USER_STATUS: Record = { 2: { text: '待激活', color: 'orange' } } -/** - * 可分配的角色,**必须与后端 `BuiltinRole` 逐字一致**。 - * - * 为什么在前端也写一份:一期没有任何"自定义角色"的界面,角色集合由代码决定、 - * 不由数据库决定(这是项目已定的决策 —— 权限码是前后端契约,放 DB 必然漂移)。 - * 所以这里是「与后端枚举对称的镜像」,不是"业务配置"。 - * - * 真实的可分配范围仍由后端 `UserService.resolveAssignableRoles` 判定: - * 传了这里的列表之外的角色会拿到 40301,前端只负责不显示没得选的项。 - * - * 平台运营角色只在平台租户可分配 —— 学校管理员拿到它会直接越权到平台级。 - */ -export const SCHOOL_ROLE_OPTIONS: Array<{ value: string, label: string }> = [ - { value: 'SCHOOL_ADMIN', label: '学校管理员' }, - { value: 'HEAD_TEACHER', label: '班主任' }, - { value: 'DEPUTY_TEACHER', label: '副班主任' }, - { value: 'AUDITOR', label: '审核员' }, - { value: 'STUDENT', label: '学生' } -] - -/** 平台租户的可分配角色 */ -export const PLATFORM_ROLE_OPTIONS: Array<{ value: string, label: string }> = [ - { value: 'PLATFORM_ADMIN', label: '平台运营' } -] - -/** 导入时「角色」列允许写的角色中文名 → 角色码(与后端 BuiltinRole 对齐) */ -export const ROLE_NAME_TO_CODE: Record = { - 平台运营: 'PLATFORM_ADMIN', - 学校管理员: 'SCHOOL_ADMIN', - 班主任: 'HEAD_TEACHER', - 副班主任: 'DEPUTY_TEACHER', - 审核员: 'AUDITOR', - 学生: 'STUDENT' -} - /** 导入文件里被识别的列名(与后端 HEADER_ALIASES 对齐,供界面提示用) */ export const IMPORT_FIELD_NAMES: Record = { realName: '姓名', diff --git a/frontend-pc/src/assets/images/logo.svg b/frontend-pc/src/assets/images/logo.svg index 9f239d6..bc00ab8 100644 --- a/frontend-pc/src/assets/images/logo.svg +++ b/frontend-pc/src/assets/images/logo.svg @@ -1,15 +1,29 @@ - + @@ -22,24 +36,20 @@ - + - + - - 填 + + + + + diff --git a/frontend-pc/src/auto-import.d.ts b/frontend-pc/src/auto-import.d.ts deleted file mode 100644 index 72370df..0000000 --- a/frontend-pc/src/auto-import.d.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* eslint-disable */ -/* prettier-ignore */ -// @ts-nocheck -// noinspection JSUnusedGlobalSymbols -// Generated by unplugin-auto-import -// biome-ignore lint: disable -export {} -declare global { - const EffectScope: typeof import('vue').EffectScope - const computed: typeof import('vue').computed - const createApp: typeof import('vue').createApp - const customRef: typeof import('vue').customRef - const defineAsyncComponent: typeof import('vue').defineAsyncComponent - const defineComponent: typeof import('vue').defineComponent - const effectScope: typeof import('vue').effectScope - const getCurrentInstance: typeof import('vue').getCurrentInstance - const getCurrentScope: typeof import('vue').getCurrentScope - const getCurrentWatcher: typeof import('vue').getCurrentWatcher - const h: typeof import('vue').h - const inject: typeof import('vue').inject - const isProxy: typeof import('vue').isProxy - const isReactive: typeof import('vue').isReactive - const isReadonly: typeof import('vue').isReadonly - const isRef: typeof import('vue').isRef - const isShallow: typeof import('vue').isShallow - const markRaw: typeof import('vue').markRaw - const nextTick: typeof import('vue').nextTick - const onActivated: typeof import('vue').onActivated - const onBeforeMount: typeof import('vue').onBeforeMount - const onBeforeRouteLeave: typeof import('vue-router').onBeforeRouteLeave - const onBeforeRouteUpdate: typeof import('vue-router').onBeforeRouteUpdate - const onBeforeUnmount: typeof import('vue').onBeforeUnmount - const onBeforeUpdate: typeof import('vue').onBeforeUpdate - const onDeactivated: typeof import('vue').onDeactivated - const onErrorCaptured: typeof import('vue').onErrorCaptured - const onMounted: typeof import('vue').onMounted - const onRenderTracked: typeof import('vue').onRenderTracked - const onRenderTriggered: typeof import('vue').onRenderTriggered - const onScopeDispose: typeof import('vue').onScopeDispose - const onServerPrefetch: typeof import('vue').onServerPrefetch - const onUnmounted: typeof import('vue').onUnmounted - const onUpdated: typeof import('vue').onUpdated - const onWatcherCleanup: typeof import('vue').onWatcherCleanup - const provide: typeof import('vue').provide - const reactive: typeof import('vue').reactive - const readonly: typeof import('vue').readonly - const ref: typeof import('vue').ref - const resolveComponent: typeof import('vue').resolveComponent - const shallowReactive: typeof import('vue').shallowReactive - const shallowReadonly: typeof import('vue').shallowReadonly - const shallowRef: typeof import('vue').shallowRef - const toRaw: typeof import('vue').toRaw - const toRef: typeof import('vue').toRef - const toRefs: typeof import('vue').toRefs - const toValue: typeof import('vue').toValue - const triggerRef: typeof import('vue').triggerRef - const unref: typeof import('vue').unref - const useAttrs: typeof import('vue').useAttrs - const useCssModule: typeof import('vue').useCssModule - const useCssVars: typeof import('vue').useCssVars - const useId: typeof import('vue').useId - const useLink: typeof import('vue-router').useLink - const useModel: typeof import('vue').useModel - const useRoute: typeof import('vue-router').useRoute - const useRouter: typeof import('vue-router').useRouter - const useSlots: typeof import('vue').useSlots - const useTemplateRef: typeof import('vue').useTemplateRef - const watch: typeof import('vue').watch - const watchEffect: typeof import('vue').watchEffect - const watchPostEffect: typeof import('vue').watchPostEffect - const watchSyncEffect: typeof import('vue').watchSyncEffect -} -// for type re-export -declare global { - // @ts-ignore - export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' - import('vue') -} diff --git a/frontend-pc/src/components.d.ts b/frontend-pc/src/components.d.ts deleted file mode 100644 index 896f07b..0000000 --- a/frontend-pc/src/components.d.ts +++ /dev/null @@ -1,188 +0,0 @@ -/* eslint-disable */ -// @ts-nocheck -// biome-ignore lint: disable -// oxlint-disable -// ------ -// Generated by unplugin-vue-components -// Read more: https://github.com/vuejs/core/pull/3399 -import { GlobalComponents } from 'vue' - -export {} - -/* prettier-ignore */ -declare module 'vue' { - export interface GlobalComponents { - AAlert: typeof import('@arco-design/web-vue')['Alert'] - AAvatar: typeof import('@arco-design/web-vue')['Avatar'] - ABadge: typeof import('@arco-design/web-vue')['Badge'] - ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb'] - ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem'] - AButton: typeof import('@arco-design/web-vue')['Button'] - ACard: typeof import('@arco-design/web-vue')['Card'] - ACheckbox: typeof import('@arco-design/web-vue')['Checkbox'] - ACheckboxGroup: typeof import('@arco-design/web-vue')['CheckboxGroup'] - ACol: typeof import('@arco-design/web-vue')['Col'] - AComment: typeof import('@arco-design/web-vue')['Comment'] - AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider'] - ADatePicker: typeof import('@arco-design/web-vue')['DatePicker'] - ADescriptions: typeof import('@arco-design/web-vue')['Descriptions'] - ADescriptionsItem: typeof import('@arco-design/web-vue')['DescriptionsItem'] - ADivider: typeof import('@arco-design/web-vue')['Divider'] - ADoption: typeof import('@arco-design/web-vue')['Doption'] - ADrawer: typeof import('@arco-design/web-vue')['Drawer'] - ADropdown: typeof import('@arco-design/web-vue')['Dropdown'] - AEmpty: typeof import('@arco-design/web-vue')['Empty'] - AForm: typeof import('@arco-design/web-vue')['Form'] - AFormItem: typeof import('@arco-design/web-vue')['FormItem'] - AGrid: typeof import('@arco-design/web-vue')['Grid'] - AGridItem: typeof import('@arco-design/web-vue')['GridItem'] - AInput: typeof import('@arco-design/web-vue')['Input'] - AInputGroup: typeof import('@arco-design/web-vue')['InputGroup'] - AInputNumber: typeof import('@arco-design/web-vue')['InputNumber'] - AInputPassword: typeof import('@arco-design/web-vue')['InputPassword'] - AInputSearch: typeof import('@arco-design/web-vue')['InputSearch'] - AInputTag: typeof import('@arco-design/web-vue')['InputTag'] - ALayout: typeof import('@arco-design/web-vue')['Layout'] - ALayoutHeader: typeof import('@arco-design/web-vue')['LayoutHeader'] - ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider'] - ALink: typeof import('@arco-design/web-vue')['Link'] - AMenu: typeof import('@arco-design/web-vue')['Menu'] - AMenuItem: typeof import('@arco-design/web-vue')['MenuItem'] - AModal: typeof import('@arco-design/web-vue')['Modal'] - AOption: typeof import('@arco-design/web-vue')['Option'] - AOverflowList: typeof import('@arco-design/web-vue')['OverflowList'] - APopconfirm: typeof import('@arco-design/web-vue')['Popconfirm'] - APopover: typeof import('@arco-design/web-vue')['Popover'] - AProgress: typeof import('@arco-design/web-vue')['Progress'] - ARadio: typeof import('@arco-design/web-vue')['Radio'] - ARadioGroup: typeof import('@arco-design/web-vue')['RadioGroup'] - ARow: typeof import('@arco-design/web-vue')['Row'] - AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar'] - ASelect: typeof import('@arco-design/web-vue')['Select'] - ASpace: typeof import('@arco-design/web-vue')['Space'] - ASpin: typeof import('@arco-design/web-vue')['Spin'] - ASplit: typeof import('@arco-design/web-vue')['Split'] - ASubMenu: typeof import('@arco-design/web-vue')['SubMenu'] - ASwitch: typeof import('@arco-design/web-vue')['Switch'] - ATable: typeof import('@arco-design/web-vue')['Table'] - ATableColumn: typeof import('@arco-design/web-vue')['TableColumn'] - ATabPane: typeof import('@arco-design/web-vue')['TabPane'] - ATabs: typeof import('@arco-design/web-vue')['Tabs'] - ATag: typeof import('@arco-design/web-vue')['Tag'] - ATextarea: typeof import('@arco-design/web-vue')['Textarea'] - ATooltip: typeof import('@arco-design/web-vue')['Tooltip'] - ATree: typeof import('@arco-design/web-vue')['Tree'] - ATreeSelect: typeof import('@arco-design/web-vue')['TreeSelect'] - AUpload: typeof import('@arco-design/web-vue')['Upload'] - GiButton: typeof import('./components/GiButton/GiButton.vue')['default'] - GiCellAvatar: typeof import('./components/GiCell/GiCellAvatar.vue')['default'] - GiCellGender: typeof import('./components/GiCell/GiCellGender.vue')['default'] - GiCellStatus: typeof import('./components/GiCell/GiCellStatus.vue')['default'] - GiCellTags: typeof import('./components/GiCell/GiCellTags.vue')['default'] - GiDot: typeof import('./components/GiDot/GiDot.tsx')['default'] - GiEditTable: typeof import('./components/GiEditTable/GiEditTable.vue')['default'] - GiFlex: typeof import('./components/GiFlex/GiFlex.vue')['default'] - GiForm: typeof import('./components/GiForm/GiForm.vue')['default'] - GiIconBox: typeof import('./components/GiIconBox/GiIconBox.vue')['default'] - GiInputSearch: typeof import('./components/GiInputSearch/GiInputSearch.vue')['default'] - GiOption: typeof import('./components/GiOption/GiOption.vue')['default'] - GiOptionItem: typeof import('./components/GiOption/GiOptionItem.vue')['default'] - GiPageLayout: typeof import('./components/GiPageLayout/GiPageLayout.vue')['default'] - GiTable: typeof import('./components/GiTable/GiTable.vue')['default'] - GiTableSetting: typeof import('./components/GiTableSetting/GiTableSetting.vue')['default'] - GiTag: typeof import('./components/GiTag/GiTag.tsx')['default'] - GiTenantPicker: typeof import('./components/GiTenantPicker/GiTenantPicker.vue')['default'] - GiThemeBtn: typeof import('./components/GiThemeBtn/GiThemeBtn.vue')['default'] - RouterLink: typeof import('vue-router')['RouterLink'] - RouterView: typeof import('vue-router')['RouterView'] - SplitButton: typeof import('./components/GiPageLayout/SplitButton.vue')['default'] - } -} - -// For TSX support -declare global { - const AAlert: typeof import('@arco-design/web-vue')['Alert'] - const AAvatar: typeof import('@arco-design/web-vue')['Avatar'] - const ABadge: typeof import('@arco-design/web-vue')['Badge'] - const ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb'] - const ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem'] - const AButton: typeof import('@arco-design/web-vue')['Button'] - const ACard: typeof import('@arco-design/web-vue')['Card'] - const ACheckbox: typeof import('@arco-design/web-vue')['Checkbox'] - const ACheckboxGroup: typeof import('@arco-design/web-vue')['CheckboxGroup'] - const ACol: typeof import('@arco-design/web-vue')['Col'] - const AComment: typeof import('@arco-design/web-vue')['Comment'] - const AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider'] - const ADatePicker: typeof import('@arco-design/web-vue')['DatePicker'] - const ADescriptions: typeof import('@arco-design/web-vue')['Descriptions'] - const ADescriptionsItem: typeof import('@arco-design/web-vue')['DescriptionsItem'] - const ADivider: typeof import('@arco-design/web-vue')['Divider'] - const ADoption: typeof import('@arco-design/web-vue')['Doption'] - const ADrawer: typeof import('@arco-design/web-vue')['Drawer'] - const ADropdown: typeof import('@arco-design/web-vue')['Dropdown'] - const AEmpty: typeof import('@arco-design/web-vue')['Empty'] - const AForm: typeof import('@arco-design/web-vue')['Form'] - const AFormItem: typeof import('@arco-design/web-vue')['FormItem'] - const AGrid: typeof import('@arco-design/web-vue')['Grid'] - const AGridItem: typeof import('@arco-design/web-vue')['GridItem'] - const AInput: typeof import('@arco-design/web-vue')['Input'] - const AInputGroup: typeof import('@arco-design/web-vue')['InputGroup'] - const AInputNumber: typeof import('@arco-design/web-vue')['InputNumber'] - const AInputPassword: typeof import('@arco-design/web-vue')['InputPassword'] - const AInputSearch: typeof import('@arco-design/web-vue')['InputSearch'] - const AInputTag: typeof import('@arco-design/web-vue')['InputTag'] - const ALayout: typeof import('@arco-design/web-vue')['Layout'] - const ALayoutHeader: typeof import('@arco-design/web-vue')['LayoutHeader'] - const ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider'] - const ALink: typeof import('@arco-design/web-vue')['Link'] - const AMenu: typeof import('@arco-design/web-vue')['Menu'] - const AMenuItem: typeof import('@arco-design/web-vue')['MenuItem'] - const AModal: typeof import('@arco-design/web-vue')['Modal'] - const AOption: typeof import('@arco-design/web-vue')['Option'] - const AOverflowList: typeof import('@arco-design/web-vue')['OverflowList'] - const APopconfirm: typeof import('@arco-design/web-vue')['Popconfirm'] - const APopover: typeof import('@arco-design/web-vue')['Popover'] - const AProgress: typeof import('@arco-design/web-vue')['Progress'] - const ARadio: typeof import('@arco-design/web-vue')['Radio'] - const ARadioGroup: typeof import('@arco-design/web-vue')['RadioGroup'] - const ARow: typeof import('@arco-design/web-vue')['Row'] - const AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar'] - const ASelect: typeof import('@arco-design/web-vue')['Select'] - const ASpace: typeof import('@arco-design/web-vue')['Space'] - const ASpin: typeof import('@arco-design/web-vue')['Spin'] - const ASplit: typeof import('@arco-design/web-vue')['Split'] - const ASubMenu: typeof import('@arco-design/web-vue')['SubMenu'] - const ASwitch: typeof import('@arco-design/web-vue')['Switch'] - const ATable: typeof import('@arco-design/web-vue')['Table'] - const ATableColumn: typeof import('@arco-design/web-vue')['TableColumn'] - const ATabPane: typeof import('@arco-design/web-vue')['TabPane'] - const ATabs: typeof import('@arco-design/web-vue')['Tabs'] - const ATag: typeof import('@arco-design/web-vue')['Tag'] - const ATextarea: typeof import('@arco-design/web-vue')['Textarea'] - const ATooltip: typeof import('@arco-design/web-vue')['Tooltip'] - const ATree: typeof import('@arco-design/web-vue')['Tree'] - const ATreeSelect: typeof import('@arco-design/web-vue')['TreeSelect'] - const AUpload: typeof import('@arco-design/web-vue')['Upload'] - const GiButton: typeof import('./components/GiButton/GiButton.vue')['default'] - const GiCellAvatar: typeof import('./components/GiCell/GiCellAvatar.vue')['default'] - const GiCellGender: typeof import('./components/GiCell/GiCellGender.vue')['default'] - const GiCellStatus: typeof import('./components/GiCell/GiCellStatus.vue')['default'] - const GiCellTags: typeof import('./components/GiCell/GiCellTags.vue')['default'] - const GiDot: typeof import('./components/GiDot/GiDot.tsx')['default'] - const GiEditTable: typeof import('./components/GiEditTable/GiEditTable.vue')['default'] - const GiFlex: typeof import('./components/GiFlex/GiFlex.vue')['default'] - const GiForm: typeof import('./components/GiForm/GiForm.vue')['default'] - const GiIconBox: typeof import('./components/GiIconBox/GiIconBox.vue')['default'] - const GiInputSearch: typeof import('./components/GiInputSearch/GiInputSearch.vue')['default'] - const GiOption: typeof import('./components/GiOption/GiOption.vue')['default'] - const GiOptionItem: typeof import('./components/GiOption/GiOptionItem.vue')['default'] - const GiPageLayout: typeof import('./components/GiPageLayout/GiPageLayout.vue')['default'] - const GiTable: typeof import('./components/GiTable/GiTable.vue')['default'] - const GiTableSetting: typeof import('./components/GiTableSetting/GiTableSetting.vue')['default'] - const GiTag: typeof import('./components/GiTag/GiTag.tsx')['default'] - const GiTenantPicker: typeof import('./components/GiTenantPicker/GiTenantPicker.vue')['default'] - const GiThemeBtn: typeof import('./components/GiThemeBtn/GiThemeBtn.vue')['default'] - const RouterLink: typeof import('vue-router')['RouterLink'] - const RouterView: typeof import('vue-router')['RouterView'] - const SplitButton: typeof import('./components/GiPageLayout/SplitButton.vue')['default'] -} \ No newline at end of file diff --git a/frontend-pc/src/components/GiButton/GiButton.vue b/frontend-pc/src/components/GiButton/GiButton.vue deleted file mode 100644 index 637f1f7..0000000 --- a/frontend-pc/src/components/GiButton/GiButton.vue +++ /dev/null @@ -1,86 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/GiButton/index.ts b/frontend-pc/src/components/GiButton/index.ts deleted file mode 100644 index 9750fe6..0000000 --- a/frontend-pc/src/components/GiButton/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import Button from './GiButton.vue' - -export type ButtonInstance = InstanceType -export * from './type' -export { Button as GiButton } diff --git a/frontend-pc/src/components/GiButton/type.ts b/frontend-pc/src/components/GiButton/type.ts deleted file mode 100644 index 7c6bf35..0000000 --- a/frontend-pc/src/components/GiButton/type.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { ButtonProps as ArcoButtonProps } from '@arco-design/web-vue' - -export interface ButtonProps extends Omit { - type?: 'add' | 'edit' | 'delete' | 'search' | 'reset' | 'upload' | 'import' | 'export' | '' -} diff --git a/frontend-pc/src/components/GiCell/GiCellAvatar.vue b/frontend-pc/src/components/GiCell/GiCellAvatar.vue deleted file mode 100644 index 5bf95dd..0000000 --- a/frontend-pc/src/components/GiCell/GiCellAvatar.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - {{ props.name }} - {{ props.name }} - - - - - - diff --git a/frontend-pc/src/components/GiCell/GiCellGender.vue b/frontend-pc/src/components/GiCell/GiCellGender.vue deleted file mode 100644 index ee22cbf..0000000 --- a/frontend-pc/src/components/GiCell/GiCellGender.vue +++ /dev/null @@ -1,28 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/GiCell/GiCellStatus.vue b/frontend-pc/src/components/GiCell/GiCellStatus.vue deleted file mode 100644 index 918885e..0000000 --- a/frontend-pc/src/components/GiCell/GiCellStatus.vue +++ /dev/null @@ -1,24 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/GiCell/GiCellTags.vue b/frontend-pc/src/components/GiCell/GiCellTags.vue deleted file mode 100644 index 7456aab..0000000 --- a/frontend-pc/src/components/GiCell/GiCellTags.vue +++ /dev/null @@ -1,33 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/GiCell/index.ts b/frontend-pc/src/components/GiCell/index.ts deleted file mode 100644 index d39267f..0000000 --- a/frontend-pc/src/components/GiCell/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import GiCellAvatar from './GiCellAvatar.vue' -import GiCellGender from './GiCellGender.vue' -import GiCellStatus from './GiCellStatus.vue' -import GiCellTags from './GiCellTags.vue' - -export { GiCellAvatar, GiCellGender, GiCellStatus, GiCellTags } diff --git a/frontend-pc/src/components/GiDot/GiDot.tsx b/frontend-pc/src/components/GiDot/GiDot.tsx deleted file mode 100644 index 83c6119..0000000 --- a/frontend-pc/src/components/GiDot/GiDot.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @file GiDot 组件 - 用于显示带有可选动画效果的状态点 - */ -import type { PropType } from 'vue' -import type { DotType, DotProps as Props } from './type' -import { defineComponent } from 'vue' -import './dot.scss' - -export default defineComponent({ - name: 'GiDot', - props: { - animation: { - type: Boolean as PropType, - default: true, - required: false - }, - type: { - type: String as PropType, - default: 'primary', - required: false, - validator: (value: string): boolean => { - return ['primary', 'success', 'warning', 'danger', 'info'].includes(value) - } - } - }, - setup(props: Props) { - return () => ( - - ) - } -}) diff --git a/frontend-pc/src/components/GiDot/dot.scss b/frontend-pc/src/components/GiDot/dot.scss deleted file mode 100644 index a762721..0000000 --- a/frontend-pc/src/components/GiDot/dot.scss +++ /dev/null @@ -1,58 +0,0 @@ -.gi-dot { - display: inline-block; - width: 8px; - height: 8px; - vertical-align: middle; - background: #000; - border-radius: 50%; -} - -$types: primary, success, warning, danger, info; - -@each $i in $types { - .gi-dot-#{$i} - - { - background-color: rgb(var(--#{$i}-6)); - } - - @if ($i ==info) { - .gi-dot-#{$i} - - { - background-color: rgb(var(--gray-6)); - } - } -} - -.gi-dot-processing { - position: relative; -} - -.gi-dot-processing::after { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - content: ''; - background: inherit; - border-radius: 50%; - animation: gi-dot-animated 1.2s ease-in-out infinite; -} - -@keyframes gi-dot-animated { - 0% { - opacity: 1; - transform: scale(0.5); - } - - 30% { - opacity: 0.7; - } - - 100% { - opacity: 0; - transform: scale(2.5); - } -} \ No newline at end of file diff --git a/frontend-pc/src/components/GiDot/index.ts b/frontend-pc/src/components/GiDot/index.ts deleted file mode 100644 index 0f39877..0000000 --- a/frontend-pc/src/components/GiDot/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import Dot from './GiDot.tsx' - -export type * from './type' -export type DotInstance = InstanceType -export { Dot as GiDot } diff --git a/frontend-pc/src/components/GiDot/type.ts b/frontend-pc/src/components/GiDot/type.ts deleted file mode 100644 index c066c69..0000000 --- a/frontend-pc/src/components/GiDot/type.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** 点的类型 */ -export type DotType = 'primary' | 'success' | 'warning' | 'danger' | 'info' - -/** 组件属性定义 */ -export interface DotProps { - /** 点的类型 */ - type?: DotType - /** 是否启用动画效果 */ - animation?: boolean -} diff --git a/frontend-pc/src/components/GiEditTable/GiEditTable.vue b/frontend-pc/src/components/GiEditTable/GiEditTable.vue deleted file mode 100644 index f6bef61..0000000 --- a/frontend-pc/src/components/GiEditTable/GiEditTable.vue +++ /dev/null @@ -1,176 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/GiEditTable/index.ts b/frontend-pc/src/components/GiEditTable/index.ts deleted file mode 100644 index cd2918f..0000000 --- a/frontend-pc/src/components/GiEditTable/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { FormInstance } from '@arco-design/web-vue' -import EditTable from './GiEditTable.vue' - -export type * from './type' - -/** - * 组件实例类型。 - * - * 与 `GiTable/index.ts` 同一个原因:泛型 SFC 不能用 `InstanceType<>` 推导 - * (` - - diff --git a/frontend-pc/src/components/GiFlex/index.ts b/frontend-pc/src/components/GiFlex/index.ts deleted file mode 100644 index 4293675..0000000 --- a/frontend-pc/src/components/GiFlex/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import Flex from './GiFlex.vue' - -export type FlexInstance = InstanceType -export * from './type' -export { Flex as GiFlex } diff --git a/frontend-pc/src/components/GiFlex/type.ts b/frontend-pc/src/components/GiFlex/type.ts deleted file mode 100644 index 252c50f..0000000 --- a/frontend-pc/src/components/GiFlex/type.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** 组件属性定义 */ -export interface FlexProps { - /** 排列方向 */ - column?: boolean - /** 水平对齐方式(与 CSS justify-content 一致) */ - justify?: - | 'start' - | 'center' - | 'end' - | 'flex-start' - | 'flex-end' - | 'space-between' - | 'space-around' - | 'space-evenly' - /** 垂直对齐方式(与 CSS align-items 一致) */ - align?: - | 'start' - | 'center' - | 'end' - | 'flex-start' - | 'flex-end' - | 'baseline' - | 'stretch' - /** 是否自动换行 */ - wrap?: boolean - /** `boolean` 须写在联合类型最前,否则模板里单独写 `gap` 会被解析成 `""` 而不是 `true` */ - gap?: boolean | number | string - flex?: number | string -} diff --git a/frontend-pc/src/components/GiForm/GiForm.vue b/frontend-pc/src/components/GiForm/GiForm.vue deleted file mode 100644 index b3d3462..0000000 --- a/frontend-pc/src/components/GiForm/GiForm.vue +++ /dev/null @@ -1,273 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/components/GiForm/index.ts b/frontend-pc/src/components/GiForm/index.ts deleted file mode 100644 index 219e98e..0000000 --- a/frontend-pc/src/components/GiForm/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import Form from './GiForm.vue' - -export type * from './type' -export type FormInstance = InstanceType -export { Form as GiForm } diff --git a/frontend-pc/src/components/GiForm/type.ts b/frontend-pc/src/components/GiForm/type.ts deleted file mode 100644 index c37bc0b..0000000 --- a/frontend-pc/src/components/GiForm/type.ts +++ /dev/null @@ -1,299 +0,0 @@ -/** - * @file GiForm 组件类型定义 - * @description 包含表单组件所需的所有类型定义,用于构建动态表单 - */ - -import type * as A from '@arco-design/web-vue' -import type { GridItemProps } from '@arco-design/web-vue' -import type { Component, VNode } from 'vue' -import type { MergeMultiple } from '@/types/tool' - -type FormItemProps = A.FormItemInstance['$props'] - -/** 支持的表单项类型 */ -export type FormColumnItemType - = | 'input' // 输入框 - | 'input-number' // 数字输入框 - | 'input-tag' // 标签输入框 - | 'input-search' // 搜索框 - | 'textarea' // 文本域 - | 'select' // 选择器 - | 'tree-select' // 树选择器 - | 'radio' // 单选框 - | 'radio-group' // 单选框组 - | 'checkbox' // 复选框 - | 'checkbox-group' // 复选框组 - | 'date-picker' // 日期选择器 - | 'year-picker' // 年份选择器 - | 'quarter-picker' // 季度选择器 - | 'month-picker' // 月份选择器 - | 'week-picker' // 周选择器 - | 'time-picker' // 时间选择器 - | 'range-picker' // 范围选择器 - | 'color-picker' // 颜色选择器 - | 'rate' // 评分 - | 'switch' // 开关 - | 'slider' // 滑块 - | 'cascader' // 级联选择器 - | 'upload' // 上传 - | 'auto-complete' // 自动完成 - | 'mention' // 提及 - | 'group-title' // 分组标题 - | 'slot' // 插槽,由父组件通过具名插槽渲染内容 - | Component - -/** 组件属性合并类型 */ -export type FormComponentProps = MergeMultiple<[ - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial, - Partial -]> - -/** 选项类型定义 */ -export type FormColumnItemOptions - = MergeMultiple<[A.SelectInstance['$props']['options'], A.RadioGroupInstance['$props']['options'], A.CheckboxGroupInstance['$props']['options'], A.CascaderInstance['$props']['options']]> - -/** 数据源类型定义 */ -export type FormColumnItemData - = MergeMultiple<[A.TreeSelectInstance['$props']['data'], A.AutoCompleteInstance['$props']['data'], A.MentionInstance['$props']['data']]> - -/** 自动完成组件插槽 */ -type AutoCompleteSlots = { - option: (e: { data: (string | number | A.SelectOptionData | A.SelectOptionGroup)[] }) => VNode - footer: () => VNode -} - -/** 级联选择器插槽 */ -type CascaderSlots = { - 'label': (e: { data: A.CascaderOption }) => VNode - 'prefix': () => VNode - 'arrow-icon': () => VNode - 'loading-icon': () => VNode - 'search-icon': () => VNode - 'empty': () => VNode - 'option': (e: { data: A.CascaderOption }) => VNode -} - -/** 复选框组插槽 */ -type CheckboxGroupSlots = { - checkbox: (e: { checked: boolean, disabled: string }) => VNode - label: (e: { data: A.CheckboxOption }) => VNode -} - -/** 单选框组插槽 */ -type RadioGroupSlots = { - radio: (e: { checked: boolean, disabled: string }) => VNode - label: (e: { data: any }) => VNode -} - -/** 日期选择器插槽 */ -type DatePickerSlots = { - 'prefix': () => VNode - 'suffix-icon': () => VNode - 'icon-next-double': () => VNode - 'icon-prev-double': () => VNode - 'icon-next': () => VNode - 'icon-prev': () => VNode - 'cell': (e: { data: Date }) => VNode - 'extra': () => VNode -} - -/** 输入框插槽 */ -type InputSlots = { - append: (() => VNode) | string - prepend: (() => VNode) | string - suffix: (() => VNode) | string - prefix: (() => VNode) | string -} - -/** 数字输入框插槽 */ -type InputNumberSlots = { - minus: (() => VNode) | string - plus: (() => VNode) | string - append: (() => VNode) | string - prepend: (() => VNode) | string - suffix: (() => VNode) | string -} - -/** 标签输入框插槽 */ -type InputTagSlots = { - tag: (e: { data: A.TagData }) => VNode - prefix: (() => VNode) | string - suffix: (() => VNode) | string -} - -/** 评分插槽 */ -type RateSlots = { - character: (e: { index: number }) => VNode -} - -/** 选择器插槽 */ -type SelectSlots = { - 'trigger': () => VNode - 'prefix': () => VNode - 'search-icon': () => VNode - 'loading-icon': () => VNode - 'arrow-icon': () => VNode - 'footer': () => VNode - 'header': () => VNode - 'label': (e: { data: A.SelectOptionData }) => VNode - 'option': (e: { data: A.SelectOptionData }) => VNode - 'empty': () => VNode -} - -/** 开关插槽 */ -type SwitchSlots = { - 'checked-icon': () => VNode - 'unchecked-icon': () => VNode - 'checked': () => VNode - 'unchecked': () => VNode -} - -/** 树选择器插槽 */ -type TreeSelectSlots = { - 'trigger': () => VNode - 'prefix': () => VNode - 'label': (e: { data: any }) => VNode - 'header': () => VNode - 'loader': () => VNode - 'empty': () => VNode - 'footer': () => VNode - 'tree-slot-extra': () => VNode - 'tree-slot-title': (e: { title: string }) => VNode - 'tree-slot-icon': (e: { node: A.TreeNodeData }) => VNode - 'tree-slot-switcher-icon': () => VNode -} - -/** 提及插槽 */ -type MentionSlots = { - option: (e: { data: any }) => VNode -} - -/** 组件插槽合并类型 */ -export type FormComponentSlots = MergeMultiple<[ - AutoCompleteSlots, - CascaderSlots, - CheckboxGroupSlots, - RadioGroupSlots, - DatePickerSlots, - InputSlots, - InputNumberSlots, - InputTagSlots, - RateSlots, - SelectSlots, - SwitchSlots, - TreeSelectSlots, - MentionSlots -]> - -/** 表单项插槽接口 */ -export type FormColumnItemSlots = FormComponentSlots - -/** 表单项显隐控制类型 */ -export type FormColumnItemHide = boolean | ((form: F) => boolean) -export type FormColumnItemDisabled = boolean | ((form: F) => boolean) - -/** 表单项数据请求相关类型 */ -export type FormColumnItemRequest = (form: F) => Promise -export type FormColumnItemFormat = (res: T) => FormColumnItemOptions | FormColumnItemData - -/** 表单列配置项接口 */ -export interface FormColumnItem { - /** 表单项类型 */ - type?: FormColumnItemType - /** 标签名 */ - label?: string - /** 字段名 */ - field: string - fieldName?: string - /** 自定义渲染label */ - labelRender?: (() => VNode) - /** 表单项属性 */ - props?: Partial - /** 表单项校验规则 */ - rules?: FormItemProps['rules'] - /** 是否必填 */ - required?: boolean - /** 栅格属性 */ - gridItemProps?: GridItemProps - /** 表单项属性 */ - formItemProps?: FormItemProps - /** 表单项插槽 */ - slots?: FormColumnItemSlots - /** 表单项组件插槽 */ - formItemSlots?: Record VNode)> - /** 是否隐藏(支持函数) */ - hide?: boolean | ((model: F) => boolean) - /** 是否禁用(支持函数) */ - disabled?: boolean | ((model: F) => boolean) - /** 栅格跨度 */ - span?: number | Record -} - -/** Props 类型定义 */ -export interface FormProps { - /** 表单数据对象 */ - modelValue: any - /** 表单布局方式 */ - layout?: A.FormInstance['layout'] - /** 表单尺寸 */ - size?: A.FormInstance['size'] - /** 标签布局属性 */ - labelColProps?: A.FormInstance['labelColProps'] - /** 表单项布局属性 */ - wrapperColProps?: A.FormInstance['wrapperColProps'] - /** 标签对齐方式 */ - labelAlign?: A.FormInstance['labelAlign'] - /** 是否禁用表单 */ - disabled?: A.FormInstance['disabled'] - /** 表单校验规则 */ - rules?: A.FormInstance['rules'] - /** 自动标签宽度 */ - autoLabelWidth?: A.FormInstance['autoLabelWidth'] - /** 表单ID */ - id?: A.FormInstance['id'] - /** 是否滚动到第一个错误项 */ - scrollToFirstError?: A.FormInstance['scrollToFirstError'] - /** 表单列配置 */ - columns: FormColumnItem[] - /** 栅格布局属性 */ - gridProps?: A.GridProps - /** 栅格项属性 */ - gridItemProps?: GridItemProps - /** 是否为搜索模式 */ - search?: boolean - /** 默认是否折叠 */ - defaultCollapsed?: boolean - /** 搜索按钮文字 */ - searchBtnText?: string - /** 是否隐藏折叠按钮 */ - hideFoldBtn?: boolean - /** 是否显示后缀 */ - suffix?: boolean - /** 表单控制 */ - fc?: Record -} diff --git a/frontend-pc/src/components/GiIconBox/GiIconBox.vue b/frontend-pc/src/components/GiIconBox/GiIconBox.vue deleted file mode 100644 index 2d184bb..0000000 --- a/frontend-pc/src/components/GiIconBox/GiIconBox.vue +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/components/GiIconBox/index.ts b/frontend-pc/src/components/GiIconBox/index.ts deleted file mode 100644 index 255bffc..0000000 --- a/frontend-pc/src/components/GiIconBox/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import IconBox from './GiIconBox.vue' - -export * from './type' -export type IconBoxInstance = InstanceType -export { IconBox as GiIconBox } diff --git a/frontend-pc/src/components/GiIconBox/type.ts b/frontend-pc/src/components/GiIconBox/type.ts deleted file mode 100644 index 180d504..0000000 --- a/frontend-pc/src/components/GiIconBox/type.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** 组件属性定义 */ -export interface IconBoxProps { - color?: 'primary' | 'success' | 'warning' | 'danger' | string - size?: number - shape?: 'square' | 'round' -} diff --git a/frontend-pc/src/components/GiInputSearch/GiInputSearch.vue b/frontend-pc/src/components/GiInputSearch/GiInputSearch.vue deleted file mode 100644 index fbdf1e5..0000000 --- a/frontend-pc/src/components/GiInputSearch/GiInputSearch.vue +++ /dev/null @@ -1,50 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/GiInputSearch/index.ts b/frontend-pc/src/components/GiInputSearch/index.ts deleted file mode 100644 index 9e3fb86..0000000 --- a/frontend-pc/src/components/GiInputSearch/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import InputSearch from './GiInputSearch.vue' - -export type InputSearchInstance = InstanceType -export * from './type' -export { InputSearch as GiInputSearch } diff --git a/frontend-pc/src/components/GiInputSearch/type.ts b/frontend-pc/src/components/GiInputSearch/type.ts deleted file mode 100644 index b6cbf1c..0000000 --- a/frontend-pc/src/components/GiInputSearch/type.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface InputSearchProps { - placeholder?: string - disabled?: boolean - readonly?: boolean -} diff --git a/frontend-pc/src/components/GiOption/GiOption.vue b/frontend-pc/src/components/GiOption/GiOption.vue deleted file mode 100644 index 8a35cb8..0000000 --- a/frontend-pc/src/components/GiOption/GiOption.vue +++ /dev/null @@ -1,16 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/GiOption/GiOptionItem.vue b/frontend-pc/src/components/GiOption/GiOptionItem.vue deleted file mode 100644 index 72f9550..0000000 --- a/frontend-pc/src/components/GiOption/GiOptionItem.vue +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/components/GiOption/index.ts b/frontend-pc/src/components/GiOption/index.ts deleted file mode 100644 index 811323f..0000000 --- a/frontend-pc/src/components/GiOption/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import Option from './GiOption.vue' -import OptionItem from './GiOptionItem.vue' - -export type OptionInstance = InstanceType -export type OptionItemInstance = InstanceType -export * from './type' -export { Option as GiOption, OptionItem as GiOptionItem } diff --git a/frontend-pc/src/components/GiOption/type.ts b/frontend-pc/src/components/GiOption/type.ts deleted file mode 100644 index bbddcce..0000000 --- a/frontend-pc/src/components/GiOption/type.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** 组件属性定义 */ -export interface OptionItemProps { - /** 图标名称 */ - icon?: string - /** 选项文本 */ - label?: string - /** 是否显示更多图标 */ - more?: boolean - /** 是否激活状态 */ - active?: boolean -} diff --git a/frontend-pc/src/components/GiPageLayout/GiPageLayout.vue b/frontend-pc/src/components/GiPageLayout/GiPageLayout.vue deleted file mode 100644 index f2511cb..0000000 --- a/frontend-pc/src/components/GiPageLayout/GiPageLayout.vue +++ /dev/null @@ -1,192 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/GiPageLayout/SplitButton.vue b/frontend-pc/src/components/GiPageLayout/SplitButton.vue deleted file mode 100644 index 820ab64..0000000 --- a/frontend-pc/src/components/GiPageLayout/SplitButton.vue +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/components/GiPageLayout/index.ts b/frontend-pc/src/components/GiPageLayout/index.ts deleted file mode 100644 index 7050a1b..0000000 --- a/frontend-pc/src/components/GiPageLayout/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import PageLayout from './GiPageLayout.vue' - -export type PageLayoutInstance = InstanceType -export * from './type' -export { PageLayout as GiPageLayout } diff --git a/frontend-pc/src/components/GiPageLayout/type.ts b/frontend-pc/src/components/GiPageLayout/type.ts deleted file mode 100644 index e25e74e..0000000 --- a/frontend-pc/src/components/GiPageLayout/type.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { CSSProperties } from 'vue' - -/** 组件属性定义 */ -export interface PageLayoutProps { - size?: string - margin?: boolean - inner?: boolean - headerBordered?: boolean - leftStyle?: CSSProperties - headerStyle?: CSSProperties - bodyStyle?: CSSProperties - collapsed?: boolean - bgTransparent?: boolean -} diff --git a/frontend-pc/src/components/GiTable/GiTable.vue b/frontend-pc/src/components/GiTable/GiTable.vue deleted file mode 100644 index 5efa51b..0000000 --- a/frontend-pc/src/components/GiTable/GiTable.vue +++ /dev/null @@ -1,440 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/components/GiTable/index.ts b/frontend-pc/src/components/GiTable/index.ts deleted file mode 100644 index b717e3c..0000000 --- a/frontend-pc/src/components/GiTable/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { TableInstance as ArcoTableInstance } from '@arco-design/web-vue' -import Table from './GiTable.vue' - -export type * from './type' - -/** - * 组件实例类型。 - * - * ⚠️ 这里**不能**写 `InstanceType`。 - * `GiTable.vue` 是泛型 SFC(` - - diff --git a/frontend-pc/src/components/GiTableSetting/index.ts b/frontend-pc/src/components/GiTableSetting/index.ts deleted file mode 100644 index 048b334..0000000 --- a/frontend-pc/src/components/GiTableSetting/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import TableSetting from './GiTableSetting.vue' - -export type * from './type' -export type TableSettingInstance = InstanceType -export { TableSetting as GiTableSetting } diff --git a/frontend-pc/src/components/GiTableSetting/type.ts b/frontend-pc/src/components/GiTableSetting/type.ts deleted file mode 100644 index 53eb8f7..0000000 --- a/frontend-pc/src/components/GiTableSetting/type.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { TableInstance } from '@arco-design/web-vue' - -export interface GiTableSettingProps { - /** 工具栏标题 */ - title?: string - /** 列配置(用于列设置) */ - columns?: TableInstance['columns'] - /** 禁止控制显示隐藏的列 key */ - disabledColumnKeys?: string[] -} - -/** 列设置项类型 */ -export interface TableSettingColumnListItem { - title: string - key: string - show: boolean - disabled: boolean - /** 固定左侧 */ - fixedLeft?: boolean - /** 固定右侧 */ - fixedRight?: boolean -} diff --git a/frontend-pc/src/components/GiTableSetting/useTableColumnSetting.ts b/frontend-pc/src/components/GiTableSetting/useTableColumnSetting.ts deleted file mode 100644 index 5fab867..0000000 --- a/frontend-pc/src/components/GiTableSetting/useTableColumnSetting.ts +++ /dev/null @@ -1,141 +0,0 @@ -import type { TableColumnData, TableInstance } from '@arco-design/web-vue' -import type { MaybeRefOrGetter } from 'vue' -import type { TableSettingColumnListItem } from './type' -import { computed, ref, toValue, watch } from 'vue' - -export interface UseTableColumnSettingOptions { - columns: MaybeRefOrGetter - disabledColumnKeys?: MaybeRefOrGetter -} - -/** 获取列的 key */ -export function getTableColumnKey(column: TableColumnData, index?: number) { - if (column.dataIndex) return String(column.dataIndex) - if (typeof column.title === 'string' && column.title) return column.title - return `__column_${index ?? Date.now()}__` -} - -/** 表格列设置:显隐、排序、固定 */ -export function useTableColumnSetting(options: UseTableColumnSettingOptions) { - const showSettingColumnBtn = computed(() => { - const columns = Array.isArray(toValue(options.columns)) ? toValue(options.columns) : undefined - return Boolean(columns?.length) - }) - - const initialSettingColumns = computed(() => { - const raw = toValue(options.columns) - if (!raw) return [] - - const columns = Array.isArray(raw) ? raw : [] - return columns.map((column, index) => { - const key = getTableColumnKey(column, index) - const fixed = column.fixed - const disabledKeys = toValue(options.disabledColumnKeys) ?? [] - return { - key, - title: typeof column.title === 'string' ? column.title : '', - show: true, - disabled: disabledKeys.includes(key), - fixedLeft: fixed === 'left', - fixedRight: fixed === 'right' - } - }) - }) - - const settingColumnList = ref([]) - - const isColumnStructureMatch = ( - userColumns: TableSettingColumnListItem[], - initialColumns: TableSettingColumnListItem[] - ): boolean => { - if (userColumns.length === 0 || userColumns.length !== initialColumns.length) return false - const initialKeys = new Set(initialColumns.map((item) => item.key)) - const userKeys = new Set(userColumns.map((item) => item.key)) - return initialKeys.size === userKeys.size && [...initialKeys].every((key) => userKeys.has(key)) - } - - const columnMap = computed(() => { - const raw = toValue(options.columns) - if (!raw) return new Map() - const columns = Array.isArray(raw) ? raw : [] - return new Map(columns.map((col, index) => [getTableColumnKey(col, index), col])) - }) - - const currentSettingColumns = computed(() => { - const isValid = isColumnStructureMatch(settingColumnList.value, initialSettingColumns.value) - return isValid ? settingColumnList.value : initialSettingColumns.value - }) - - const resetSettingColumns = () => { - settingColumnList.value = [...initialSettingColumns.value] - } - - const ensureSettingColumnList = () => { - if (settingColumnList.value.length === 0 && initialSettingColumns.value.length > 0) { - settingColumnList.value = initialSettingColumns.value.map((item) => ({ ...item })) - } - } - - const toggleFixedLeft = (key: string) => { - ensureSettingColumnList() - settingColumnList.value = settingColumnList.value.map((item) => - item.key === key - ? { ...item, fixedLeft: !item.fixedLeft, fixedRight: false } - : item - ) - } - - const toggleFixedRight = (key: string) => { - ensureSettingColumnList() - settingColumnList.value = settingColumnList.value.map((item) => - item.key === key - ? { ...item, fixedRight: !item.fixedRight, fixedLeft: false } - : item - ) - } - - watch( - () => toValue(options.columns), - () => { - if (!isColumnStructureMatch(settingColumnList.value, initialSettingColumns.value)) { - resetSettingColumns() - } - }, - { deep: true } - ) - - const settingColumns = computed(() => { - const raw = toValue(options.columns) - if (!raw) return [] - - const shown = currentSettingColumns.value.filter((item) => item.show) - const leftFixed: typeof shown = [] - const noFixed: typeof shown = [] - const rightFixed: typeof shown = [] - for (const item of shown) { - if (item.fixedLeft) leftFixed.push(item) - else if (item.fixedRight) rightFixed.push(item) - else noFixed.push(item) - } - const ordered = [...leftFixed, ...noFixed, ...rightFixed] - - return ordered - .map((item) => { - const col = columnMap.value.get(item.key) - if (!col) return null - const fixed = item.fixedRight ? 'right' : item.fixedLeft ? 'left' : undefined - return { ...col, fixed } - }) - .filter(Boolean) as TableColumnData[] - }) - - return { - showSettingColumnBtn, - currentSettingColumns, - settingColumnList, - settingColumns, - resetSettingColumns, - toggleFixedLeft, - toggleFixedRight - } -} diff --git a/frontend-pc/src/components/GiTag/GiTag.tsx b/frontend-pc/src/components/GiTag/GiTag.tsx deleted file mode 100644 index 07ae1a4..0000000 --- a/frontend-pc/src/components/GiTag/GiTag.tsx +++ /dev/null @@ -1,135 +0,0 @@ -/** - * @file GiTag 组件 - 提供多样化的标签展示 - * @description 支持多种样式类型、状态、颜色和大小的标签组件 - */ - -import type { CSSProperties, PropType } from 'vue' -import type { TagProps as Props } from './type' -import { computed, defineComponent } from 'vue' -import './tag.scss' - -/** 基础颜色映射表 */ -const BASE_COLORS: Record = { - red: '#FF0000', - orangered: '#f77234', - orange: '#ff7d00', - gold: '#f7ba1e', - lime: '#9fdb1d', - green: '#00b42a', - cyan: '#14c9c9', - blue: '#3491fa', - purple: '#722ed1', - pink: '#f5319d', - gray: '#86909c' -} as const - -export default defineComponent({ - name: 'GiTag', - props: { - type: { - type: String as PropType, - default: 'light' - }, - status: { - type: String as PropType, - default: 'primary' - }, - color: { - type: String as PropType, - default: '' - }, - size: { - type: String as PropType, - default: 'small' - }, - closable: { - type: Boolean, - default: false - } - }, - emits: ['click', 'close'], - setup(props: Props, { slots, emit }) { - /** - * 将十六进制颜色转换为 RGB 对象 - * @param hex - 十六进制颜色值 - */ - const hexToRgb = (hex: string) => { - const cleanHex = hex.startsWith('#') ? hex.slice(1) : hex - return { - r: Number.parseInt(cleanHex.slice(0, 2), 16), - g: Number.parseInt(cleanHex.slice(2, 4), 16), - b: Number.parseInt(cleanHex.slice(4, 6), 16) - } - } - - /** 计算标签的 class */ - const className = computed(() => { - return [ - 'gi-tag', - props.type && `gi-tag__type--${props.type}`, - props.size && `gi-tag__size--${props.size}`, - props.status && `gi-tag__status--${props.status}` - ].filter(Boolean) - }) - - /** 计算标签的样式 */ - const calcStyle = computed((): CSSProperties => { - if (!props.color) return {} - - const color = BASE_COLORS[props.color as string] || props.color - const { r, g, b } = hexToRgb(color) - const style: CSSProperties = {} - - switch (props.type) { - case 'light': - style.color = color - style.backgroundColor = `rgba(${r}, ${g}, ${b}, 0.1)` - style['--tag-close-hover-bg-color'] = color - break - case 'dark': - style.color = '#fff' - style.backgroundColor = color - style['--tag-close-hover-color'] = color - style['--tag-close-hover-bg-color'] = 'rgba(255, 255, 255, 0.9)' - break - case 'outline': - style.color = color - style.backgroundColor = 'transparent' - style.borderColor = color - style['--tag-close-hover-bg-color'] = color - break - case 'light-outline': - style.color = color - style.backgroundColor = `rgba(${r}, ${g}, ${b}, 0.1)` - style.borderColor = `rgba(${r}, ${g}, ${b}, 0.2)` - style['--tag-close-hover-bg-color'] = color - break - } - - return style - }) - - /** 点击标签事件处理 */ - const handleClick = () => emit('click') - - /** 关闭标签事件处理 */ - const handleClose = (event: MouseEvent) => { - event.stopPropagation() - emit('close') - } - - /** 关闭图标组件 */ - const CloseIcon = props.closable && ( - - - - ) - - return () => ( - - {slots.default?.()} - {CloseIcon} - - ) - } -}) diff --git a/frontend-pc/src/components/GiTag/index.ts b/frontend-pc/src/components/GiTag/index.ts deleted file mode 100644 index fe51656..0000000 --- a/frontend-pc/src/components/GiTag/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import Tag from './GiTag.tsx' - -export type TagInstance = InstanceType -export * from './type' -export { Tag as GiTag } diff --git a/frontend-pc/src/components/GiTag/tag.scss b/frontend-pc/src/components/GiTag/tag.scss deleted file mode 100644 index ed27c19..0000000 --- a/frontend-pc/src/components/GiTag/tag.scss +++ /dev/null @@ -1,166 +0,0 @@ -$status: primary, success, warning, danger; - -$tag-size-mini-height: 20px; -$tag-size-small-height: 22px; -$tag-size-large-height: 24px; - -$tag-size-mini-padding: 0 6px; -$tag-size-small-padding: 0 8px; -$tag-size-large-padding: 0 10px; - -.gi-tag { - box-sizing: border-box; - display: inline-flex; - align-items: center; - justify-content: center; - height: $tag-size-small-height; - padding: $tag-size-small-padding; - font-size: 12px; - line-height: 1; - white-space: nowrap; - cursor: pointer; - border-radius: 3px; -} - -.gi-tag-close-btn { - position: relative; - box-sizing: border-box; - display: flex; - align-items: center; - justify-content: center; - width: 16px; - height: 16px; - margin-left: 4px; - cursor: pointer; - background-color: transparent; - border-radius: var(--border-radius-circle); - transition: background-color 0.1s cubic-bezier(0, 0, 1, 1); - - .close-icon { - z-index: 9; - width: 12px; - height: 12px; - } -} - -.gi-tag__size--mini { - height: $tag-size-mini-height; - padding: $tag-size-mini-padding; - - .gi-tag-close-btn { - .close-icon { - width: 10px; - height: 10px; - } - - &::before { - width: 14px; - height: 14px; - } - } -} - -.gi-tag__size--small { - height: $tag-size-small-height; - padding: $tag-size-small-padding; -} - -.gi-tag__size--large { - height: $tag-size-large-height; - padding: $tag-size-small-padding; -} - -.gi-tag__type--light { - color: #fff; - - @each $i in $status { - &.gi-tag__status--#{$i} - - { - color: rgb(var(--#{$i}-6)); - background-color: rgb(var(--#{$i}-1)); - - --tag-close-hover-color: #fff; - --tag-close-hover-bg-color: rgb(var(--#{$i}-6)); - - .gi-tag-close-btn { - &:hover { - color: var(--tag-close-hover-color); - background-color: var(--tag-close-hover-bg-color); - } - } - } - } -} - -.gi-tag__type--dark { - color: #fff; - - @each $i in $status { - &.gi-tag__status--#{$i} - - { - background-color: rgb(var(--#{$i}-6)); - - --tag-close-hover-color: rgb(var(--#{$i}-6)); - --tag-close-hover-bg-color: rgb(255 255 255 / 90%); - - .gi-tag-close-btn { - &:hover { - color: var(--tag-close-hover-color); - background-color: var(--tag-close-hover-bg-color); - } - } - } - } -} - -.gi-tag__type--outline { - background: transparent; - border-style: solid; - border-width: 1px; - - @each $i in $status { - &.gi-tag__status--#{$i} - - { - color: rgb(var(--#{$i}-6)); - border-color: rgb(var(--#{$i}-6)); - - --tag-close-hover-color: #fff; - --tag-close-hover-bg-color: rgb(var(--#{$i}-6)); - - .gi-tag-close-btn { - &:hover { - color: var(--tag-close-hover-color); - background-color: var(--tag-close-hover-bg-color); - } - } - } - } -} - -.gi-tag__type--light-outline { - border-style: solid; - border-width: 1px; - - @each $i in $status { - &.gi-tag__status--#{$i} - - { - color: rgb(var(--#{$i}-6)); - background-color: rgb(var(--#{$i}-1)); - border-color: rgb(var(--#{$i}-2)); - - --tag-close-hover-color: #fff; - --tag-close-hover-bg-color: rgb(var(--#{$i}-6)); - - .gi-tag-close-btn { - &:hover { - color: var(--tag-close-hover-color); - background-color: var(--tag-close-hover-bg-color); - } - } - } - } -} \ No newline at end of file diff --git a/frontend-pc/src/components/GiTag/type.ts b/frontend-pc/src/components/GiTag/type.ts deleted file mode 100644 index 91e282e..0000000 --- a/frontend-pc/src/components/GiTag/type.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** 组件属性定义 */ -export interface TagProps { - /** 标签类型 */ - type?: 'dark' | 'light' | 'outline' | 'light-outline' - /** 标签状态 */ - status?: 'primary' | 'success' | 'warning' | 'danger' | 'info' - /** 自定义颜色,支持预设颜色名或十六进制颜色值 */ - color?: 'red' | 'orangered' | 'orange' | 'gold' | 'lime' | 'green' | 'cyan' | 'blue' | 'purple' | 'pink' | 'gray' | string - /** 标签尺寸 */ - size?: 'mini' | 'small' | 'large' - /** 是否可关闭 */ - closable?: boolean -} diff --git a/frontend-pc/src/components/GiTenantPicker/GiTenantPicker.vue b/frontend-pc/src/components/GiTenantPicker/GiTenantPicker.vue deleted file mode 100644 index 3d8c7f4..0000000 --- a/frontend-pc/src/components/GiTenantPicker/GiTenantPicker.vue +++ /dev/null @@ -1,74 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/GiThemeBtn/GiThemeBtn.vue b/frontend-pc/src/components/GiThemeBtn/GiThemeBtn.vue deleted file mode 100644 index 19d2d05..0000000 --- a/frontend-pc/src/components/GiThemeBtn/GiThemeBtn.vue +++ /dev/null @@ -1,16 +0,0 @@ - - - diff --git a/frontend-pc/src/components/GiThemeBtn/index.ts b/frontend-pc/src/components/GiThemeBtn/index.ts deleted file mode 100644 index 228ec31..0000000 --- a/frontend-pc/src/components/GiThemeBtn/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import ThemeBtn from './GiThemeBtn.vue' - -export type ThemeBtnInstance = InstanceType -export { ThemeBtn as GiThemeBtn } diff --git a/frontend-pc/src/components/dialog/TreeUserList/Tree.vue b/frontend-pc/src/components/dialog/TreeUserList/Tree.vue deleted file mode 100644 index 3921133..0000000 --- a/frontend-pc/src/components/dialog/TreeUserList/Tree.vue +++ /dev/null @@ -1,45 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/dialog/TreeUserList/index.vue b/frontend-pc/src/components/dialog/TreeUserList/index.vue deleted file mode 100644 index 91f27d0..0000000 --- a/frontend-pc/src/components/dialog/TreeUserList/index.vue +++ /dev/null @@ -1,93 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/dialog/UserList/index.vue b/frontend-pc/src/components/dialog/UserList/index.vue deleted file mode 100644 index 9645e20..0000000 --- a/frontend-pc/src/components/dialog/UserList/index.vue +++ /dev/null @@ -1,89 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/dialog/index.ts b/frontend-pc/src/components/dialog/index.ts deleted file mode 100644 index 3d3ffd5..0000000 --- a/frontend-pc/src/components/dialog/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { ListItem as UserListItem } from '@/apis/person' -import { createSelectDialog } from '@/utils/createSelectDialog' -import TreeUserList from './TreeUserList/index.vue' -import UserList from './UserList/index.vue' - -/** 选择用户列表的对话框 */ -export const SelectUserListDialog = createSelectDialog({ - title: '选择用户', - component: UserList -}) - -/** 左树右表的对话框 */ -export const SelectTreeUserListDialog = createSelectDialog({ - title: '左树右表对话框示例', - component: TreeUserList -}) diff --git a/frontend-pc/src/components/icons/Icon403.vue b/frontend-pc/src/components/icons/Icon403.vue deleted file mode 100644 index 6f0abf4..0000000 --- a/frontend-pc/src/components/icons/Icon403.vue +++ /dev/null @@ -1,625 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/icons/Icon404.vue b/frontend-pc/src/components/icons/Icon404.vue deleted file mode 100644 index e6972fd..0000000 --- a/frontend-pc/src/components/icons/Icon404.vue +++ /dev/null @@ -1,363 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/icons/Icon500.vue b/frontend-pc/src/components/icons/Icon500.vue deleted file mode 100644 index 91b82a2..0000000 --- a/frontend-pc/src/components/icons/Icon500.vue +++ /dev/null @@ -1,967 +0,0 @@ - - - - - diff --git a/frontend-pc/src/components/index.ts b/frontend-pc/src/components/index.ts deleted file mode 100644 index e735932..0000000 --- a/frontend-pc/src/components/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 组件库出口。 - * - * ⚠️ 这里的每一行都必须对应 `src/components/` 下的真实目录。 - * 剥离 gi-demo 演示页时删掉过 6 个演示专用组件 - * (GiCodeButton / GiCodeView / GiFooter / GiIconSelector / GiIframe / GiTrend), - * 但出口文件没同步 —— `export * from './已删除的组件'` 在构建时**不会报错** - * (Vite 只在使用到该导出时才解析),直到某天有人 import 它才炸。 - * 所以增删组件时务必回来同步这一份清单。 - */ -export * from './GiButton' -export * from './GiCell' -export * from './GiDot' -export * from './GiEditTable' -export * from './GiFlex' -export * from './GiForm' -export * from './GiIconBox' -export * from './GiInputSearch' -export * from './GiOption' -export * from './GiPageLayout' -export * from './GiTable' -export * from './GiTableSetting' -export * from './GiTag' -export * from './GiThemeBtn' diff --git a/frontend-pc/src/composables/pagination.ts b/frontend-pc/src/composables/pagination.ts new file mode 100644 index 0000000..de676d7 --- /dev/null +++ b/frontend-pc/src/composables/pagination.ts @@ -0,0 +1,37 @@ +import type { PaginationProps } from 'naive-ui' + +/** + * 列表页分页器的统一参数。 + * + * ## 为什么要有这个文件 + * 手写 `pagination` 对象时,`prefix` 的类型最容易写错: + * ```ts + * // ✗ 这样写 vue-tsc 会报 TS2322:naive 给的 info 里 itemCount 是 `number | undefined` + * pagination: { prefix: ({ itemCount }: { itemCount: number }) => `共 ${itemCount} 条` } + * ``` + * 报错信息很长(说的是 `RenderPrefix` 不兼容),而真正的原因只是**少了个 `?`**。 + * 三个列表页各踩一次不值得,所以收口到这里。 + * + * ## 另一个踩过的坑(与类型无关,但同样只在运行时暴露) + * naive 的 `n-data-table` 在 `pagination` 非 false 时会**自己再切一次数据** + * (内部 `flattenData`)。所以远程分页必须同时: + * ① `remote` 打开;② `:pagination` 的 `page`/`pageSize` 由**我们**控制; + * ③ 处理 `@update:page` 与 `@update:page-size` **两个**事件并重新取数。 + * 只改本地每页条数不重新取数,就会出现"分页器说 50 条/页、表里还是 20 条"。 + */ +export function listPagination(opts: { + page: number + pageSize: number + itemCount: number + pageSizes?: number[] +}): PaginationProps { + return { + page: opts.page, + pageSize: opts.pageSize, + itemCount: opts.itemCount, + showSizePicker: true, + pageSizes: opts.pageSizes ?? [10, 20, 50, 100], + // ⚠️ `itemCount?: number` 里的 `?` 是必需的(见上方注释) + prefix: (info: { itemCount?: number }) => `共 ${info.itemCount ?? 0} 条` + } +} diff --git a/frontend-pc/src/config/setting.json b/frontend-pc/src/config/setting.json deleted file mode 100644 index c8cd449..0000000 --- a/frontend-pc/src/config/setting.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "themeColor": "#0052D9", - "layout": "left", - "tabStyle": "card", - "isTabVisible": true, - "transitionName": "zoom-fade", - "isTransitionEnabled": true, - "isMenuCollapsed": false, - "isMenuAccordion": false, - "isMenuDark": false -} diff --git a/frontend-pc/src/config/xt-routes.ts b/frontend-pc/src/config/xt-routes.ts index 7f4a72d..c6d21ec 100644 --- a/frontend-pc/src/config/xt-routes.ts +++ b/frontend-pc/src/config/xt-routes.ts @@ -1,294 +1,189 @@ /** - * 业务菜单与路由表。 + * 业务菜单表 —— **这一张表同时驱动三件事**:左侧菜单、路由注册、面包屑标题。 * - * 设计:**这一张表同时驱动三件事** —— 左侧菜单、路由注册、权限提示。 * 三者若各写一份,改一处漏一处几乎必然发生(表现是"菜单点进去 404"或"有权限但不显示")。 * - * 菜单可见性由**后端**决定:`/auth/me` 返回 `menus: string[]`(菜单键集合), + * 菜单可见性由**后端**决定:`/auth/me` 与登录响应返回 `menus: string[]`(菜单键集合), * 这里按菜单键过滤。真正的访问控制也在后端(绕过前端只会拿到 40301), * 前端的过滤只是"不显示没权限的入口"。 * - * ⚠️ `key` 必须与后端 `BuiltinRole` 里的菜单键逐字一致。 + * ⚠️ `key` 必须与后端 `BuiltinRole` 里的菜单键**逐字一致** —— + * `scripts/check-wiring.py` 会双向核对,对不上会红。 + * ⚠️ 2026-10 需求收敛:删掉了 `join`(加入申请)与 `cleanup`(历史整理器)两个菜单键, + * 后端 `BuiltinRole` 同步删除;权限码也去掉了 join:audit / cleanup:run。 */ -import type { UserRouteItem } from '@/apis/user' /** 单个菜单定义 */ export interface XtMenu { /** 菜单键,与后端一致 */ key: string title: string - /** iconify 图标名(icon-park-outline 集) */ + /** 图标短名(见 @/icons/icon-data.ts) */ icon: string /** 路由路径(完整,以 / 开头) */ path: string - /** 视图路径(相对 src/views,不带扩展名);分组节点为空 */ - component?: string - /** 分组标题;null 表示不分组 */ - group: string | null - sort: number - /** 一期是否已实现。false 的走「待建」页,比直接隐藏更诚实 */ - ready: boolean - /** 待建说明 */ - note?: string + /** 视图路径(相对 src/views,不带扩展名) */ + component: string + /** 所属分组 */ + group: string } -/** 分组顺序(决定左侧菜单顺序) */ -const GROUPS: Array<{ id: string, title: string, icon: string, sort: number }> = [ - { id: 'collect', title: '收集管理', icon: 'icon-park-outline:application-two', sort: 10 }, - { id: 'setting', title: '配置', icon: 'icon-park-outline:setting-two', sort: 20 }, - { id: 'org', title: '组织与账号', icon: 'icon-park-outline:every-user', sort: 30 }, - { id: 'platform', title: '平台运维', icon: 'icon-park-outline:server', sort: 40 } +/** 分组(决定左侧菜单顺序) */ +export interface XtMenuGroup { + id: string + title: string + icon: string + sort: number +} + +export const GROUPS: XtMenuGroup[] = [ + { id: 'collect', title: '收集管理', icon: 'collect', sort: 10 }, + { id: 'setting', title: '配置', icon: 'setting', sort: 20 }, + { id: 'org', title: '组织与账号', icon: 'org-group', sort: 30 }, + { id: 'platform', title: '平台运维', icon: 'platform', sort: 40 } ] -/** 业务菜单(工作台在 baseRoutes 里,不列这里) */ +/** 工作台不在分组里(所有角色都有,且是落地页) */ +export const DASHBOARD: XtMenu = { + key: 'dashboard', + title: '工作台', + icon: 'dashboard', + path: '/dashboard', + component: 'dashboard/index', + group: '' +} + +/** 业务菜单(按分组顺序排列) */ export const XT_MENUS: XtMenu[] = [ // ── 收集管理 ────────────────────────────────────── { key: 'task', title: '收集任务', - icon: 'icon-park-outline:list-two', + icon: 'task', path: '/collect/task', component: 'task/index', - group: 'collect', - sort: 11, - ready: true - }, - { - key: 'join', - title: '加入申请', - icon: 'icon-park-outline:user-to-user-transmission', - path: '/collect/join', - component: 'join/index', - group: 'collect', - sort: 13, - ready: true + group: 'collect' }, { key: 'audit', title: '审核', - icon: 'icon-park-outline:check-one', + icon: 'audit', path: '/collect/audit', component: 'audit/index', - group: 'collect', - sort: 14, - ready: true + group: 'collect' }, { key: 'export', title: '导出中心', - icon: 'icon-park-outline:download-two', + icon: 'export', path: '/collect/export', component: 'export/index', - group: 'collect', - sort: 15, - ready: true + group: 'collect' }, { - // 通知记录:把「通知到底发出去没有」变成可回答的(§13.3)。 - // 刻意**不**并到审核页里 —— 它是运营视角的排查工具, - // 而审核页是老师视角的操作台,两者的"该看什么"完全不同 + // 通知记录:把「通知到底发出去没有」变成可回答的。 + // 刻意**不**并到审核页里 —— 它是运营视角的排查工具,而审核页是老师视角的操作台。 key: 'notify', title: '通知记录', - icon: 'icon-park-outline:remind', + icon: 'notify', path: '/collect/notify', component: 'notify/index', - group: 'collect', - sort: 17, - ready: true + group: 'collect' }, // ── 配置 ───────────────────────────────────────── { - key: 'cleanup', - title: '历史整理器', - icon: 'icon-park-outline:magic-wand', - path: '/setting/cleanup', - component: 'cleanup/index', - group: 'setting', - sort: 23, - ready: true - }, - { - // 字典管理(§13.3)。内置项照抄官方表格、不可改名(只可停用单项), - // 所以这一屏的主要用途是"追加客户自己的取值"(例如多了个学历写法) + // 字典管理:内置项照抄官方表格、不可改名(只可停用单项), + // 所以这一屏的主要用途是"追加客户自己的取值"。 key: 'dict', title: '字典管理', - icon: 'icon-park-outline:list-two', + icon: 'dict', path: '/setting/dict', component: 'dict/index', - group: 'setting', - sort: 24, - ready: true + group: 'setting' }, // ── 组织与账号 ──────────────────────────────────── { key: 'org', title: '组织架构', - icon: 'icon-park-outline:tree-list', + icon: 'org', path: '/org/manage', component: 'org/index', - group: 'org', - sort: 31, - ready: true + group: 'org' }, { key: 'user', title: '账号管理', - icon: 'icon-park-outline:peoples', + icon: 'user', path: '/org/user', component: 'user/index', - group: 'org', - sort: 32, - ready: true + group: 'org' }, { + // 内置角色只读展示(权限由后端代码决定)+ 自定义角色的权限码/菜单可配。 + // 改授权立即生效(服务端写授权时清缓存),不需要重新登录。 key: 'role', title: '角色与授权', - icon: 'icon-park-outline:permissions', + icon: 'role', path: '/org/role', component: 'role/index', - group: 'org', - sort: 33, - ready: false, - note: '内置角色的权限由代码决定、不可改;自定义角色属二期。' + group: 'org' }, // ── 平台运维 ────────────────────────────────────── { key: 'tenant', title: '租户管理', - icon: 'icon-park-outline:school', + icon: 'tenant', path: '/platform/tenant', component: 'tenant/index', - group: 'platform', - sort: 41, - ready: true + group: 'platform' }, { key: 'log', title: '操作日志', - icon: 'icon-park-outline:log', + icon: 'log', path: '/platform/log', component: 'log/index', - group: 'platform', - sort: 42, - ready: true, - note: '登录、查看明文、下载、导出、代传、删除的审计记录。' + group: 'platform' } ] -/** 构造一个「待建」路由项(组件统一指向 coming-soon) */ -function placeholder(menu: XtMenu): UserRouteItem { - return { - id: menu.key, - parentId: '0', - path: menu.path, - component: 'coming-soon/index', - title: menu.title, - icon: menu.icon, - sort: menu.sort, - type: 2, - status: '1', - hidden: false, - keepAlive: false, - affix: false, - breadcrumb: true, - showInTabs: true, - alwaysShow: false, - activeMenu: '', - permission: '', - roles: [], - redirect: '', - children: [] - } as UserRouteItem +/** 按后端下发的菜单键过滤出可见菜单 */ +export function filterMenus(menuKeys: string[]): XtMenu[] { + const keys = new Set(menuKeys ?? []) + return XT_MENUS.filter((m) => keys.has(m.key)) } -/** - * 按后端下发的菜单键,生成异步路由树。 - * - * @param menuKeys `/auth/me` 返回的菜单键集合 - * @returns 分组 → 页面的两级路由树(`component: 'Layout'` 由上层 formatAsyncRoutes 处理) - */ -export function buildXtRoutes(menuKeys: string[]): UserRouteItem[] { - const keys = new Set(menuKeys) - const visible = XT_MENUS.filter((m) => keys.has(m.key)) - if (!visible.length) { - return [] +/** 可见菜单按分组归拢(左侧菜单用;空分组不返回) */ +export function visibleGroups(menuKeys: string[]): Array { + const visible = filterMenus(menuKeys) + return GROUPS + .map((g) => ({ ...g, items: visible.filter((m) => m.group === g.id) })) + .filter((g) => g.items.length > 0) +} + +/** 按路径取菜单(面包屑/标题用;工作台也在内) */ +export function menuByPath(path: string): XtMenu | undefined { + if (path === DASHBOARD.path) { + return DASHBOARD } - - const routes: UserRouteItem[] = [] - for (const g of GROUPS) { - const items = visible.filter((m) => m.group === g.id) - if (!items.length) { - continue - } - routes.push({ - id: g.id, - parentId: '0', - path: `/${g.id}`, - // 'Layout' 是约定的布局组件标识,formatAsyncRoutes 会替换成真实组件 - component: 'Layout', - title: g.title, - icon: g.icon, - sort: g.sort, - type: 1, - status: '1', - hidden: false, - keepAlive: false, - affix: false, - breadcrumb: true, - showInTabs: false, - alwaysShow: true, - activeMenu: '', - permission: '', - roles: [], - redirect: items[0].path, - children: items - .sort((a, b) => a.sort - b.sort) - .map((m) => (m.ready ? readyItem(m) : placeholder(m))) - } as UserRouteItem) - } - return routes -} - -/** 已实现页面 */ -function readyItem(menu: XtMenu): UserRouteItem { - return { - id: menu.key, - parentId: menu.group ?? '0', - path: menu.path, - component: menu.component!, - title: menu.title, - icon: menu.icon, - sort: menu.sort, - type: 2, - status: '1', - hidden: false, - keepAlive: false, - affix: false, - breadcrumb: true, - showInTabs: true, - alwaysShow: false, - activeMenu: '', - permission: '', - roles: [], - redirect: '', - children: [] - } as UserRouteItem -} - -/** 按路由路径取菜单定义(「待建」页要显示说明用) */ -export function findMenuByPath(path: string): XtMenu | undefined { return XT_MENUS.find((m) => m.path === path) } -/** 角色码 → 中文名(与后端 BuiltinRole 对齐) */ +/** 角色码 → 中文名(与后端 `BuiltinRole` 对齐;2026-10 收敛后只剩 4 个) */ export const ROLE_NAMES: Record = { PLATFORM_ADMIN: '平台运营', SCHOOL_ADMIN: '学校管理员', HEAD_TEACHER: '班主任', - DEPUTY_TEACHER: '副班主任', - AUDITOR: '审核员', STUDENT: '学生' } + +/** 角色码数组 → 中文名(多角色用「、」连接) */ +export function roleLabel(codes?: string[] | null): string { + if (!codes || !codes.length) { + return '—' + } + return codes.map((c) => ROLE_NAMES[c] ?? c).join('、') +} diff --git a/frontend-pc/src/directives/hasPerm.ts b/frontend-pc/src/directives/hasPerm.ts deleted file mode 100644 index b615b59..0000000 --- a/frontend-pc/src/directives/hasPerm.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { Directive, DirectiveBinding } from 'vue' -import { hasPermOr } from '@/utils/permission' - -/** - * 权限检查函数 - * @description 检查元素是否具有指定的权限,如果没有权限则移除该元素 - * @param el - 要检查的 DOM 元素 - * @param binding - 指令绑定对象 - * @throws {Error} 当权限参数格式不正确时抛出错误 - */ -function checkPermission(el: HTMLElement, binding: DirectiveBinding) { - const { value } = binding - - // 检查权限参数是否有效 - if (!value || !Array.isArray(value) || value.length === 0) { - throw new Error('权限参数格式错误,示例:v-hasPerm="[\'home:btn:edit\',\'home:btn:delete\']"') - } - - // 检查是否具有权限 - const hasPermission = hasPermOr(value) - if (!hasPermission) { - el.parentNode?.removeChild(el) - } -} - -/** - * 权限指令 - * @description 用于控制元素的显示/隐藏,基于用户权限 - * @example - * ```vue - * - * - * ``` - */ -const hasPermDirective: Directive = { - mounted(el: HTMLElement, binding: DirectiveBinding) { - checkPermission(el, binding) - }, - updated(el: HTMLElement, binding: DirectiveBinding) { - checkPermission(el, binding) - } -} - -export default hasPermDirective diff --git a/frontend-pc/src/directives/hasRole.ts b/frontend-pc/src/directives/hasRole.ts deleted file mode 100644 index 0499fd6..0000000 --- a/frontend-pc/src/directives/hasRole.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { Directive, DirectiveBinding } from 'vue' -import { hasRoleOr } from '@/utils/permission' - -/** - * 角色检查函数 - * @description 检查元素是否具有指定的角色,如果没有角色则移除该元素 - * @param el - 要检查的 DOM 元素 - * @param binding - 指令绑定对象 - * @throws {Error} 当角色参数格式不正确时抛出错误 - */ -function checkRole(el: HTMLElement, binding: DirectiveBinding) { - const { value } = binding - - // 检查角色参数是否有效 - if (!value || !Array.isArray(value) || value.length === 0) { - throw new Error('角色参数格式错误,示例:v-hasRole="[\'admin\',\'user\']"') - } - - // 检查是否具有角色 - const hasRole = hasRoleOr(value) - if (!hasRole) { - el.parentNode?.removeChild(el) - } -} - -/** - * 角色权限指令 - * @description 用于控制元素的显示/隐藏,基于用户角色 - * @example - * ```vue - *
管理员可见
- *
管理员或用户可见
- * ``` - */ -const hasRoleDirective: Directive = { - mounted(el: HTMLElement, binding: DirectiveBinding) { - checkRole(el, binding) - }, - updated(el: HTMLElement, binding: DirectiveBinding) { - checkRole(el, binding) - } -} - -export default hasRoleDirective diff --git a/frontend-pc/src/directives/index.ts b/frontend-pc/src/directives/index.ts deleted file mode 100644 index b50238e..0000000 --- a/frontend-pc/src/directives/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { App } from 'vue' -import hasPerm from './hasPerm' -import hasRole from './hasRole' - -export default { - install(Vue: App) { - Vue.directive('hasPerm', hasPerm) - Vue.directive('hasRole', hasRole) - } -} diff --git a/frontend-pc/src/hooks/app/index.ts b/frontend-pc/src/hooks/app/index.ts deleted file mode 100644 index 42d70dd..0000000 --- a/frontend-pc/src/hooks/app/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './useDept' -export * from './useRole' diff --git a/frontend-pc/src/hooks/app/useDept.ts b/frontend-pc/src/hooks/app/useDept.ts deleted file mode 100644 index 94c7395..0000000 --- a/frontend-pc/src/hooks/app/useDept.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type * as T from '@/apis/system/dept' -import { ref } from 'vue' -import { mapTree } from 'xe-utils' -import { baseAPI } from '@/apis/system/dept' - -/** 部门模块 */ -export function useDept(options?: { onSuccess?: () => void }) { - const loading = ref(false) - const deptList = ref([]) - - const getDeptList = async () => { - try { - loading.value = true - const res = await baseAPI.getList() - deptList.value = mapTree(res.data, (i) => { - if (i.children?.length) { - i.children = i.children.filter((i) => i.status === '1') - } - return i - }) - options?.onSuccess && options.onSuccess() - } finally { - loading.value = false - } - } - return { deptList, getDeptList, loading } -} diff --git a/frontend-pc/src/hooks/app/useRole.ts b/frontend-pc/src/hooks/app/useRole.ts deleted file mode 100644 index ce70ad1..0000000 --- a/frontend-pc/src/hooks/app/useRole.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type * as T from '@/apis/system/role' -import { ref } from 'vue' -import { baseAPI } from '@/apis/system/role' - -/** 角色模块 */ -export function useRole() { - const loading = ref(false) - const roleList = ref([]) - const total = ref(0) - const getRoleList = async () => { - try { - loading.value = true - const res = await baseAPI.getList({ page: 1, size: 99 }) - roleList.value = res.data.records.filter((i) => i.status === '1') - total.value = res.data.total - } finally { - loading.value = false - } - } - return { roleList, getRoleList, loading, total } -} diff --git a/frontend-pc/src/hooks/index.ts b/frontend-pc/src/hooks/index.ts deleted file mode 100644 index b5656f1..0000000 --- a/frontend-pc/src/hooks/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export * from './useBreakpoint' -export * from './useChart' -export * from './useDevice' -export * from './useDict' -export * from './useLoading' -export * from './usePagination' -export * from './useRequest' -export * from './useResetReactive' -export * from './useRouteListener' -export * from './useTable' -export * from './useTheme' diff --git a/frontend-pc/src/hooks/useBreakpoint.ts b/frontend-pc/src/hooks/useBreakpoint.ts deleted file mode 100644 index f884de9..0000000 --- a/frontend-pc/src/hooks/useBreakpoint.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { ColProps } from '@arco-design/web-vue' -import type { ComputedRef } from 'vue' -import { useBreakpoints } from '@vueuse/core' -import { computed } from 'vue' - -type ColBreakpoint = Pick -type Breakpoint = keyof ColBreakpoint - -// 定义断点配置 -const BREAKPOINT_CONFIG = { - xs: 576, // <576px - sm: 576, // ≥576px - md: 768, // ≥768px - lg: 992, // ≥992px - xl: 1200, // ≥1200px - xxl: 1600 // ≥1600px -} as const - -/** - * 响应式断点 Hook - * @returns {{ breakpoint: ComputedRef }} 当前激活的最大断点值 - */ -export function useBreakpoint() { - const breakpoints = useBreakpoints(BREAKPOINT_CONFIG) - - const currentBreakpoints = breakpoints.current() as ComputedRef - - const breakpoint = computed(() => { - const arr = currentBreakpoints.value - // arr.at(-1) 的类型是 Breakpoint | undefined —— 空数组时就是 undefined。 - // 原来写成 `arr.length ? arr.at(-1) : 'xs'` 是个类型谎: - // 长度判断并不能让 TS 窄化 at() 的结果,严格模式下会报 TS2769。 - return arr.length ? (arr.at(-1) ?? 'xs') : 'xs' - }) - - return { breakpoint } -} diff --git a/frontend-pc/src/hooks/useChart.ts b/frontend-pc/src/hooks/useChart.ts deleted file mode 100644 index 2222113..0000000 --- a/frontend-pc/src/hooks/useChart.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { EChartsOption } from 'echarts' -import { computed } from 'vue' -import { useTheme } from '@/hooks' - -interface Options { - (isDark?: boolean): EChartsOption -} - -/** - * 用于集中管理 ECharts 配置,提供完整类型提示 - * @param sourceOption - ECharts 配置项 - * @returns { option: ComputedRef, theme: ComputedRef } - 返回 ECharts 配置项和主题 - */ -export function useChart(sourceOption: Options) { - const { isDark } = useTheme() - - // echarts support https://echarts.apache.org/zh/theme-builder.html - const option = computed(() => { - return sourceOption(isDark.value) - }) - - const theme = computed(() => isDark.value ? 'dark' : undefined) - - return { - /** ECharts 配置项 */ - option, - /** ECharts 主题 */ - theme - } -} diff --git a/frontend-pc/src/hooks/useDevice.ts b/frontend-pc/src/hooks/useDevice.ts deleted file mode 100644 index 0a8305e..0000000 --- a/frontend-pc/src/hooks/useDevice.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { useWindowSize } from '@vueuse/core' -import { computed } from 'vue' - -/** - * 响应式布局容器固定宽度 - * - * 大屏(>=1200px) - * 中屏(>=992px) - * 小屏(>=768px) - */ -export function useDevice() { - const { width } = useWindowSize() - const isDesktop = computed(() => width.value > 768) - const isMobile = computed(() => !isDesktop.value) - - return { isMobile, isDesktop } -} diff --git a/frontend-pc/src/hooks/useDict.ts b/frontend-pc/src/hooks/useDict.ts deleted file mode 100644 index c2f0dae..0000000 --- a/frontend-pc/src/hooks/useDict.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { SelectOptionData } from '@arco-design/web-vue' -import { computed } from 'vue' -import { useDictStore } from '@/stores' - -/** - * 将字符串数组转换为对象类型 - * @example - * ArrayToObject<['STATUS', 'GENDER']> // { STATUS: SelectOptionData[], GENDER: SelectOptionData[] } - */ -type ArrayToObject = { - [K in T[number]]: SelectOptionData[] -} - -/** - * 字典数据 Hook - * @description 根据传入的字典代码数组,返回对应的字典数据对象 - * @param codes - 字典代码数组,会自动推断字面量类型以提供类型提示 - * @returns 返回包含字典数据的响应式对象,对象的键为 codes 中的元素 - * @example - * const { dictData } = useDict(['STATUS', 'GENDER'] as const) - * // dictData.value.STATUS ✅ 有类型提示 - * // dictData.value.GENDER ✅ 有类型提示 - * // dictData.value.OTHER ❌ TypeScript 会报错 - */ -export function useDict(codes: T) { - const dictStore = useDictStore() - - const dictData = computed>(() => { - const result = {} as ArrayToObject - for (const code of codes) { - result[code as T[number]] = dictStore.dictData[code] || [] - } - return result - }) - - return { dictData } -} diff --git a/frontend-pc/src/hooks/useLoading.ts b/frontend-pc/src/hooks/useLoading.ts deleted file mode 100644 index 3e49152..0000000 --- a/frontend-pc/src/hooks/useLoading.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { ref } from 'vue' - -/** - * 加载状态管理 Hook - * @description 提供加载状态管理的响应式 Hook,包含加载状态的设置、切换等功能 - * @param initValue - 初始加载状态,默认为 false - * @returns 返回包含加载状态管理和操作方法的对象 - * {{ - * loading: Ref, // 加载状态的响应式引用 - * setLoading: (value: boolean) => void, // 设置加载状态的函数 - * toggle: () => void // 切换加载状态的函数 - * }} - */ -export function useLoading(initValue = false) { - const loading = ref(initValue) - - const setLoading = (value: boolean) => { - loading.value = value - } - - const toggle = () => { - loading.value = !loading.value - } - - return { - loading, - setLoading, - toggle - } -} diff --git a/frontend-pc/src/hooks/usePagination.ts b/frontend-pc/src/hooks/usePagination.ts deleted file mode 100644 index 718506d..0000000 --- a/frontend-pc/src/hooks/usePagination.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { reactive, toRefs, watch } from 'vue' -import { useBreakpoint } from '@/hooks' - -type Callback = () => void - -type Options = { - defaultPageSize: number -} - -/** - * 分页管理 Hook - * @description 提供分页状态管理的响应式 Hook,支持响应式断点,自动适配移动端显示 - * @param callback - 分页变化时的回调函数 - * @param options - 分页配置选项,默认每页显示 10 条 - * @returns 包含分页状态和操作方法的响应式对象 - * {{ - * current: Ref, // 当前页码 - * pageSize: Ref, // 每页显示数量 - * total: Ref, // 总数据量 - * pagination: Object, // 完整的分页配置对象 - * changeCurrent: (page: number) => void, // 改变当前页码 - * changePageSize: (size: number) => void, // 改变每页显示数量 - * setTotal: (value: number) => void // 设置总数据量 - * }} - * @example - * const { pagination, setTotal } = usePagination(() => { - * // 分页变化时的回调 - * fetchData() - * }) - */ -export function usePagination(callback: Callback, options: Options = { defaultPageSize: 10 }) { - // 获取响应式断点 - const { breakpoint } = useBreakpoint() - - // 创建响应式分页配置对象 - const pagination = reactive({ - showPageSize: true, - showTotal: true, - current: 1, - pageSize: options.defaultPageSize, - total: 0, - simple: false, - onChange: (size: number) => { - pagination.current = size - callback && callback() - }, - onPageSizeChange: (size: number) => { - pagination.current = 1 - pagination.pageSize = size - callback && callback() - } - }) - - // 监听断点变化,自动适配移动端显示 - watch( - () => breakpoint.value, - () => { - // 在 xs(移动端)断点下使用简单模式 - pagination.simple = ['xs'].includes(breakpoint.value) - // 在 xs(移动端)断点下不显示总数据量 - pagination.showTotal = !['xs'].includes(breakpoint.value) - }, - { immediate: true } - ) - - // 导出便捷方法 - const changeCurrent = pagination.onChange - const changePageSize = pagination.onPageSizeChange - - /** - * 设置总数据量 - * @param value - 新的总数据量 - */ - function setTotal(value: number) { - pagination.total = value - } - - // 导出响应式引用 - const { current, pageSize, total } = toRefs(pagination) - - return { - current, - pageSize, - total, - pagination, - changeCurrent, - changePageSize, - setTotal - } -} diff --git a/frontend-pc/src/hooks/useRequest.ts b/frontend-pc/src/hooks/useRequest.ts deleted file mode 100644 index 96ad4c2..0000000 --- a/frontend-pc/src/hooks/useRequest.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { ref } from 'vue' -import { useLoading } from '@/hooks' - -/** - * 请求配置选项 - */ -interface UseRequestOptions { - /** 默认值 */ - defaultValue?: T - /** 是否立即执行请求,默认为 true */ - immediate?: boolean - /** 初始加载状态,默认为 true */ - isLoading?: boolean - /** 请求成功回调 */ - onSuccess?: (data: T) => void - /** 请求失败回调 */ - onError?: (error: any) => void -} - -/** - * 请求管理 Hook - * @description 提供请求状态管理的响应式 Hook,支持自动/手动触发、错误处理、刷新等功能 - * @template T - 响应数据类型 - * @param api - API 请求函数,返回 Promise> - * @param options - 请求配置选项 - * @returns 返回包含请求状态和操作方法的对象 - * {{ - * loading: Ref, // 加载状态的响应式引用 - * data: Ref, // 响应数据的响应式引用 - * error: Ref, // 错误信息的响应式引用 - * run: () => Promise, // 手动触发请求的函数 - * }} - * @example - * // 基础用法 - 立即执行 - * const { loading, data } = useRequest(() => getUserInfo()) - * - * // 手动触发 - * const { loading, data, run } = useRequest(() => getUserInfo(), { immediate: false }) - * run() - * - * // 带默认值和回调 - * const { loading, data } = useRequest( - * () => getUserInfo(), - * { - * defaultValue: {}, - * onSuccess: (data) => console.log('成功', data), - * onError: (error) => console.error('失败', error) - * } - * ) - */ -export function useRequest( - api: () => Promise>, - options: UseRequestOptions = {} -) { - const { defaultValue, immediate = true, isLoading = true, onSuccess, onError } = options - const { loading, setLoading } = useLoading(isLoading) - const data = ref(defaultValue as T) - const error = ref(null) - - /** - * 执行请求 - * @description 调用 API 并更新响应数据,处理加载状态和错误 - */ - async function run() { - try { - error.value = null - setLoading(true) - const res = await api() - data.value = res.data as T - onSuccess?.(res.data) - } catch (err) { - error.value = err - onError?.(err) - } finally { - setLoading(false) - } - } - - // 立即执行请求 - if (immediate) { - run() - } - - return { - loading, - data, - error, - run - } -} diff --git a/frontend-pc/src/hooks/useResetReactive.ts b/frontend-pc/src/hooks/useResetReactive.ts deleted file mode 100644 index fec7ca2..0000000 --- a/frontend-pc/src/hooks/useResetReactive.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { cloneDeep } from 'lodash-es' -import { reactive } from 'vue' - -/** - * 响应式状态重置 Hook - * @description 创建一个可重置的响应式对象,支持将状态重置为初始值 - * @template T - 状态对象的类型 - * @param initialValue - 初始状态值 - * @returns [state, reset] - 返回响应式状态和重置函数的元组 - * @example - * const initialForm = { - * name: '', - * age: 0 - * } - * - * const [form, resetForm] = useResetReactive(initialForm) - * - * // 修改表单 - * form.name = 'John' - * form.age = 25 - * - * // 重置表单到初始状态 - * resetForm() - */ -export function useResetReactive(initialValue: T) { - /** - * 获取初始值的深拷贝 - * @returns 初始值的深拷贝 - */ - const getInitialValue = () => cloneDeep(initialValue) - - // 创建响应式状态 - const state = reactive(getInitialValue()) - - /** - * 重置状态到初始值 - * @description 先清空所有属性,然后用初始值重新赋值,确保引用不变 - */ - const reset = () => { - // 清空当前状态的所有属性 - Object.keys(state).forEach((key) => delete (state as any)[key]) - // 使用初始值重新赋值 - Object.assign(state, getInitialValue()) - } - - return [state, reset] as const -} diff --git a/frontend-pc/src/hooks/useRouteListener.ts b/frontend-pc/src/hooks/useRouteListener.ts deleted file mode 100644 index 55c5c97..0000000 --- a/frontend-pc/src/hooks/useRouteListener.ts +++ /dev/null @@ -1,92 +0,0 @@ -/** - * 路由变化监听 Hook - * @description 使用发布订阅模式管理路由变化,避免重复监听导致的性能问题 - */ - -import type { Handler } from 'mitt' -import type { RouteLocationNormalized } from 'vue-router' -import mitt from 'mitt' -import { onUnmounted, ref } from 'vue' - -// 事件类型定义 -type RouteChangeEvent = { - to: RouteLocationNormalized - from?: RouteLocationNormalized -} - -// 事件键定义 -const ROUTE_CHANGE_KEY = Symbol('ROUTE_CHANGE') - -// 事件发射器 -const emitter = mitt<{ - [ROUTE_CHANGE_KEY]: RouteChangeEvent -}>() - -// 最新路由状态 -const latestRoute = ref(null) - -/** - * 设置路由变化事件 - * @param to 目标路由 - * @param from 来源路由 - */ -export function setRouteEmitter(to: RouteLocationNormalized, from?: RouteLocationNormalized) { - const event: RouteChangeEvent = { to, from } - emitter.emit(ROUTE_CHANGE_KEY, event) - latestRoute.value = to -} - -/** - * 路由监听 Hook - * @returns 路由监听相关方法 - */ -export function useRouteListener() { - // 监听器列表 - const listeners = new Set>() - - /** - * 监听路由变化 - * @param handler 处理函数 - * @param immediate 是否立即执行 - */ - function listenerRouteChange( - handler: (event: RouteChangeEvent) => void, - immediate = true - ) { - emitter.on(ROUTE_CHANGE_KEY, handler) - listeners.add(handler) - - if (immediate && latestRoute.value) { - handler({ to: latestRoute.value }) - } - - return handler - } - - /** - * 移除路由监听器 - * @param handler 要移除的处理函数 - */ - function removeRouteListener(handler?: Handler) { - if (handler) { - emitter.off(ROUTE_CHANGE_KEY, handler) - listeners.delete(handler) - } else { - // 移除所有监听器 - listeners.forEach((listener) => { - emitter.off(ROUTE_CHANGE_KEY, listener) - }) - listeners.clear() - } - } - - // 组件卸载时清理 - onUnmounted(() => { - removeRouteListener() - }) - - return { - listenerRouteChange, - removeRouteListener - } -} diff --git a/frontend-pc/src/hooks/useTable.ts b/frontend-pc/src/hooks/useTable.ts deleted file mode 100644 index d489743..0000000 --- a/frontend-pc/src/hooks/useTable.ts +++ /dev/null @@ -1,277 +0,0 @@ -import type { TableData, TableInstance } from '@arco-design/web-vue' -import { Message, Modal } from '@arco-design/web-vue' -import { useBreakpoint, usePagination } from '@/hooks' - -interface Options { - formatResult?: (data: T[]) => U[] - onSuccess?: () => void - immediate?: boolean - rowKey?: keyof T - /** 是否开启跨页多选,开启后切换分页会保留已选中的 key */ - crossPageSelect?: boolean - listAPI: (params: { page: number, size: number }) => Promise>> | Promise> - deleteAPI?: (ids: string[]) => Promise> -} - -/** 删除操作的配置选项 */ -interface DeleteOptions { - /** 确认框标题 */ - title?: string - /** 确认框内容 */ - content?: string - /** 成功提示信息 */ - successTip?: string - /** 是否显示确认框 */ - showModal?: boolean -} - -/** - * 表格数据管理 Hook - * @description 提供表格数据管理的响应式 Hook,支持分页、搜索、刷新、删除等功能 - * @template T - 原始数据类型 - * @template U - 格式化后的数据类型 - * @param options - 表格配置选项 - * @returns 表格相关的状态和方法 - * @example - * const { - * loading, - * tableData, - * pagination, - * selectedKeys, - * search, - * refresh, - * handleDelete - * } = useTable({ - * listAPI: (p) => getTableApi(p), - * formatResult: (data) => data.map(formatData), - * immediate: true, - * rowKey: 'id' - * }) - */ -export function useTable(options: Options) { - const { formatResult, onSuccess, immediate = true, rowKey = 'id', crossPageSelect = false, listAPI, deleteAPI } - = options || {} - - // 分页相关 - const { pagination, setTotal } = usePagination(() => getTableData()) - const loading = ref(false) - const tableData: Ref = ref([]) - - /** - * 获取表格数据 - * @description 调用 API 获取数据,处理分页、格式化等逻辑 - */ - async function getTableData() { - try { - loading.value = true - const res = await listAPI({ page: pagination.current, size: pagination.pageSize }) - // 处理返回的数据结构可能是分页或数组的情况 - const data = !Array.isArray(res.data) ? res.data.records : res.data - tableData.value = formatResult ? formatResult(data) : data - // 设置总数据量 - const total = !Array.isArray(res.data) ? res.data.total : data.length - setTotal(total) - onSuccess?.() - } finally { - loading.value = false - } - } - - // 首次加载数据 - immediate && getTableData() - - // 多选 - const selectedKeys = ref<(string | number)[]>([]) - - /** 当前页所有行的 key(用于跨页多选时合并) */ - function getCurrentPageKeys() { - return (tableData.value as TableData[]).map((i) => i[rowKey as string]) - } - - /** - * 行选择回调 - * @param rowKeys - 表格传入的选中行 key(可能仅为当前页,也可能含跨页;以当前页 key 为准做合并避免重复) - */ - const select: TableInstance['onSelect'] = (rowKeys) => { - if (crossPageSelect) { - const pageKeys = getCurrentPageKeys() - const rest = selectedKeys.value.filter((k) => !pageKeys.includes(k)) - // 只合并当前页的选中:避免表格传入的 rowKeys 含其它页时造成重复 - const currentPageSelected = rowKeys.filter((k) => pageKeys.includes(k)) - selectedKeys.value = [...new Set([...rest, ...currentPageSelected])] - } else { - selectedKeys.value = rowKeys - } - } - - /** - * 全选回调 - * @param checked - 是否全选 - */ - const selectAll: TableInstance['onSelectAll'] = (checked) => { - const arr = (tableData.value as TableData[]).filter((i) => !(i?.disabled ?? false)) - const pageKeys = arr.map((i) => i[rowKey as string]) - if (crossPageSelect) { - if (checked) { - const rest = selectedKeys.value.filter((k) => !pageKeys.includes(k)) - selectedKeys.value = [...rest, ...pageKeys] - } else { - selectedKeys.value = selectedKeys.value.filter((k) => !pageKeys.includes(k)) - } - } else { - selectedKeys.value = checked ? pageKeys : [] - } - } - - /** 获取选中的数据 */ - const getSelectedData = () => { - return tableData.value.filter((i) => selectedKeys.value.includes(i[rowKey as string])) - } - - /** - * 搜索 - * @description 重置页码为1并重新获取数据 - */ - const search = () => { - selectedKeys.value = [] - pagination.onChange(1) - } - - /** - * 刷新表格数据 - * @description 使用当前的分页参数重新获取数据 - */ - const refresh = () => { - getTableData() - } - - /** - * 处理删除操作 - * @description 支持删除确认框,自动处理删除后的刷新 - * @param deleteApi - 删除操作的 API 函数 - * @param options - 删除操作的配置选项 - * @returns Promise 删除操作的结果 - */ - const handleDelete = async ( - deleteApi: () => Promise>, - options?: DeleteOptions - ): Promise => { - const onDelete = async (isMultiple: boolean) => { - try { - const res = await deleteApi() - if (res.success) { - Message.success(options?.successTip || '删除成功!') - // 处理当前页码 - const deleteNum = isMultiple ? selectedKeys.value.length : 1 - const totalPage = Math.ceil((pagination.total - deleteNum) / pagination.pageSize) - if (pagination.current > totalPage) { - pagination.current = totalPage > 0 ? totalPage : 1 - } - isMultiple && (selectedKeys.value = []) - getTableData() - } - return res.success - } catch (error) { - // eslint-disable-next-line no-console - console.log('删除失败', error) - return false - } - } - - if (!(options?.showModal ?? true)) { - return onDelete(false) - } - - Modal.warning({ - title: options?.title || '提示', - content: options?.content || '是否确认删除?', - hideCancel: false, - maskClosable: false, - onBeforeOk: () => onDelete(true) - }) - } - - /** - * 删除单条数据 - * @description 需在 options 中配置 deleteAPI - * @param row - 要删除的行数据 - */ - const onDelete = (row: U) => { - if (!deleteAPI) { - Message.error('deleteAPI 没有配置') - return - } - const ids = String(row[rowKey as keyof U]) - return handleDelete(() => deleteAPI([ids]), { showModal: false }) - } - - /** - * 批量删除数据 - * @description 需在 options 中配置 deleteAPI - */ - const onBatchDelete = () => { - if (!deleteAPI) { - Message.error('deleteAPI 没有配置') - return - } - if (!selectedKeys.value.length) { - Message.warning('请选择要删除的数据') - return - } - handleDelete(() => deleteAPI(selectedKeys.value as string[]), { - title: '批量删除', - content: `确定要删除选中的 ${selectedKeys.value.length} 条数据吗?` - }) - } - - // 自定义扩展... - // 导入 - const onImport = () => { - Message.warning('点击了导入') - } - // 导出 - const onExport = () => { - Message.success('点击了导出') - } - // 下载模板 - // const onDownloadTemplate = () => {} - - // 响应式处理表格操作列固定状态 - const { breakpoint } = useBreakpoint() - const fixed = computed(() => !['xs', 'sm'].includes(breakpoint.value) ? 'right' : undefined) - - return { - /** 表格加载状态 */ - loading, - /** 表格数据 */ - tableData, - /** 获取表格数据 */ - getTableData, - /** 搜索,页码会重置为1 */ - search, - /** 分页的传参 */ - pagination, - /** 选择的行keys */ - selectedKeys, - /** 选择行 */ - select, - /** 全选行 */ - selectAll, - /** 获取选择的数据 */ - getSelectedData, - /** 处理删除、批量删除 */ - handleDelete, - /** 删除单条数据,需配置 deleteAPI */ - onDelete, - /** 批量删除数据,需配置 deleteAPI */ - onBatchDelete, - /** 刷新表格数据,页码会缓存 */ - refresh, - /** 操作列在小屏场景下不固定在右侧 */ - fixed, - /** 导入 */ - onImport, - /** 导出 */ - onExport - } -} diff --git a/frontend-pc/src/hooks/useTheme.ts b/frontend-pc/src/hooks/useTheme.ts deleted file mode 100644 index e198327..0000000 --- a/frontend-pc/src/hooks/useTheme.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { generate, getRgbStr } from '@arco-design/color' -import { useDark, useToggle } from '@vueuse/core' -import { computed } from 'vue' -import { useAppStore } from '@/stores' - -/** - * 主题管理 Hook - * @description 使用 vueuse 的 useDark 来管理主题状态 - */ -export function useTheme() { - const appStore = useAppStore() - - /** 是否为暗色主题 */ - const isDark = useDark({ - // 主题切换目标元素 - selector: 'body', - // 主题属性名 - attribute: 'arco-theme', - // 暗色主题值 - valueDark: 'dark', - // 亮色主题值 - valueLight: 'light', - // 本地存储键名 - storageKey: 'arco-theme', - // 初始值 - initialValue: 'light' - }) - - /** 当前主题 */ - const theme = computed(() => isDark.value ? 'dark' : 'light') - - /** 主题切换函数定义 */ - const onToggle = useToggle(isDark) - - /** 主题切换函数 */ - function toggleTheme() { - onToggle() - setThemeColor(appStore.themeColor) - } - - /** - * 设置主题色 - * 生成主题色的色阶并应用到 CSS 变量 - * @param color - 主题色值 - */ - function setThemeColor(color: string) { - if (!color) return - appStore.themeColor = color - const themeColors = generate(color, { - list: true, - dark: isDark.value - }) as string[] - themeColors.forEach((color, index) => { - document.body.style.setProperty(`--primary-${index + 1}`, getRgbStr(color)) - }) - } - - /** - * 初始化主题设置 - * 在应用启动时设置初始主题色 - */ - function initThemeColor() { - if (appStore.themeColor) { - setThemeColor(appStore.themeColor) - } - } - - return { - isDark, - theme, - toggleTheme, - setThemeColor, - initThemeColor - } -} diff --git a/frontend-pc/src/icons/AppIcon.vue b/frontend-pc/src/icons/AppIcon.vue new file mode 100644 index 0000000..338e7b0 --- /dev/null +++ b/frontend-pc/src/icons/AppIcon.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/frontend-pc/src/icons/custom-icons.json b/frontend-pc/src/icons/custom-icons.json deleted file mode 100644 index 569b97d..0000000 --- a/frontend-pc/src/icons/custom-icons.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "prefix": "custom", - "icons": { - "borders": { - "body": "", - "width": 48, - "height": 48 - }, - "table-size": { - "body": "", - "width": 48, - "height": 48 - }, - "tree-add": { - "body": "", - "width": 48, - "height": 48 - }, - "tree-reduce": { - "body": "", - "width": 48, - "height": 48 - }, - "alipay": { - "body": "", - "width": 1024, - "height": 1024 - }, - "avatar-man": { - "body": "\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "avatar-woman": { - "body": "\r\n \r\n \r\n \r\n \r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "backtop": { - "body": "", - "width": 1024, - "height": 1024 - }, - "file-all": { - "body": "", - "width": 1024, - "height": 1024 - }, - "file-close": { - "body": "", - "width": 1024, - "height": 1024 - }, - "file-css": { - "body": "\r\n \r\n \r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-dir": { - "body": "", - "width": 1024, - "height": 1024 - }, - "file-excel": { - "body": "\r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-exe": { - "body": "\r\n \r\n \r\n \r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-html": { - "body": "\r\n \r\n \r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-image": { - "body": "\r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-js": { - "body": "\r\n \r\n \r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-json": { - "body": "\r\n \r\n \r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-music": { - "body": "\r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-open": { - "body": "", - "width": 1024, - "height": 1024 - }, - "file-other": { - "body": "\r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-pdf": { - "body": "\r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-ppt": { - "body": "\r\n \r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-rar": { - "body": "\r\n \r\n \r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-txt": { - "body": "\r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-video": { - "body": "\r\n \r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-wps": { - "body": "\r\n \r\n \r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "file-zip": { - "body": "", - "width": 1024, - "height": 1024 - }, - "file": { - "body": "\r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "icon-msg": { - "body": "", - "width": 20, - "height": 20 - }, - "icon-notice": { - "body": "", - "width": 20, - "height": 20 - }, - "icon-num": { - "body": "", - "width": 20, - "height": 20 - }, - "icon-user": { - "body": "", - "width": 1024, - "height": 1024 - }, - "icon-wait": { - "body": "", - "width": 20, - "height": 20 - }, - "upload-file": { - "body": "\r\n ", - "width": 1228, - "height": 1024 - }, - "upload-folder": { - "body": "\r\n \r\n ", - "width": 1024, - "height": 1024 - }, - "vue": { - "body": "\r\n ", - "width": 128, - "height": 128 - }, - "wechat": { - "body": "\r\n ", - "width": 1024, - "height": 1024 - } - }, - "width": 48, - "height": 48 -} diff --git a/frontend-pc/src/icons/icon-data.ts b/frontend-pc/src/icons/icon-data.ts new file mode 100644 index 0000000..670d559 --- /dev/null +++ b/frontend-pc/src/icons/icon-data.ts @@ -0,0 +1,200 @@ +/** + * 菜单与通用图标的数据(由 scripts/extract-pc-icons.py 生成,**不要手改**)。 + * + * 来源:@iconify-json/icon-park-outline(?), + * 只抽了本项目实际用到的 37 个,避免把 4MB 的整包打进来。 + */ + +export interface IconData { + body: string + width: number + height: number +} + +export const ICONS: Record = { + 'dashboard': { + body: '', + width: 49, + height: 48 + }, + 'collect': { + body: '', + width: 48, + height: 48 + }, + 'setting': { + body: '', + width: 48, + height: 48 + }, + 'org-group': { + body: '', + width: 48, + height: 48 + }, + 'platform': { + body: '', + width: 48, + height: 48 + }, + 'task': { + body: '', + width: 48, + height: 48 + }, + 'audit': { + body: '', + width: 48, + height: 48 + }, + 'export': { + body: '', + width: 48, + height: 48 + }, + 'notify': { + body: '', + width: 48, + height: 48 + }, + 'dict': { + body: '', + width: 48, + height: 48 + }, + 'org': { + body: '', + width: 48, + height: 48 + }, + 'user': { + body: '', + width: 48, + height: 48 + }, + 'role': { + body: '', + width: 48, + height: 48 + }, + 'tenant': { + body: '', + width: 48, + height: 48 + }, + 'log': { + body: '', + width: 48, + height: 48 + }, + 'fold': { + body: '', + width: 48, + height: 48 + }, + 'unfold': { + body: '', + width: 48, + height: 48 + }, + 'account': { + body: '', + width: 48, + height: 48 + }, + 'logout': { + body: '', + width: 48, + height: 48 + }, + 'refresh': { + body: '', + width: 48, + height: 48 + }, + 'plus': { + body: '', + width: 48, + height: 48 + }, + 'search': { + body: '', + width: 48, + height: 48 + }, + 'close': { + body: '', + width: 48, + height: 48 + }, + 'check': { + body: '', + width: 48, + height: 48 + }, + 'warning': { + body: '', + width: 48, + height: 48 + }, + 'info': { + body: '', + width: 48, + height: 48 + }, + 'download': { + body: '', + width: 48, + height: 48 + }, + 'upload': { + body: '', + width: 48, + height: 48 + }, + 'edit': { + body: '', + width: 48, + height: 48 + }, + 'delete': { + body: '', + width: 48, + height: 48 + }, + 'more': { + body: '', + width: 48, + height: 48 + }, + 'arrow-down': { + body: '', + width: 48, + height: 48 + }, + 'file': { + body: '', + width: 48, + height: 48 + }, + 'photo': { + body: '', + width: 48, + height: 48 + }, + 'idcard': { + body: '', + width: 48, + height: 48 + }, + 'school-roll': { + body: '', + width: 48, + height: 48 + }, + 'work-proof': { + body: '', + width: 48, + height: 48 + } +} diff --git a/frontend-pc/src/icons/index.ts b/frontend-pc/src/icons/index.ts deleted file mode 100644 index 94b04a9..0000000 --- a/frontend-pc/src/icons/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import IconParkOutlineIcons from '@iconify-json/icon-park-outline/icons.json' -import { addCollection } from '@iconify/vue' -import CustomIcons from '@/icons/custom-icons.json' - -addCollection(IconParkOutlineIcons) -addCollection(CustomIcons) diff --git a/frontend-pc/src/layout/AppHeader.vue b/frontend-pc/src/layout/AppHeader.vue new file mode 100644 index 0000000..dba4f9d --- /dev/null +++ b/frontend-pc/src/layout/AppHeader.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/frontend-pc/src/layout/AppSider.vue b/frontend-pc/src/layout/AppSider.vue new file mode 100644 index 0000000..2fe6972 --- /dev/null +++ b/frontend-pc/src/layout/AppSider.vue @@ -0,0 +1,115 @@ + + + + + diff --git a/frontend-pc/src/layout/LayoutColumns.vue b/frontend-pc/src/layout/LayoutColumns.vue deleted file mode 100644 index fbec7c7..0000000 --- a/frontend-pc/src/layout/LayoutColumns.vue +++ /dev/null @@ -1,77 +0,0 @@ - - - - - diff --git a/frontend-pc/src/layout/LayoutDefault.vue b/frontend-pc/src/layout/LayoutDefault.vue deleted file mode 100644 index dc1c439..0000000 --- a/frontend-pc/src/layout/LayoutDefault.vue +++ /dev/null @@ -1,33 +0,0 @@ - - - - - diff --git a/frontend-pc/src/layout/LayoutMix.vue b/frontend-pc/src/layout/LayoutMix.vue deleted file mode 100644 index 725bccd..0000000 --- a/frontend-pc/src/layout/LayoutMix.vue +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/layout/LayoutTop.vue b/frontend-pc/src/layout/LayoutTop.vue deleted file mode 100644 index 9fcd390..0000000 --- a/frontend-pc/src/layout/LayoutTop.vue +++ /dev/null @@ -1,57 +0,0 @@ - - - - - diff --git a/frontend-pc/src/layout/components/Asider/index.vue b/frontend-pc/src/layout/components/Asider/index.vue deleted file mode 100644 index eaa748e..0000000 --- a/frontend-pc/src/layout/components/Asider/index.vue +++ /dev/null @@ -1,79 +0,0 @@ - - - - - diff --git a/frontend-pc/src/layout/components/Breadcrumb/index.vue b/frontend-pc/src/layout/components/Breadcrumb/index.vue deleted file mode 100644 index 9b203b7..0000000 --- a/frontend-pc/src/layout/components/Breadcrumb/index.vue +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/layout/components/Header/index.vue b/frontend-pc/src/layout/components/Header/index.vue deleted file mode 100644 index b7d31d7..0000000 --- a/frontend-pc/src/layout/components/Header/index.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - - - diff --git a/frontend-pc/src/layout/components/HeaderRightBar/ChangePasswordModal.vue b/frontend-pc/src/layout/components/HeaderRightBar/ChangePasswordModal.vue deleted file mode 100644 index 2ed1a9d..0000000 --- a/frontend-pc/src/layout/components/HeaderRightBar/ChangePasswordModal.vue +++ /dev/null @@ -1,120 +0,0 @@ - - - - - diff --git a/frontend-pc/src/layout/components/HeaderRightBar/SettingDrawerPanel.vue b/frontend-pc/src/layout/components/HeaderRightBar/SettingDrawerPanel.vue deleted file mode 100644 index d7f8d0c..0000000 --- a/frontend-pc/src/layout/components/HeaderRightBar/SettingDrawerPanel.vue +++ /dev/null @@ -1,168 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/layout/components/HeaderRightBar/components/LayoutModeItem.vue b/frontend-pc/src/layout/components/HeaderRightBar/components/LayoutModeItem.vue deleted file mode 100644 index 306dd52..0000000 --- a/frontend-pc/src/layout/components/HeaderRightBar/components/LayoutModeItem.vue +++ /dev/null @@ -1,270 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/layout/components/HeaderRightBar/index.vue b/frontend-pc/src/layout/components/HeaderRightBar/index.vue deleted file mode 100644 index cb2aa41..0000000 --- a/frontend-pc/src/layout/components/HeaderRightBar/index.vue +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/layout/components/Logo.vue b/frontend-pc/src/layout/components/Logo.vue deleted file mode 100644 index d6afe62..0000000 --- a/frontend-pc/src/layout/components/Logo.vue +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/layout/components/Main.vue b/frontend-pc/src/layout/components/Main.vue deleted file mode 100644 index ebcd78f..0000000 --- a/frontend-pc/src/layout/components/Main.vue +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/layout/components/Menu/MenuIcon.vue b/frontend-pc/src/layout/components/Menu/MenuIcon.vue deleted file mode 100644 index f243a09..0000000 --- a/frontend-pc/src/layout/components/Menu/MenuIcon.vue +++ /dev/null @@ -1,21 +0,0 @@ - - - - - diff --git a/frontend-pc/src/layout/components/Menu/MenuItem.vue b/frontend-pc/src/layout/components/Menu/MenuItem.vue deleted file mode 100644 index 7eb491d..0000000 --- a/frontend-pc/src/layout/components/Menu/MenuItem.vue +++ /dev/null @@ -1,44 +0,0 @@ - - - - - diff --git a/frontend-pc/src/layout/components/MenuFoldBtn.vue b/frontend-pc/src/layout/components/MenuFoldBtn.vue deleted file mode 100644 index ca8faad..0000000 --- a/frontend-pc/src/layout/components/MenuFoldBtn.vue +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/layout/components/OneLevelMenu/index.vue b/frontend-pc/src/layout/components/OneLevelMenu/index.vue deleted file mode 100644 index f6fe9b3..0000000 --- a/frontend-pc/src/layout/components/OneLevelMenu/index.vue +++ /dev/null @@ -1,108 +0,0 @@ - - - - - diff --git a/frontend-pc/src/layout/components/Tabs/MagicIcon.vue b/frontend-pc/src/layout/components/Tabs/MagicIcon.vue deleted file mode 100644 index 65c2361..0000000 --- a/frontend-pc/src/layout/components/Tabs/MagicIcon.vue +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/layout/components/Tabs/ReloadIcon.vue b/frontend-pc/src/layout/components/Tabs/ReloadIcon.vue deleted file mode 100644 index 23b237f..0000000 --- a/frontend-pc/src/layout/components/Tabs/ReloadIcon.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/layout/components/Tabs/index.vue b/frontend-pc/src/layout/components/Tabs/index.vue deleted file mode 100644 index 88e8194..0000000 --- a/frontend-pc/src/layout/components/Tabs/index.vue +++ /dev/null @@ -1,238 +0,0 @@ - - - - - - diff --git a/frontend-pc/src/layout/hooks/useLevelMenu.ts b/frontend-pc/src/layout/hooks/useLevelMenu.ts deleted file mode 100644 index b604a66..0000000 --- a/frontend-pc/src/layout/hooks/useLevelMenu.ts +++ /dev/null @@ -1,123 +0,0 @@ -import type { CSSProperties } from 'vue' -import type { RouteLocationNormalizedGeneric, RouteRecordRaw } from 'vue-router' -import { eachTree, searchTree } from 'xe-utils' -import { useRouteListener } from '@/hooks' -import { useAppStore, useRouteStore } from '@/stores' -import { filterTree } from '@/utils' -import { isExternal } from '@/utils/validate' - -type Options = { - oneActivePathChange?: (path: string) => void -} - -/** 获取一级菜单,二级菜单的hooks */ -export function useLevelMenu(options?: Options) { - const route = useRoute() - const router = useRouter() - const appStore = useAppStore() - const routeStore = useRouteStore() - const { listenerRouteChange } = useRouteListener() - - const menuTheme = computed(() => appStore.isMenuDark ? 'dark' : 'light') - - // 获取菜单样式 - const getMenuStyle = computed(() => { - return { backgroundColor: menuTheme.value === 'dark' ? 'var(--color-menu-dark-bg)' : 'var(--color-menu-light-bg)' } as CSSProperties - }) - - // 克隆一份路由,避免直接操作原始路由 - const cloneRoutes = JSON.parse(JSON.stringify(routeStore.routes)) as RouteRecordRaw[] - const showMenuList = filterTree(cloneRoutes, (i) => i.meta?.hidden === false) as RouteRecordRaw[] - - // 一级菜单 - const oneLevelMenus = ref([]) - - // 二级菜单 - const twoLevelMenus = computed(() => { - const path = route.matched[0].path - return showMenuList.find((i) => i.path === path)?.children || [] - }) - - // 一级菜单选中的路由 - const oneActiveRoute = ref(null) - const oneActivePath = ref('') - const getOneActiveRoute = (to: RouteLocationNormalizedGeneric) => { - let path = to.path - if (to?.meta?.activeMenu) { - path = to.meta.activeMenu - } - const arr = searchTree(showMenuList, (i) => i.path === path) - oneActivePath.value = arr?.[0]?.path - return arr?.[0] - } - - // 二级菜单选中的路由 - const twoActivePath = ref('') - const getTwoActivePath = (to: RouteLocationNormalizedGeneric) => { - let path = to.path - if (to?.meta?.activeMenu) { - path = to.meta.activeMenu - } - return path - } - - listenerRouteChange(({ to }) => { - oneActiveRoute.value = getOneActiveRoute(to) - twoActivePath.value = getTwoActivePath(to) - }) - - // 获取一级菜单 - function getOneLevelMenus() { - const cloneRoutes = JSON.parse(JSON.stringify(routeStore.routes)) as RouteRecordRaw[] - const showMenuList = filterTree(cloneRoutes, (i) => i.meta?.hidden === false) as RouteRecordRaw[] - eachTree(showMenuList, (i) => { - if (i?.children?.length === 1 && i?.meta?.alwaysShow !== true) { - if (i.meta) { - i.meta.title = i.meta?.title || i.children?.[0]?.meta?.title - i.meta.icon = i.meta?.icon || i.children?.[0]?.meta?.icon - } - delete i.children - } - }) - oneLevelMenus.value = showMenuList - } - - // 一级菜单点击事件 - function handleOneMenuItemClick(path: string) { - if (isExternal(path)) { - window.open(path) - return - } - options?.oneActivePathChange?.(path) - oneActivePath.value = path - const obj = oneLevelMenus.value.find((i) => i.path === path) - if (obj?.redirect === 'noRedirect' || !obj?.redirect) { - router.push({ path: obj?.children?.[0]?.path }) - return - } - router.push({ path: (obj?.redirect as string) || path }) - } - - // 二级菜单点击事件 - function handleTwoMenuItemClick(path: string) { - if (isExternal(path)) { - window.open(path) - return - } - twoActivePath.value = path - router.push({ path }) - } - - return { - menuTheme, - getMenuStyle, - oneLevelMenus, - twoLevelMenus, - oneActiveRoute, - oneActivePath, - twoActivePath, - getOneLevelMenus, - handleOneMenuItemClick, - handleTwoMenuItemClick - } -} diff --git a/frontend-pc/src/layout/hooks/useMenu.ts b/frontend-pc/src/layout/hooks/useMenu.ts deleted file mode 100644 index 1d9bdc1..0000000 --- a/frontend-pc/src/layout/hooks/useMenu.ts +++ /dev/null @@ -1,101 +0,0 @@ -import type { CSSProperties } from 'vue' -import type { RouteRecordRaw } from 'vue-router' -import { computed } from 'vue' -import { eachTree } from 'xe-utils' -import { useDevice, useRouteListener } from '@/hooks' -import { useAppStore, useRouteStore } from '@/stores' -import { filterTree } from '@/utils' -import { isExternal } from '@/utils/validate' - -/** - * 菜单管理 Hooks - * 用于获取和处理应用程序的菜单列表 - * 提供菜单的过滤、展平处理等功能 - * @returns {object} 包含处理后菜单列表的响应式对象 - */ -export function useMenu() { - const router = useRouter() - // 路由存储实例,用于获取原始路由配置 - const routeStore = useRouteStore() - const appStore = useAppStore() - const { listenerRouteChange } = useRouteListener() - const { isDesktop } = useDevice() - - // 是否折叠菜单 - const collapsed = computed(() => - !isDesktop.value ? false : appStore.isMenuCollapsed - ) - - // 菜单触发器配置 - const menuTriggerProps = { - animationName: 'slide-dynamic-origin' - } - - // 菜单主题 - const menuTheme = computed(() => appStore.isMenuDark ? 'dark' : 'light') - - // 获取菜单样式 - const getMenuStyle = computed(() => { - return { backgroundColor: menuTheme.value === 'dark' ? 'var(--color-menu-dark-bg)' : 'var(--color-menu-light-bg)' } as CSSProperties - }) - - /** - * 处理后的菜单列表 - * 响应式计算属性,当路由配置变化时自动更新, - * > hidden:false那么代表这个路由项显示在左侧菜单栏中 - * > 子项chidren只有一个hidden:false的子元素时, 且alwaysShow为false, 那么这个子项会被展平到父项中 - * 包含以下处理逻辑: - * 1. 深拷贝原始路由配置避免直接修改 - * 2. 过滤掉设置为隐藏的菜单项 - * 3. 展平只有一个子项的菜单项(提升用户体验) - */ - const menuList = computed(() => { - // 深拷贝路由配置,防止修改原始数据 - const cloneRoutes = JSON.parse(JSON.stringify(routeStore.routes)) as RouteRecordRaw[] - - // 过滤出非隐藏的菜单(meta.hidden !== false) - const showMenuList = filterTree(cloneRoutes, (i) => i.meta?.hidden === false) as RouteRecordRaw[] - - // 遍历处理菜单树,展平只有一个子项的菜单项 - eachTree(showMenuList, (i) => { - if (i?.children?.length === 1 && i?.meta?.alwaysShow !== true) { - if (i.meta) { - i.meta.title = i.meta?.title || i.children?.[0]?.meta?.title - i.meta.icon = i.meta?.icon || i.children?.[0]?.meta?.icon - } - i.path = i.children?.[0]?.path - delete i.children - } - }) - return showMenuList - }) - - const selectedKeys = ref([]) - - function handleMenuItemClick(key: string) { - if (isExternal(key)) { - window.open(key) - return - } - selectedKeys.value = [key] - router.push({ path: key }) - } - - listenerRouteChange(({ to }) => { - if (to?.meta?.activeMenu) { - selectedKeys.value = [to.meta.activeMenu] - return - } - selectedKeys.value = [to.path] - }) - - return { - menuTheme, - getMenuStyle, - menuList, - menuTriggerProps, - selectedKeys, - collapsed, - handleMenuItemClick - } -} diff --git a/frontend-pc/src/layout/index.vue b/frontend-pc/src/layout/index.vue index 24525fb..5d362d6 100644 --- a/frontend-pc/src/layout/index.vue +++ b/frontend-pc/src/layout/index.vue @@ -1,45 +1,66 @@ - - - - + + + diff --git a/frontend-pc/src/main.ts b/frontend-pc/src/main.ts index a545a06..be44590 100644 --- a/frontend-pc/src/main.ts +++ b/frontend-pc/src/main.ts @@ -1,39 +1,27 @@ -import { Card, Drawer, Modal } from '@arco-design/web-vue' -// 额外引入 Arco Design Icon图标库 -import ArcoVueIcon from '@arco-design/web-vue/es/icon' +import { createPinia } from 'pinia' +import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' import { createApp } from 'vue' -// 状态管理 -import pinia from '@/stores' import App from './App.vue' -// 自定义指令 -import directives from './directives' import router from './router' -// ⚠️ Arco 组件样式必须显式引入: -// vite.config.ts 里 ArcoResolver 配了 sideEffect:false(样式不随组件自动引入), -// 原本靠 @arco-themes/vue-gi-demo 主题包带入;我们换成自己的主题后就没人带了。 -// 不引的后果是「组件能用但完全没样式」,页面直接散架,且构建不报错。 -import '@arco-design/web-vue/dist/arco.css' -// 导入本项目主题(内部会覆盖 Arco 默认变量) -import '@/styles/arco-ui/index.less' -// 使用动画库 -import 'animate.css/animate.min.css' -// 自定义过渡动画 -import '@/styles/css/transition.css' -// 导入全局scss主文件 -import '@/styles/index.scss' -// 注册离线图标集 -import '@/icons/index' - -// 对特定组件进行默认配置 -Card.props.bordered = false +import { useActingTenantStore } from './stores/acting-tenant' +import { clearToken } from './utils/auth' +import { setActingTenantHeader, setUnauthorizedHandler } from './utils/http' +import './styles/index.scss' const app = createApp(App) -Modal._context = app._context -Drawer._context = app._context - -app.use(router) +const pinia = createPinia() +pinia.use(piniaPluginPersistedstate) app.use(pinia) -app.use(ArcoVueIcon) -app.use(directives) +app.use(router) + +// ① 401 的处理交给路由(http 层不 import router,避免循环引用) +setUnauthorizedHandler(() => { + clearToken() + const back = encodeURIComponent(router.currentRoute.value.fullPath) + void router.replace({ path: '/login', query: { redirect: back } }) +}) + +// ② 把持久化的"当前学校"推给 http 层(刷新后第一屏请求就要带上它) +setActingTenantHeader(useActingTenantStore().tenantId) app.mount('#app') diff --git a/frontend-pc/src/router/guard/index.ts b/frontend-pc/src/router/guard/index.ts deleted file mode 100644 index 903d133..0000000 --- a/frontend-pc/src/router/guard/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** @file 路由守卫配置模块 - 处理路由导航守卫和权限控制 */ - -import type { Router } from 'vue-router' -import { setRouteEmitter } from '@/hooks' -import { setupPermissionGuard } from './setupPermissionGuard' - -/** - * 设置页面路由守卫 - * @description 处理路由变化时的页面级操作,如路由变化事件通知 - * @param router - Vue Router 实例 - */ -export const setupPageGuard = (router: Router) => { - router.beforeEach((to, from) => { - // 触发路由变化事件,通知所有监听器 - setRouteEmitter(to, from) - }) -} - -/** - * 创建路由守卫 - * @description 统一配置所有路由守卫,包括: - * 1. 页面路由守卫 - 处理路由变化事件 - * 2. 权限路由守卫 - 处理路由访问权限 - * @param router - Vue Router 实例 - */ -export default function createRouteGuard(router: Router) { - // 设置页面路由守卫 - setupPageGuard(router) - // 设置权限路由守卫 - setupPermissionGuard(router) -} diff --git a/frontend-pc/src/router/guard/setupPermissionGuard.ts b/frontend-pc/src/router/guard/setupPermissionGuard.ts deleted file mode 100644 index 3cafe56..0000000 --- a/frontend-pc/src/router/guard/setupPermissionGuard.ts +++ /dev/null @@ -1,89 +0,0 @@ -/** @file 路由权限守卫模块 - 处理路由访问权限和动态路由加载 */ - -import type { RouteLocationNormalized, Router } from 'vue-router' -import { Message } from '@arco-design/web-vue' -import { useUserStore } from '@/stores' -import { getToken } from '@/utils/auth' - -/** 免登录白名单路径 */ -const WHITE_LIST = ['/login', '/404'] - -/** 是否已生成动态路由 */ -let isDynamicRoutesGenerated = false - -/** - * 处理动态路由加载 - * @param router - 路由实例 - * @param to - 目标路由 - */ -async function handleDynamicRoutes( - router: Router, - to: RouteLocationNormalized -): Promise<{ path: string, query?: any, replace: boolean }> { - const userStore = useUserStore() - - try { - await userStore.generateRoutes() - // 标记动态路由已生成 - isDynamicRoutesGenerated = true - // 确保路由添加完成后返回目标路由 - return { ...to, replace: true } - } catch (error) { - // 发生错误时重置用户状态并跳转登录页 - userStore.resetToken() - const errorMessage = error instanceof Error ? error.message : '路由守卫过程发生错误' - Message.error(errorMessage) - return { path: `/login`, query: { redirect: to.path }, replace: true } - } -} - -/** - * 初始化路由守卫 - * @param router - 路由实例 - */ -export const setupPermissionGuard = (router: Router): void => { - router.beforeEach(async (to, _from, next) => { - const token = getToken() - const userStore = useUserStore() - - // 已登录状态 - if (token) { - if (to.path === '/login') { - // 已登录时访问登录页,直接放行(通常会被重定向到首页) - next() - return - } - - // 只在首次访问或路由未生成时处理动态路由 - if (!isDynamicRoutesGenerated) { - try { - const redirectRoute = await handleDynamicRoutes(router, to) - next(redirectRoute) - } catch { - // 如果出错,清空角色信息并重定向到登录页 - userStore.resetToken() - // 重置路由生成标志 - isDynamicRoutesGenerated = false - next(`/login?redirect=${to.path}`) - } - return - } - - // 正常访问 - next() - return - } - - // 未登录状态时重置路由生成标志 - isDynamicRoutesGenerated = false - - if (WHITE_LIST.includes(to.path)) { - // 白名单路径直接访问 - next() - return - } - - // 其他路径重定向到登录页 - next('/login') - }) -} diff --git a/frontend-pc/src/router/index.ts b/frontend-pc/src/router/index.ts index 4a864b9..630f360 100644 --- a/frontend-pc/src/router/index.ts +++ b/frontend-pc/src/router/index.ts @@ -1,99 +1,183 @@ -/** @file 路由配置模块 - 处理路由的创建、守卫和重置 */ - import type { RouteRecordRaw } from 'vue-router' -import { createRouter, createWebHashHistory } from 'vue-router' -import createRouteGuard from '@/router/guard' -import { useRouteStore } from '@/stores' +import type { XtMenu } from '@/config/xt-routes' +import { defineComponent, h } from 'vue' +import { createRouter, createWebHistory, useRoute } from 'vue-router' +import { DASHBOARD, XT_MENUS } from '@/config/xt-routes' +import Layout from '@/layout/index.vue' +import { useUserStore } from '@/stores/user' -/** 默认布局组件 */ -const Layout = () => import('@/layout/index.vue') +/** + * 路由。 + * + * 两条规则决定了这里的形状: + * 1. **"有哪些页面"由前端代码决定,"谁能看"由后端决定**(`/auth/me` 的 menus)。 + * 所以路由不是后端下发的树,而是本地菜单表按菜单键过滤后注册。 + * 2. 业务路径都是**绝对路径**(`/collect/task`),统一挂在 `/` 这个 Layout 路由下 —— + * 分组只影响左侧菜单的排版,不影响 URL。这样"点菜单 404"这类问题不会出现。 + */ + +/** 视图懒加载表:把 component 字符串映射成真正的组件 */ +const viewModules = import.meta.glob('../views/**/*.vue') + +/** + * 菜单指向了不存在的视图时用的兜底组件。 + * + * ⚠️ 这里**不能抛异常**:注册路由是"登录后一次性把所有可见菜单都注册一遍", + * 抛出去会让**整次登录后的导航全挂**(表现是"登录成功但页面白屏"), + * 而真正的原因只是某一个菜单的 `component` 拼错了 —— 一个页面拖垮整个应用, + * 排查方向还完全不在这上面。 + * + * 也不能静默显示空白页:那样看起来像"这个页面没数据"。 + * 所以显示一句明确的话,并在控制台打出正确路径的拼法。 + */ +const MissingView = defineComponent({ + name: 'MissingView', + setup() { + const route = useRoute() + return () => h('div', { + style: 'margin:24px;padding:20px;border:1px solid #e5e5e5;border-radius:6px;' + + 'background:#fff;color:#c0392b;line-height:1.8' + }, [ + h('div', { style: 'font-weight:600' }, '这个菜单指向的页面不存在(开发错误,不是权限问题)'), + h('div', { style: 'color:#666;margin-top:8px' }, `当前路径:${route.path}`), + h('div', { style: 'color:#666' }, '请检查 src/config/xt-routes.ts 里该菜单的 component 是否与 src/views 下的文件一一对应。') + ]) + } +}) + +function loadView(component: string) { + const path = `../views/${component}.vue` + const loader = viewModules[path] + if (!loader) { + console.error(`[router] 菜单指向的视图不存在:src/views/${component}.vue`) + return MissingView + } + return loader +} -/** 基础路由配置 */ const baseRoutes: RouteRecordRaw[] = [ { - path: '/redirect', + path: '/login', + name: 'Login', + component: () => import('@/views/login/index.vue'), + meta: { title: '登录', anonymous: true } + }, + { + path: '/', + name: 'Root', component: Layout, - meta: { hidden: true }, + redirect: DASHBOARD.path, children: [ { - path: '/redirect/:path(.*)', - component: () => import('@/views/redirect/index.vue') + path: DASHBOARD.path.slice(1), + name: DASHBOARD.key, + component: loadView(DASHBOARD.component), + meta: { title: DASHBOARD.title, icon: DASHBOARD.icon } } ] }, { - path: '/login', - component: () => import('@/views/login/index.vue'), - meta: { hidden: true } - } -] - -/** 错误页面路由配置 */ -const errorRoutes: RouteRecordRaw[] = [ - { - path: '/:pathMatch(.*)*', - component: () => import('@/views/error/404.vue'), - meta: { hidden: true } + path: '/403', + name: 'Forbidden', + component: () => import('@/views/error/403.vue'), + meta: { title: '无权限' } }, { - path: '/403', - component: () => import('@/views/error/403.vue'), - meta: { hidden: true } + // ⚠️ **兜底路由绝对不能标 `anonymous: true`**,这里踩过一次: + // "首次进入一个还没注册的动态路由(菜单页)"时,匹配到的**正是这条兜底路由**。 + // 如果它被当成匿名页放行,守卫就**不会**去 fetchMe + registerMenus —— + // 于是动态路由永远建不起来,表现是"直接打开/刷新任何菜单地址都是 404", + // 而在应用内点菜单却一切正常(那时路由已经注册好了)。 + // 只有"从外部链接进来 / 按 F5"才会暴露,很容易被判成"部署问题"。 + path: '/:pathMatch(.*)*', + name: 'NotFound', + component: () => import('@/views/error/404.vue'), + meta: { title: '页面不存在' } } ] -/** 主页路由配置 */ -const homeRoute: RouteRecordRaw = { - path: '/', - name: 'Home', - component: Layout, - redirect: '/dashboard', - meta: { hidden: false }, - children: [ - { - path: '/dashboard', - component: () => import('@/views/dashboard/index.vue'), - name: 'Dashboard', - meta: { - title: '工作台', - icon: 'icon-park-outline:workbench', - affix: true, - hidden: false, - breadcrumb: false - } - } - ] -} - -/** 静态路由配置 */ -export const constantRoutes: RouteRecordRaw[] = [...baseRoutes, ...errorRoutes, homeRoute] - -/** 创建路由实例 */ const router = createRouter({ - history: createWebHashHistory(import.meta.env.BASE_URL), - routes: constantRoutes, - scrollBehavior: () => ({ left: 0, top: 0 }) + history: createWebHistory(import.meta.env.VITE_BASE || '/'), + routes: baseRoutes, + scrollBehavior: () => ({ top: 0 }) }) -// 创建路由守卫 -createRouteGuard(router) +/** 已注册的动态路由(退出登录时要摘掉,否则换个人登录会看到上一个人的菜单) */ +let registered: string[] = [] -/** 重置路由配置 - 清除所有动态添加的路由 */ -export function resetRouter(): void { - try { - const routeStore = useRouteStore() - // 移除所有动态路由 - routeStore.asyncRoutes.forEach((route) => { - const { name } = route - if (name && router.hasRoute(name)) { - router.removeRoute(name) - } - }) - } catch (error) { - console.error('重置路由失败:', error) - // 降级处理:刷新页面以重置路由状态 - window.location.reload() +/** 把某个菜单注册成 Layout 的子路由 */ +function registerMenu(menu: XtMenu): void { + if (router.hasRoute(menu.key)) { + return + } + router.addRoute('Root', { + path: menu.path.slice(1), + name: menu.key, + component: loadView(menu.component), + meta: { title: menu.title, icon: menu.icon, group: menu.group } + }) + registered.push(menu.key) +} + +/** 按菜单键注册动态路由(登录后调用;重复调用是幂等的) */ +export function registerMenus(menuKeys: string[]): void { + const keys = new Set(menuKeys ?? []) + for (const menu of XT_MENUS) { + if (keys.has(menu.key)) { + registerMenu(menu) + } } } +/** 摘掉全部动态路由(退出登录时调用) */ +export function resetMenus(): void { + for (const name of registered) { + if (router.hasRoute(name)) { + router.removeRoute(name) + } + } + registered = [] +} + +// ── 守卫 ──────────────────────────────────────────── + +router.beforeEach(async (to) => { + const user = useUserStore() + + if (to.meta.anonymous) { + return true + } + + if (!user.token) { + return { path: '/login', query: { redirect: to.fullPath } } + } + + // 刷新页面后 pinia 里没有档案,先补回来(同时拿到菜单键) + if (!user.loaded) { + try { + await user.fetchMe() + registerMenus(user.menus) + // 动态路由刚注册完,当前这次导航还匹配在 404 兜底上 → 用**路径**重新走一遍。 + // + // ⚠️ 不能写 `return { ...to, replace: true }`:`to` 里带着 `name: 'NotFound'` + // (因为首次匹配时动态路由还不存在,命中的就是兜底路由), + // 而 Vue Router 在同时有 name 与 path 时**按 name 匹配** —— + // 于是"重试"又落到 404,表现为**刷新页面或从外部链接进来时菜单页全是 404**, + // 而在应用内点击菜单却正常(那时路由已经注册好了)。 + // 这个 bug 只有"直接打开一个菜单地址"才会暴露。 + return { path: to.fullPath, replace: true } + } catch { + await user.logout() + return { path: '/login', query: { redirect: to.fullPath } } + } + } + + return true +}) + +router.afterEach((to) => { + const title = (to.meta.title as string | undefined) ?? '' + document.title = title ? `${title} · 深学在线` : '深学在线' +}) + export default router diff --git a/frontend-pc/src/stores/acting-tenant.ts b/frontend-pc/src/stores/acting-tenant.ts new file mode 100644 index 0000000..86dce49 --- /dev/null +++ b/frontend-pc/src/stores/acting-tenant.ts @@ -0,0 +1,45 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' +import { setActingTenantHeader } from '@/utils/http' + +/** + * 平台运营的「当前学校」。 + * + * ## 为什么需要它 + * 平台运营(`tenantId = 0`)是唯一跨租户的身份,服务端的租户拦截器因此对它 + * **整个关掉了租户过滤**。于是「组织架构」「账号管理」这类**本来就是租户内**的页面, + * 在平台账号下拿到的是**所有租户混在一起**的数据 —— 而且看不出错: + * 只有一所学校有数据时,它和学校管理员看到的完全一样。 + * + * 所以平台要代管某所学校时必须**显式指明是哪一所**:带上 `X-Tenant-Id` + * (见 `@/utils/http` 的 `setActingTenantHeader`),服务端会把本次请求的租户上下文 + * 换成目标租户,之后过滤、归属、权限判定都按"我就是那所学校"走。 + * + * ## 三条约定 + * 1. **只有平台运营选它**(学校管理员的租户由 token 决定,服务端会忽略这个头)。 + * 2. **放在 store 而不是各页面 `ref`**:否则切页面要重选, + * 更糟的是两个页面各看一所学校,看起来像数据不一致。 + * 3. 退出登录时清掉(属于上一次身份的上下文)。 + */ +export const useActingTenantStore = defineStore('acting-tenant', () => { + const tenantId = ref(null) + const tenantName = ref('') + + function set(id: number | null, name = ''): void { + tenantId.value = id + tenantName.value = name + // 推给 http 层(方向固定:store → http,避免循环引用) + setActingTenantHeader(id) + } + + function clear(): void { + set(null, '') + } + + return { tenantId, tenantName, set, clear } +}, { + persist: { + key: 'xt-acting-tenant', + storage: localStorage + } +}) diff --git a/frontend-pc/src/stores/app.ts b/frontend-pc/src/stores/app.ts new file mode 100644 index 0000000..1c8ed4c --- /dev/null +++ b/frontend-pc/src/stores/app.ts @@ -0,0 +1,19 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +/** 界面状态(与业务无关的那些) */ +export const useAppStore = defineStore('app', () => { + /** 左侧菜单是否折叠 */ + const collapsed = ref(false) + + function toggleCollapse(): void { + collapsed.value = !collapsed.value + } + + return { collapsed, toggleCollapse } +}, { + persist: { + key: 'xt-app', + storage: localStorage + } +}) diff --git a/frontend-pc/src/stores/index.ts b/frontend-pc/src/stores/index.ts deleted file mode 100644 index e05e0e5..0000000 --- a/frontend-pc/src/stores/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { createPinia } from 'pinia' -import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' - -export * from './useActingTenantStore' -export * from './useAppStore' -export * from './useDictStore' -export * from './useMenuBadgeStore' -export * from './useRouteStore' -export * from './useTabsStore' -export * from './useUserStore' - -const pinia = createPinia() -pinia.use(piniaPluginPersistedstate) - -export default pinia diff --git a/frontend-pc/src/stores/useActingTenantStore.ts b/frontend-pc/src/stores/useActingTenantStore.ts deleted file mode 100644 index fed823e..0000000 --- a/frontend-pc/src/stores/useActingTenantStore.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { defineStore } from 'pinia' -import { ref } from 'vue' - -/** - * 平台运营的「当前学校」。 - * - * ## 为什么要有这个状态 - * - * 平台运营(`tenantId = 0`)是唯一跨租户的身份,服务端的租户过滤对它整体关闭。 - * 所以「组织架构」「账号管理」这类**本身就是租户内**的页面,平台必须先指明看哪所学校 —— - * 否则拿到的是所有租户混在一起的数据,而且看不出错。 - * - * 这个值是**跟着请求走的**(`X-Tenant-Id` 请求头,见 `@/apis/xt/acting-tenant`), - * 不是登录态的一部分:token 里的 tenantId 始终是 0。 - * - * ## 为什么放在 store 而不是各页面自己 `ref` - * - * 两个页面都要用同一个选择。各写一份的话,用户在「组织架构」选了学校、 - * 切到「账号管理」又得重选一次 —— 而更糟的是**看起来像是两所学校的数据不一致**。 - * - * ⚠️ `persist: false`:刷新页面就忘掉。这是刻意的 —— - * 一个跨越登录会话的"当前学校"会在下次登录时静默生效, - * 而那时候用户心里想的可能是另一所学校。 - */ -const storeSetup = () => { - /** 当前代管的租户 id;`undefined` = 未选择(此时不应请求租户内数据) */ - const tenantId = ref(undefined) - - const setTenant = (id?: number) => { - tenantId.value = id - } - - const clear = () => { - tenantId.value = undefined - } - - return { tenantId, setTenant, clear } -} - -export const useActingTenantStore = defineStore('actingTenant', storeSetup, { persist: false }) diff --git a/frontend-pc/src/stores/useAppStore.ts b/frontend-pc/src/stores/useAppStore.ts deleted file mode 100644 index 3fb6e8f..0000000 --- a/frontend-pc/src/stores/useAppStore.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { defineStore } from 'pinia' -import { computed, reactive, toRefs } from 'vue' -import defaultSettings from '@/config/setting.json' - -/** - * App Store 的核心设置逻辑 - */ -const storeSetup = () => { - // 初始化 App 配置 - const settingConfig = reactive({ ...defaultSettings }) as App.SettingConfig - - /** - * 计算页面切换动画类名 - * 根据配置决定是否启用动画以及使用哪种动画模式 - */ - const pageTransitionName = computed(() => - settingConfig.isTransitionEnabled ? settingConfig.transitionName : '' - ) - - /** - * 设置菜单折叠状态 - * @param collapsed - 是否折叠 - */ - const setMenuCollapse = (collapsed: boolean) => { - settingConfig.isMenuCollapsed = collapsed - } - - /** 恢复为 setting.json 中的默认配置 */ - const resetSettings = () => { - Object.assign(settingConfig, defaultSettings) - } - - return { - ...toRefs(settingConfig), - pageTransitionName, - setMenuCollapse, - resetSettings - } -} - -// 创建并导出 App Store,启用持久化存储 -export const useAppStore = defineStore('app', storeSetup, { persist: true }) diff --git a/frontend-pc/src/stores/useDictStore.ts b/frontend-pc/src/stores/useDictStore.ts deleted file mode 100644 index da72542..0000000 --- a/frontend-pc/src/stores/useDictStore.ts +++ /dev/null @@ -1,66 +0,0 @@ -import type { SelectOptionData } from '@arco-design/web-vue' -import { defineStore } from 'pinia' -import { ref } from 'vue' -import { getDictData as getDictDataApi } from '@/apis/system/dict' - -/** - * 字典数据类型定义 - */ -type DictDataMap = Record - -/** - * 字典 Store 的核心设置逻辑 - * 用于管理系统字典数据的状态 - */ -const storeSetup = () => { - /** - * 存储字典数据的响应式对象 - * key: 字典类型 - * value: 字典选项数组 - */ - const dictData = ref({}) - - /** - * 获取字典数据 - * 从后端 API 获取字典数据并更新到 store 中 - * @throws 当 API 请求失败时抛出错误 - */ - const getDictData = async () => { - try { - const res = await getDictDataApi() - if (res?.data) { - dictData.value = res.data - } - } catch (error) { - console.error('获取字典数据失败:', error) - throw error - } - } - - /** - * 获取指定类型的字典数据 - * @param dictType - 字典类型 - * @returns 对应类型的字典选项数组,如果不存在则返回空数组 - */ - const getDictOptions = (dictType: string): SelectOptionData[] => { - return dictData.value[dictType] || [] - } - - /** - * 重置字典数据 - * 清空所有已加载的字典数据 - */ - const resetDict = () => { - dictData.value = {} - } - - return { - dictData, - getDictData, - getDictOptions, - resetDict - } -} - -// 创建并导出字典 Store,不启用持久化存储 -export const useDictStore = defineStore('dict', storeSetup, { persist: false }) diff --git a/frontend-pc/src/stores/useMenuBadgeStore.ts b/frontend-pc/src/stores/useMenuBadgeStore.ts deleted file mode 100644 index dfddea5..0000000 --- a/frontend-pc/src/stores/useMenuBadgeStore.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { BadgeInstance } from '@arco-design/web-vue' -import { defineStore } from 'pinia' - -const storeSetup = () => { - // 菜单角标 - const badgeMap: Record = reactive({ - '/crud/index': 6, - '/form/base': 3, - '/form/step': 5, - '/table/dialog': 8 - }) - - // 菜单角标属性 - const BadgePropsMap: Record = { - '/crud/index': { - status: 'success' - }, - '/form/base': { - style: 'transform: scale(0.8);' - }, - '/form/step': { - style: { transform: 'scale(0.8)' }, - class: 'g-badge-primary' - }, - '/table/dialog': { - style: { transform: 'scale(0.8)' }, - class: 'g-badge-success' - } - } - - return { badgeMap, BadgePropsMap } -} - -export const useMenuBadgeStore = defineStore('menu-badge', storeSetup) diff --git a/frontend-pc/src/stores/useRouteStore.ts b/frontend-pc/src/stores/useRouteStore.ts deleted file mode 100644 index 1c19d82..0000000 --- a/frontend-pc/src/stores/useRouteStore.ts +++ /dev/null @@ -1,177 +0,0 @@ -/** - * @file 路由状态管理模块 - * @description 处理动态路由的加载、格式化和状态管理 - */ - -import type { RouteRecordRaw } from 'vue-router' -import type { UserRouteItem } from '@/apis/user' -import { defineStore } from 'pinia' -import { ref } from 'vue' -import { mapTree } from 'xe-utils' -import { constantRoutes } from '@/router' -import { transformPathToName } from '@/utils' - -/** - * 布局组件 - * 用于页面整体布局结构 - */ -const Layout = () => import('@/layout/index.vue') - -/** - * 视图模块映射 - * 自动导入 views 目录下的所有 .vue 文件 - * 用于动态加载路由组件 - */ -const modules = import.meta.glob('@/views/**/*.vue') - -/** - * 组件路径映射缓存 - * 将视图路径映射到对应的模块加载函数,避免每次遍历查找 - * key: 视图路径(如 'system/user/index') - * value: 模块加载函数 - */ -const viewPathMap = new Map Promise>() - -/** - * 初始化组件路径映射 - * @description 在模块加载时构建路径映射表,提升查找效率 - */ -function initViewPathMap() { - if (viewPathMap.size > 0) return // 已初始化则跳过 - - for (const path in modules) { - // 提取视图路径:从 'src/views/system/user/index.vue' 提取 'system/user/index' - const dir = path.split('views/')[1]?.split('.vue')[0] - if (dir) { - viewPathMap.set(dir, () => modules[path]()) - } - } -} - -// 初始化路径映射 -initViewPathMap() - -/** - * 加载视图组件 - * @description 根据路径加载对应的视图组件,使用 Map 缓存提升查找效率 - * @param {string} view - 组件路径(相对于 views 目录) - * @returns {Function|undefined} 返回组件的加载函数或 undefined - * @example - * loadView('system/user/index') - */ -export const loadView = (view: string) => { - return viewPathMap.get(view) -} - -/** - * 转换组件标识为实际组件 - * @description 将字符串类型的组件标识转换为实际的组件 - * @param {string} component - 组件标识 - * @returns {Function} 返回组件的加载函数 - * @example - * transformComponentView('Layout') - * transformComponentView('system/user/index') - */ -const transformComponentView = (component: string) => { - if (component === 'Layout') { - return Layout as never - } else { - return loadView(component) as never - } -} - -/** - * 格式化异步路由配置 - * @description 处理后端返回的菜单数据,转换为路由配置 - * @param {MenuItem[]} menus - 后端返回的菜单数据(已经过权限过滤) - * @returns {RouteRecordRaw[]} 返回格式化后的路由配置数组 - * - * @remarks - * 1. 对菜单数据进行排序(根据 sort 字段) - * 2. 将字符串类型的组件标识转换为实际组件 - * 3. 处理路由元数据(meta) - * - * @example - * const routes = formatAsyncRoutes([ - * { - * path: '/system', - * component: 'Layout', - * children: [...] - * } - * ]) - */ -const formatAsyncRoutes = (menus: UserRouteItem[]) => { - if (!menus.length) return [] - - // 对顶层菜单进行排序 - menus.sort((a, b) => (a?.sort ?? 0) - (b?.sort ?? 0)) - - // 使用 mapTree 递归处理菜单树 - const routes = mapTree(menus, (item) => { - // 对子菜单进行排序 - if (item.children && item.children.length) { - item.children.sort((a, b) => (a?.sort ?? 0) - (b?.sort ?? 0)) - } - - // 转换为路由配置 - return { - path: item.path, - name: transformPathToName(item.path), - component: item.component ? transformComponentView(item.component) : undefined, - redirect: item.redirect, - meta: { - hidden: item.hidden, // 是否在菜单中隐藏 - keepAlive: item.keepAlive, // 是否缓存该路由 - title: item.title, // 路由标题,用于显示在菜单和标签页 - icon: item.icon, // 图标 - affix: item.affix, // 是否固定在标签页 - breadcrumb: item.breadcrumb, // 是否显示在面包屑 - showInTabs: item.showInTabs, // 是否显示在标签页 - activeMenu: item.activeMenu, // 激活的菜单项 - alwaysShow: item.alwaysShow // 是否总是显示 - } - } - }) - return routes as RouteRecordRaw[] -} - -/** - * 路由状态管理的核心逻辑 - * @description 管理路由相关的状态和操作 - */ -const storeSetup = () => { - /** - * 所有路由配置 - * 包含常驻路由和动态路由 - */ - const routes = ref([]) - - /** - * 动态路由配置 - * 根据用户权限动态生成的路由 - */ - const asyncRoutes = ref([]) - - /** - * 合并路由配置 - * @description 将动态路由与常驻路由合并 - * @param {RouteRecordRaw[]} data - 动态路由配置 - */ - const setRoutes = (data: UserRouteItem[]) => { - const formattedRoutes = formatAsyncRoutes(data) - routes.value = constantRoutes.concat(formattedRoutes) - asyncRoutes.value = formattedRoutes - } - - return { - routes, - asyncRoutes, - setRoutes - } -} - -/** - * 路由状态管理 Store - * @description 创建路由相关的状态管理 store,启用持久化存储 - */ -export const useRouteStore = defineStore('route', storeSetup, { persist: true }) diff --git a/frontend-pc/src/stores/useTabsStore.ts b/frontend-pc/src/stores/useTabsStore.ts deleted file mode 100644 index b4e9767..0000000 --- a/frontend-pc/src/stores/useTabsStore.ts +++ /dev/null @@ -1,253 +0,0 @@ -/** - * @file 标签页状态管理模块 - * @description 处理标签页(Tabs)的状态管理,包括页签的增删改查和缓存控制 - */ - -import type { RouteLocationNormalized, RouteRecordName } from 'vue-router' -import { defineStore } from 'pinia' -import { nextTick, ref } from 'vue' -import { useRouter } from 'vue-router' - -/** - * 标签页 Store 的核心设置逻辑 - * @description 管理标签页相关的状态和操作 - */ -const storeSetup = () => { - const router = useRouter() - - /** - * 标签页列表 - * 存储当前打开的所有标签页 - */ - const tabList = ref([]) - - /** - * 缓存组件列表 - * 存储需要被 keep-alive 缓存的组件名称 - */ - const cacheList = ref([]) - - /** - * 页面重载标志 - * 用于控制页面的重新加载 - */ - const reloadFlag = ref(true) - - /** - * 判断是否为首页 - */ - const isHomePath = (path: string) => path === '/' || path === '/home' - - /** - * 获取所有固定标签页 - * @description 从路由中筛选出所有 affix: true 的固定标签页,首页排在最前面 - */ - const getAffixTabs = (): RouteLocationNormalized[] => { - const tabs = router - .getRoutes() - .filter((route) => route.meta?.affix) - .map((route) => route as unknown as RouteLocationNormalized) - // 首页排在最前面 - tabs.sort((a, b) => Number(isHomePath(b.path)) - Number(isHomePath(a.path))) - return tabs - } - - /** - * 更新缓存列表 - */ - const updateCache = (name: RouteRecordName | null | undefined, keepAlive: boolean | undefined) => { - if (!name) return - const hasCache = cacheList.value.includes(name) - if (keepAlive && !hasCache) { - cacheList.value.push(name) - } else if (!keepAlive && hasCache) { - const index = cacheList.value.findIndex((n) => n === name) - if (index >= 0) cacheList.value.splice(index, 1) - } - } - - /** - * 添加标签页 - * @description 添加新的标签页,如果已存在则更新,同时处理缓存 - */ - const addTabItem = (item: RouteLocationNormalized) => { - const index = tabList.value.findIndex((i) => i.path === item.path) - - if (index >= 0) { - // 更新已存在的标签页 - if (tabList.value[index].fullPath !== item.fullPath) { - tabList.value[index] = item - } - updateCache(item.name, item.meta?.keepAlive) - } else if (item.meta?.showInTabs ?? true) { - // 添加新标签页 - tabList.value.push(item) - updateCache(item.name, item.meta?.keepAlive) - } - } - - /** - * 设置标签页标题 - */ - const setTabTitle = (title: string): boolean => { - if (!title) return false - const route = router.currentRoute.value - const path = route?.fullPath || route.path - const tab = tabList.value.find((i) => i.fullPath === path) - if (tab) { - tab.meta.title = title - return true - } - return false - } - - /** - * 删除标签页并清理缓存 - */ - const removeTabByIndex = (index: number) => { - if (index < 0 || index >= tabList.value.length) return - const item = tabList.value[index] - updateCache(item.name, false) // 删除缓存 - tabList.value.splice(index, 1) - } - - /** - * 跳转到指定标签页 - */ - const jumpToTab = (path: string) => { - const tab = tabList.value.find((t) => t.path === path) ?? tabList.value[0] - if (tab) router.push(tab.fullPath || tab.path) - } - - /** - * 获取固定标签页的缓存列表 - */ - const getAffixCacheList = (tabs: RouteLocationNormalized[]) => { - return tabs.filter((tab) => tab.name && tab.meta?.keepAlive).map((tab) => tab.name!) - } - - /** - * 统一关闭标签页方法 - * @description 根据类型关闭标签页:left(左侧)、right(右侧)、other(其他)、all(全部)、current(当前) - */ - const close = (type: string, path?: string) => { - const currentPath = router.currentRoute.value.path - const targetPath = path || currentPath - const targetIndex = tabList.value.findIndex((i) => i.path === targetPath) - - // 关闭所有 - if (type === 'all') { - const affixTabs = getAffixTabs() - tabList.value = affixTabs - cacheList.value = getAffixCacheList(affixTabs) - router.push('/') - return - } - - // 关闭当前 - if (type === 'current') { - if (targetIndex < 0 || tabList.value[targetIndex].meta?.affix) return - removeTabByIndex(targetIndex) - // at(-1) 可能是 undefined —— 上面的 length 判断在类型层面窄化不了它, - // 所以这里要真的接住 undefined 再取 path(严格模式下是 TS2532) - const last = tabList.value.at(-1) - if (currentPath === targetPath && last) { - jumpToTab(last.path) - } - return - } - - if (targetIndex < 0) return - - // 关闭左侧 - if (type === 'left') { - for (let i = targetIndex - 1; i >= 0; i--) { - if (!tabList.value[i].meta?.affix) removeTabByIndex(i) - } - return - } - - // 关闭右侧 - if (type === 'right') { - const currentIndex = tabList.value.findIndex((t) => t.path === currentPath) - const willDeleteCurrent = currentIndex > targetIndex && !tabList.value[currentIndex]?.meta?.affix - - for (let i = tabList.value.length - 1; i > targetIndex; i--) { - if (!tabList.value[i].meta?.affix) removeTabByIndex(i) - } - - if (willDeleteCurrent && tabList.value.length) { - jumpToTab(tabList.value[targetIndex].path) - } - return - } - - // 关闭其他 - if (type === 'other') { - for (let i = tabList.value.length - 1; i >= 0; i--) { - const item = tabList.value[i] - if (item.path !== targetPath && !item.meta?.affix) { - removeTabByIndex(i) - } - } - const currentExists = tabList.value.some((t) => t.path === currentPath) - if (!currentExists && tabList.value.length) { - jumpToTab(targetPath) - } - } - } - - /** - * 重置标签页状态 - * @description 清空标签页和缓存列表,保留固定标签页及其缓存 - */ - const reset = () => { - const affixTabs = getAffixTabs() - tabList.value = affixTabs - cacheList.value = getAffixCacheList(affixTabs) - } - - /** - * 初始化标签页 - * @description 初始化标签页状态,如果没有非固定标签页则重置 - */ - const init = () => { - if (tabList.value.some((i) => !i?.meta?.affix)) return - reset() - } - - /** - * 重新加载当前页面 - * @description 清除当前页面的缓存并重新加载 - */ - const reloadPage = () => { - const route = router.currentRoute.value - if (!route.name) return - - updateCache(route.name, false) // 删除缓存 - reloadFlag.value = false - - nextTick(() => { - reloadFlag.value = true - updateCache(route.name, route.meta?.keepAlive) // 重新添加缓存 - }) - } - - return { - tabList, - cacheList, - reloadFlag, - addTabItem, - setTabTitle, - close, - reset, - init, - reloadPage - } -} - -/** - * 标签页状态管理 Store - * @description 创建标签页相关的状态管理 store,使用 sessionStorage 进行持久化存储 - */ -export const useTabsStore = defineStore('tabs', storeSetup, { persist: true }) diff --git a/frontend-pc/src/stores/useUserStore.ts b/frontend-pc/src/stores/useUserStore.ts deleted file mode 100644 index b0bc34d..0000000 --- a/frontend-pc/src/stores/useUserStore.ts +++ /dev/null @@ -1,168 +0,0 @@ -/** @file 用户状态管理模块 - 处理用户登录、权限和信息管理 */ - -import type { UserInfo } from '@/apis/user' -import type { LoginVO } from '@/apis/xt' -import { defineStore } from 'pinia' -import { computed, reactive, ref } from 'vue' -import { useRouter } from 'vue-router' -import { getMe, login as loginApi, logout as logoutApi } from '@/apis/xt' -import { buildXtRoutes } from '@/config/xt-routes' -import { resetRouter } from '@/router' -import { clearToken, getToken, setToken } from '@/utils/auth' -import { isHttp } from '@/utils/validate' -import { useActingTenantStore } from './useActingTenantStore' -import { useRouteStore } from './useRouteStore' - -/** 登录参数:password 必须是 RSA 加密后的密文(见 @/utils/rsa) */ -type LoginParams = Parameters[0] - -/** - * 用户 Store 的核心设置逻辑 - * - * 与模板原版的差别只有数据来源:模板调 `/user/login` 等 mock 接口, - * 这里改调本项目的 `/auth/login`(《03-接口设计》§5)。 - * 内部状态形状保持不变,`v-hasPerm` 指令、布局、标签页都不用改。 - */ -const storeSetup = () => { - const routeStore = useRouteStore() - const router = useRouter() - - /** 用户基本信息(形状对齐模板的 UserInfo) */ - const userInfo: UserInfo = reactive({ - id: '', - nickname: '', - avatar: '', - roles: [], - permissions: [] - }) - - /** 后端返回的原始档案,用于展示租户名等模板没有的字段 */ - const profile = ref(null) - - /** 菜单键集合(决定左侧导航) */ - const menuKeys = ref([]) - - /** 用户昵称 */ - const name = computed(() => userInfo.nickname) - /** 用户头像 */ - const avatar = computed(() => userInfo.avatar) - /** 用户角色列表 */ - const roles = computed(() => userInfo.roles) - /** 用户权限列表 */ - const permissions = computed(() => userInfo.permissions) - /** 用户令牌 */ - const token = ref(getToken() || '') - /** 所属租户名(侧栏/顶栏展示用) */ - const tenantName = computed(() => profile.value?.tenant?.tenantName ?? '') - /** 主角色中文名 */ - const roleLabel = computed(() => profile.value?.roleCodes?.[0] ?? '') - - /** 重置用户令牌 - 清除令牌并重置路由权限标志 */ - const resetToken = () => { - token.value = '' - clearToken() - } - - /** 把后端返回的 LoginVO 落到模板内部的状态上 */ - const applyProfile = (vo: LoginVO) => { - profile.value = vo - userInfo.id = String(vo.user?.id ?? '') - userInfo.nickname = vo.user?.realName ?? '' - userInfo.avatar = vo.user?.avatarUrl ?? '' - userInfo.roles = vo.roleCodes ?? [] - userInfo.permissions = vo.permissions ?? [] - menuKeys.value = vo.menus ?? [] - } - - /** - * 用户登录 - * @param params 含 RSA 加密后的密码 - */ - const login = async (params: LoginParams): Promise => { - const res = await loginApi(params) - const vo = res.data - if (!vo?.token) { - throw new Error('服务端未返回 token') - } - setToken(vo.token) - token.value = vo.token - // 登录响应已带 user/tenant/roleCodes/menus/permissions,直接落库,省一次 /auth/me 往返 - applyProfile(vo) - } - - /** 用户退出 */ - const logout = async (): Promise => { - try { - await logoutApi() - } catch { - // 登出失败也要清本地状态:否则用户会卡在"点了登出但还是登录着" - } finally { - token.value = '' - resetToken() - resetRouter() - profile.value = null - menuKeys.value = [] - // 平台运营的"当前学校"属于**上一次身份**的上下文,退出时一起清掉: - // 留着它,下一个登录的人(可能是学校管理员)会继承一个自己没选过的租户 - useActingTenantStore().clear() - } - return true - } - - /** 获取用户信息(刷新页面后靠它恢复状态) */ - const fetchUserInfo = async (): Promise => { - const res = await getMe() - applyProfile(res.data) - return true - } - - /** - * 生成动态路由 - * - * 模板原版是直接取后端下发的路由树;本项目后端只下发**菜单键**, - * 所以这里在本地路由表(@/config/xt-routes)上做一次过滤。 - * 这样"有哪些页面"由前端代码掌控,"谁能看"由后端掌控,两边职责清晰。 - */ - const generateRoutes = async (): Promise => { - // 已登录但刷新过页面时,profile 是空的,先补回来 - if (!menuKeys.value.length) { - await fetchUserInfo() - } - routeStore.setRoutes(buildXtRoutes(menuKeys.value)) - routeStore.asyncRoutes.forEach((item) => { - if (!isHttp(item.path)) { - router.addRoute(item) - } - }) - return true - } - - return { - userInfo, - profile, - menuKeys, - name, - avatar, - roles, - permissions, - token, - tenantName, - roleLabel, - login, - logout, - fetchUserInfo, - resetToken, - generateRoutes - } -} - -/** - * 用户状态管理 Store - * @description 创建用户相关的状态管理 store,使用 localStorage 进行持久化存储 - */ -export const useUserStore = defineStore('user', storeSetup, { - persist: { - pick: ['token'], - storage: localStorage - } -}) diff --git a/frontend-pc/src/stores/user.ts b/frontend-pc/src/stores/user.ts new file mode 100644 index 0000000..933fb23 --- /dev/null +++ b/frontend-pc/src/stores/user.ts @@ -0,0 +1,121 @@ +import type { LoginReq, LoginVO } from '@/apis/xt' +import { defineStore } from 'pinia' +import { computed, ref } from 'vue' +import { getMe, login as loginApi, logout as logoutApi } from '@/apis/xt' +import { clearToken, getToken, setToken } from '@/utils/auth' +import { useActingTenantStore } from './acting-tenant' + +/** + * 当前登录用户。 + * + * 数据全部来自后端:`/auth/login` 与 `/auth/me` 返回的 `LoginVO` + * (user / tenant / roleCodes / menus / permissions)。前端**不自己推断** + * 菜单与权限 —— 那样会与后端漂移,而不一致的表现是"按钮显示出来了、点了报 40301"。 + */ +export const useUserStore = defineStore('user', () => { + const token = ref(getToken()) + const profile = ref(null) + /** 是否已经从后端拿到过档案(路由守卫据此决定要不要补) */ + const loaded = ref(false) + + const name = computed(() => profile.value?.user?.realName ?? '') + const username = computed(() => profile.value?.user?.username ?? '') + const avatar = computed(() => profile.value?.user?.avatarUrl ?? '') + const tenantName = computed(() => profile.value?.tenant?.tenantName ?? '') + const tenantId = computed(() => profile.value?.tenant?.id ?? profile.value?.user?.orgId ?? null) + const userType = computed(() => profile.value?.user?.userType ?? null) + const roles = computed(() => profile.value?.roleCodes ?? []) + const menus = computed(() => profile.value?.menus ?? []) + const permissions = computed(() => profile.value?.permissions ?? []) + + /** 平台运营身份(决定"先选学校"这类交互) */ + const isPlatform = computed(() => roles.value.includes('PLATFORM_ADMIN')) + /** 是否租户级管理员(user_type = 2,与后端 LoginUser.isTenantAdmin 同一口径) */ + const isTenantAdmin = computed(() => userType.value === 2) + + /** 有某个权限码? */ + function has(code: string): boolean { + return permissions.value.includes(code) + } + + /** 有其中任意一个权限码? */ + function hasAny(...codes: string[]): boolean { + return codes.some((c) => permissions.value.includes(c)) + } + + function apply(vo: LoginVO): void { + profile.value = vo + loaded.value = true + } + + /** + * 登录。 + * + * ⚠️ 入参类型直接用接口契约里的 {@link LoginReq}(而不是手写一份字段): + * 登录页要传 `tenantCode`(账号在多个单位下重名时)以及**连续失败 2 次后的图形验证码** + * `captchaKey` / `captchaCode`。手写一份子集会让登录页在传验证码时 typecheck 报错, + * 而"少一个字段"在运行期只是"验证码没用上",很难查。 + */ + async function login(payload: LoginReq): Promise { + const vo = await loginApi(payload) + if (!vo?.token) { + throw new Error('服务端未返回 token') + } + setToken(vo.token) + token.value = vo.token + // 登录响应已带 user/tenant/roleCodes/menus/permissions,直接落库,省一次 /auth/me 往返 + apply(vo) + } + + async function fetchMe(): Promise { + apply(await getMe()) + } + + async function logout(): Promise { + try { + await logoutApi() + } catch { + // 登出失败也要清本地:否则用户会卡在"点了登出但还是登录着" + } finally { + clearToken() + token.value = '' + profile.value = null + loaded.value = false + // 动态 import:静态引 `@/router` 会形成 router → layout → stores → router 的环。 + // 退出登录只发生一次,这点动态开销换来的是"依赖方向单向"。 + const { resetMenus } = await import('@/router') + resetMenus() + // 平台运营的"当前学校"属于**上一次身份**的上下文,退出时一起清掉: + // 留着它,下一个登录的人(可能是学校管理员)会继承一个自己没选过的租户 + useActingTenantStore().clear() + } + } + + return { + token, + profile, + loaded, + name, + username, + avatar, + tenantName, + tenantId, + userType, + roles, + menus, + permissions, + isPlatform, + isTenantAdmin, + has, + hasAny, + login, + fetchMe, + logout + } +}, { + persist: { + key: 'xt-user', + pick: ['token'], + storage: localStorage + } +}) diff --git a/frontend-pc/src/styles/animated.scss b/frontend-pc/src/styles/animated.scss deleted file mode 100644 index 168cfd0..0000000 --- a/frontend-pc/src/styles/animated.scss +++ /dev/null @@ -1,23 +0,0 @@ -@keyframes fade-up { - 0% { - opacity: 0; - transform: translateY(60px); - } - - 100% { - opacity: 1; - transform: translateY(0); - } -} - -@for $i from 0 through 12 { - .animated-fade-up-#{$i} - - { - opacity: 0; - animation-name: fade-up; - animation-duration: 0.5s; - animation-delay: calc($i/10) + s; - animation-fill-mode: forwards; - } -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/a-alert.less b/frontend-pc/src/styles/arco-ui/a-alert.less deleted file mode 100644 index c77887d..0000000 --- a/frontend-pc/src/styles/arco-ui/a-alert.less +++ /dev/null @@ -1,3 +0,0 @@ -.arco-alert { - flex-shrink: 0; -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/a-card.less b/frontend-pc/src/styles/arco-ui/a-card.less deleted file mode 100644 index 8b13789..0000000 --- a/frontend-pc/src/styles/arco-ui/a-card.less +++ /dev/null @@ -1 +0,0 @@ - diff --git a/frontend-pc/src/styles/arco-ui/a-color-picker.less b/frontend-pc/src/styles/arco-ui/a-color-picker.less deleted file mode 100644 index e363ca7..0000000 --- a/frontend-pc/src/styles/arco-ui/a-color-picker.less +++ /dev/null @@ -1,19 +0,0 @@ -.arco-color-picker-panel .arco-color-picker-panel-control .arco-color-picker-input-wrapper .arco-select-view, -.arco-color-picker-panel .arco-color-picker-panel-control .arco-color-picker-input-wrapper .arco-input-wrapper { - padding-right: 0; -} - -.arco-color-picker-panel .arco-color-picker-input-alpha { - flex: none; - width: 70px; - - .arco-input-number-step { - width: 20px; - margin-left: 4px; - } -} - -.arco-color-picker-panel .arco-color-picker-input-alpha:not(.arco-input-disabled):not(.arco-input-outer-disabled):hover .arco-input-number-suffix, -.arco-color-picker-panel .arco-color-picker-input-alpha:not(.arco-input-disabled):not(.arco-input-outer-disabled):focus-within .arco-input-number-suffix { - opacity: 1; -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/a-descriptions.less b/frontend-pc/src/styles/arco-ui/a-descriptions.less deleted file mode 100644 index 060f7d4..0000000 --- a/frontend-pc/src/styles/arco-ui/a-descriptions.less +++ /dev/null @@ -1,11 +0,0 @@ -.arco-descriptions-layout-inline-horizontal { - &.arco-descriptions-size-medium { - .arco-descriptions-item { - padding-bottom: 8px; - } - } -} - -.arco-descriptions-layout-inline-horizontal .arco-descriptions-item-label-inline { - margin-right: 10px; -} diff --git a/frontend-pc/src/styles/arco-ui/a-form.less b/frontend-pc/src/styles/arco-ui/a-form.less deleted file mode 100644 index ad79db4..0000000 --- a/frontend-pc/src/styles/arco-ui/a-form.less +++ /dev/null @@ -1,28 +0,0 @@ -// 清除默认过渡动画 -.form-blink-enter-from, -.form-blink-appear-from { - opacity: inherit; -} -.form-blink-enter-to, -.form-blink-appear-to { - opacity: inherit; -} -.form-blink-enter-active, -.form-blink-appear-active { - transition: none; - animation: none; -} - -// 重设过渡动画 -.form-blink-enter-active, -.form-blink-leave-active { - opacity: 1; - transform: scaleY(1); - transition: transform 0.3s cubic-bezier(.23, 1, .32, 1), opacity 0.1s cubic-bezier(.23, 1, .32, 1); - transform-origin: center top; -} -.form-blink-enter-from, -.form-blink-leave-active { - opacity: 0; - transform: scaleY(0); -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/a-input-number.less b/frontend-pc/src/styles/arco-ui/a-input-number.less deleted file mode 100644 index c16265a..0000000 --- a/frontend-pc/src/styles/arco-ui/a-input-number.less +++ /dev/null @@ -1,70 +0,0 @@ -// 线性主题优化 -.arco-input-number.arco-input-number-mode-button { - .arco-input-prepend { - .arco-btn { - border-right: 0; - } - } - - .arco-input-append { - .arco-btn { - border-left: 0; - } - } -} - -.arco-input-number-mode-embed { - padding-right: 0; - overflow: hidden; - - &.arco-input-wrapper:not(.arco-input-focus) .arco-input-number-step-button:not(.arco-input-number-step-button-disabled):hover { - background-color: var(--color-fill-2); - } - - .arco-input-number-step { - top: 0; - right: 0; - bottom: 0; - position: static; - display: flex; - flex-direction: column; - width: 34px; - height: 100%; - opacity: 1; - visibility: visible; - transform: none; - border-left: 1px solid var(--color-border-2); - } - - .arco-input-number-step-button { - display: flex; - flex: 1; - align-items: center; - justify-content: center; - width: 100%; - height: auto; - min-height: 0; - border-left: 1px solid var(--color-border-2); - - &:not(.arco-input-number-step-button-disabled) { - &:hover { - .arco-icon { - color: rgb(var(--primary-6)); - } - } - } - - &:hover, - &:active { - background-color: var(--color-fill-2); - } - } - - .arco-input-number-step-button+.arco-input-number-step-button { - border-top: 1px solid var(--color-border-2); - } - - .arco-input-number-input { - padding-right: 34px; - } -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/a-input.less b/frontend-pc/src/styles/arco-ui/a-input.less deleted file mode 100644 index 9d75d17..0000000 --- a/frontend-pc/src/styles/arco-ui/a-input.less +++ /dev/null @@ -1,7 +0,0 @@ -// 线性主题优化 -.arco-input-prepend { - border-right: none; -} -.arco-input-append { - border-left: none; -} diff --git a/frontend-pc/src/styles/arco-ui/a-menu.less b/frontend-pc/src/styles/arco-ui/a-menu.less deleted file mode 100644 index 911c5c4..0000000 --- a/frontend-pc/src/styles/arco-ui/a-menu.less +++ /dev/null @@ -1,18 +0,0 @@ -.arco-trigger-menu-item.arco-trigger-menu-selected, -.arco-trigger-menu-pop-header.arco-trigger-menu-selected { - color: rgb(var(--primary-6)); -} - -.arco-menu-icon-suffix { - transition: all 0.2s; -} - -.arco-menu-horizontal .arco-menu-pop { - display: inline-flex; -} - -.arco-menu-icon { - display: inline-flex; - justify-content: center; - align-items: center; -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/a-message.less b/frontend-pc/src/styles/arco-ui/a-message.less deleted file mode 100644 index a8a04f7..0000000 --- a/frontend-pc/src/styles/arco-ui/a-message.less +++ /dev/null @@ -1,28 +0,0 @@ -.fade-message-enter-from { - opacity: 0; - transform: translate3d(0, -100%, 0); -} - -.fade-message-enter-to { - opacity: 1; - transform: translate3d(0, 0, 0); -} - -.fade-message-enter-active, -.fade-message-appear-active { - transition: opacity @transition-duration-3, transform @transition-duration-4; -} - -.fade-message-leave-from { - opacity: 1; - transform: translate3d(0, 0, 0); -} - -.fade-message-leave-to { - opacity: 0; - transform: translate3d(0, -100%, 0); -} - -.fade-message-leave-active { - position: absolute; -} diff --git a/frontend-pc/src/styles/arco-ui/a-modal.less b/frontend-pc/src/styles/arco-ui/a-modal.less deleted file mode 100644 index 448cc68..0000000 --- a/frontend-pc/src/styles/arco-ui/a-modal.less +++ /dev/null @@ -1,44 +0,0 @@ -.arco-modal-title .arco-icon-exclamation-circle-fill { - font-size: 24px; -} - -.arco-modal-title-icon { - display: flex; - justify-content: center; - align-items: center; - margin-right: 4px; -} - -.arco-modal-simple { - width: 90%; - max-width: 320px; - padding: 20px; - box-sizing: border-box; - border-radius: 20px; - - .arco-modal-header { - margin-bottom: 0; - - .arco-modal-title { - font-size: 18px; - } - } - - .arco-modal-body { - padding: 20px 0; - font-size: 16px; - text-align: center; - } - - .arco-modal-footer { - margin-top: 0; - display: flex; - - >.arco-btn { - flex: 1; - height: 38px; - border-radius: 100px; - font-weight: bold; - } - } -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/a-page-header.less b/frontend-pc/src/styles/arco-ui/a-page-header.less deleted file mode 100644 index 70c8da0..0000000 --- a/frontend-pc/src/styles/arco-ui/a-page-header.less +++ /dev/null @@ -1,6 +0,0 @@ -.arco-page-header { - padding: 8px 0; - background: var(--color-bg-1); - border-bottom: 1px solid var(--color-border-2); - box-sizing: border-box; -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/a-pagination.less b/frontend-pc/src/styles/arco-ui/a-pagination.less deleted file mode 100644 index 25661fd..0000000 --- a/frontend-pc/src/styles/arco-ui/a-pagination.less +++ /dev/null @@ -1,41 +0,0 @@ -// 分页组件 -.arco-pagination-item { - padding: 0 6px; - border: 1px solid var(--color-border-3); - background-color: var(--color-bg-2); - border-radius: var(--border-radius-small); - display: inline-flex; - justify-content: center; - align-items: center; - transition: none; - box-sizing: border-box; - &:hover { - color: rgb(var(--primary-6)); - border: 1px solid rgb(var(--primary-6)); - background-color: rgba(var(--primary-6), 0.1); - } - &:active { - color: #fff; - background-color: rgb(var(--primary-6)); - border: 1px solid rgb(var(--primary-6)); - } -} - -.arco-pagination-item-active { - color: #fff; - background-color: rgb(var(--primary-6)); - border: 1px solid rgb(var(--primary-6)); - &:hover { - color: #fff; - background-color: rgb(var(--primary-6)); - border: 1px solid rgb(var(--primary-6)); - } -} - -.arco-pagination .arco-pagination-item-previous.arco-pagination-item-disabled, -.arco-pagination .arco-pagination-item-next.arco-pagination-item-disabled { - border-color: var(--color-border-2); - &:hover { - border-color: var(--color-border-2); - } -} diff --git a/frontend-pc/src/styles/arco-ui/a-picker.less b/frontend-pc/src/styles/arco-ui/a-picker.less deleted file mode 100644 index 5c5aaed..0000000 --- a/frontend-pc/src/styles/arco-ui/a-picker.less +++ /dev/null @@ -1,3 +0,0 @@ -.arco-picker { - width: 100%; -} diff --git a/frontend-pc/src/styles/arco-ui/a-radio.less b/frontend-pc/src/styles/arco-ui/a-radio.less deleted file mode 100644 index 430e2a0..0000000 --- a/frontend-pc/src/styles/arco-ui/a-radio.less +++ /dev/null @@ -1,3 +0,0 @@ -.arco-radio-button-content { - white-space: nowrap; -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/a-scrollbar.less b/frontend-pc/src/styles/arco-ui/a-scrollbar.less deleted file mode 100644 index ba3a990..0000000 --- a/frontend-pc/src/styles/arco-ui/a-scrollbar.less +++ /dev/null @@ -1,21 +0,0 @@ -.arco-scrollbar-track-direction-vertical { - width: 8px; -} - -.arco-scrollbar-thumb-direction-vertical .arco-scrollbar-thumb-bar { - width: 6px; - margin: 0 1px; -} - -.arco-scrollbar-track-direction-horizontal { - height: 8px; -} - -.arco-scrollbar-thumb-direction-horizontal .arco-scrollbar-thumb-bar { - height: 6px; - margin: 1px 0; -} - -.arco-scrollbar-thumb-bar { - background-color: var(--color-fill-3); -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/a-space.less b/frontend-pc/src/styles/arco-ui/a-space.less deleted file mode 100644 index e69de29..0000000 diff --git a/frontend-pc/src/styles/arco-ui/a-steps.less b/frontend-pc/src/styles/arco-ui/a-steps.less deleted file mode 100644 index 2b22702..0000000 --- a/frontend-pc/src/styles/arco-ui/a-steps.less +++ /dev/null @@ -1,5 +0,0 @@ -.arco-steps-item-process .arco-steps-icon { - display: flex; - justify-content: center; - align-items: center; -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/a-switch.less b/frontend-pc/src/styles/arco-ui/a-switch.less deleted file mode 100644 index 1dbaa77..0000000 --- a/frontend-pc/src/styles/arco-ui/a-switch.less +++ /dev/null @@ -1,7 +0,0 @@ -.arco-switch-type-round.arco-switch-small { - border-radius: 3px; -} - -.arco-switch-type-round.arco-switch-small .arco-switch-handle { - border-radius: 2px; -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/a-table.less b/frontend-pc/src/styles/arco-ui/a-table.less deleted file mode 100644 index 6a38f3b..0000000 --- a/frontend-pc/src/styles/arco-ui/a-table.less +++ /dev/null @@ -1,9 +0,0 @@ -.arco-table-th-title { - font-size: 13px; - font-weight: 600; -} - -.arco-table-container { - border-radius: var(--border-radius-small); - overflow: hidden; -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/a-tabs.less b/frontend-pc/src/styles/arco-ui/a-tabs.less deleted file mode 100644 index 895b36a..0000000 --- a/frontend-pc/src/styles/arco-ui/a-tabs.less +++ /dev/null @@ -1,17 +0,0 @@ -.arco-tabs-nav-extra:not(:empty) { - margin-right: var(--padding); -} - -.arco-tabs { - background: var(--color-bg-1); -} - -.arco-tabs-nav-type-rounded .arco-tabs-tab { - margin: 4px; - padding: 2px 12px; -} - -.arco-tabs-nav-type-card .arco-tabs-tab:first-child, -.arco-tabs-nav-type-card .arco-tabs-tab:last-child { - border-radius: 0; -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/a-tree.less b/frontend-pc/src/styles/arco-ui/a-tree.less deleted file mode 100644 index ef420f7..0000000 --- a/frontend-pc/src/styles/arco-ui/a-tree.less +++ /dev/null @@ -1,23 +0,0 @@ -.arco-tree-show-line .arco-tree-node-is-leaf:not(.arco-tree-node-is-tail) .arco-tree-node-indent::after, -.arco-tree-show-line .arco-tree-node-indent-block::before { - transform: translateX(0); - border-color: var(--color-border-3); - flex-shrink: 0; -} - -.arco-tree-node-minus-icon, -.arco-tree-node-plus-icon { - border: 1px solid var(--color-border-3); - box-sizing: border-box; - border-radius: 4px; -} - -.arco-tree-node-switcher-icon { - display: flex; - justify-content: center; - align-items: center; -} - -.arco-tree-node-title { - white-space: nowrap; -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/index.less b/frontend-pc/src/styles/arco-ui/index.less deleted file mode 100644 index bcea62e..0000000 --- a/frontend-pc/src/styles/arco-ui/index.less +++ /dev/null @@ -1,46 +0,0 @@ -// Arco 的 LESS 变量(供本项目组件覆写时引用,如 @transition-duration-3)。 -// 这是纯变量文件,不输出任何 CSS —— 原本由被移除的 @arco-themes/vue-gi-demo 带入。 -@import '@arco-design/web-vue/es/style/theme/index.less'; - -// 本项目主题:腾讯云 / TDesign 色板(覆盖 Arco 底层色板,上层自动跟随) -@import './xt-theme.less'; - -@import './a-alert.less'; -@import './a-card.less'; -@import './a-color-picker.less'; -@import './a-descriptions.less'; -@import './a-form.less'; -@import './a-input-number.less'; -@import './a-input.less'; -@import './a-menu.less'; -@import './a-message.less'; -@import './a-modal.less'; -@import './a-page-header.less'; -@import './a-pagination.less'; -@import './a-picker.less'; -@import './a-radio.less'; -@import './a-scrollbar.less'; -@import './a-space.less'; -@import './a-steps.less'; -@import './a-switch.less'; -@import './a-table.less'; -@import './a-tabs.less'; -@import './a-tree.less'; - -.app-menu-dark:extend(body[arco-theme='dark']) { - --color-menu-dark-bg: #222222; -} - -// 禁止文本复制 -.arco-link, -.arco-menu-title, -.arco-menu-item-inner, -.arco-tabs-tab-title, -.arco-breadcrumb-item, -.arco-checkbox-label { - user-select: none; -} - -.arco-menu .arco-menu-item { - display: inline-flex; -} \ No newline at end of file diff --git a/frontend-pc/src/styles/arco-ui/xt-theme.less b/frontend-pc/src/styles/arco-ui/xt-theme.less deleted file mode 100644 index 280d644..0000000 --- a/frontend-pc/src/styles/arco-ui/xt-theme.less +++ /dev/null @@ -1,237 +0,0 @@ -/** - * 腾讯云(TDesign)主题 - * ================================================================ - * 色值来源:TDesign 官方 light 主题 token - * https://github.com/Tencent/tdesign-common style/web/theme/_light.less - * 主色 = 腾讯蓝 #0052D9 - * - * ⚠️ 格式坑(写错整个主题不生效,且不报错): - * Arco 的颜色变量是 **逗号分隔的 RGB 三元组**,不是 #RRGGBB。 - * 定义:--arcoblue-6: 22,93,255; - * 消费:rgb(var(--primary-6)) - * 写成 `--arcoblue-6: #0052d9;` 的话,`rgb(#0052d9)` 是非法值, - * 浏览器会静默丢弃该声明 —— 表现是"主题完全不生效",但控制台没有报错。 - * - * 为什么只覆写底层色板就够了: - * Arco 的 --primary-N / --success-N / --warning-N / --danger-N / --link-N - * 分别是 --arcoblue-N / --green-N / --orange-N / --red-N 的别名, - * 而 --color-text-* / --color-border-* / --color-fill-* 又派生自 gray 色板。 - * 所以换底层色板 = 整套主题跟着换,不用逐个组件去改。 - * - * 索引对应关系(两套色板的「主色」下标不同,必须按角色映射,不能按下标硬对): - * Arco : -1 最浅底 · -5 hover · -6 主色 · -7 active · -8~-10 更深 - * TD : -1 最浅底 · -7 hover · -8 主色 · -9 active · -10 最深 - */ - -/** - * ⚠️ 必须同时写 `:root, body`,只写 `:root` 是不生效的。 - * - * Arco 把自己的色板定义在 **body** 选择器上(不是 :root)。 - * CSS 自定义属性是**按元素解析**的:body 上的 `--arcoblue-6` 会盖掉 - * 从 html(:root)继承下来的同名变量,哪怕我们的样式加载在后面。 - * 所以覆盖必须落在同一层级(body)才能赢。 - * - * 排查特征:`getComputedStyle(document.documentElement).getPropertyValue('--arcoblue-6')` - * 已经是新值,但按钮颜色没变 —— 就是这个原因。 - */ -:root, -body { - /* ── 品牌色:腾讯蓝 #0052D9 ────────────────────────── - TD brand: 1 #ecf2fe · 2 #d4e3fc · 3 #bbd3fb · 4 #96bbf8 - 5 #699ef5 · 6 #4787f0 · 7 #266fe8 · 8 #0052d9(主) - 9 #0034b5 · 10 #001f97 - Arco 9/10 由 TD 10 继续压暗推导(TD 官方只到 10 阶) */ - --arcoblue-1: 236, 242, 254; /* TD brand-1 #ecf2fe */ - --arcoblue-2: 212, 227, 252; /* TD brand-2 #d4e3fc */ - --arcoblue-3: 187, 211, 251; /* TD brand-3 #bbd3fb */ - --arcoblue-4: 150, 187, 248; /* TD brand-4 #96bbf8 */ - --arcoblue-5: 71, 135, 240; /* TD brand-6 #4787f0 — Arco 的 -5 是 hover */ - --arcoblue-6: 0, 82, 217; /* TD brand-8 #0052d9 ★ 主色 */ - --arcoblue-7: 0, 52, 181; /* TD brand-9 #0034b5 — Arco 的 -7 是 active */ - --arcoblue-8: 0, 31, 151; /* TD brand-10 #001f97 */ - --arcoblue-9: 0, 22, 122; /* 推导:由 #001f97 再压暗 */ - --arcoblue-10: 0, 18, 95; /* 推导 */ - - /* ── 成功色:TD success,主色 -5 #00a870 ───────────── */ - --green-1: 232, 248, 242; /* TD success-1 #e8f8f2 */ - --green-2: 188, 235, 220; /* TD success-2 #bcebdc */ - --green-3: 133, 219, 190; /* TD success-3 #85dbbe */ - --green-4: 103, 209, 173; /* 推导:TD 3↔4 之间,给 Arco -4 留一档 */ - --green-5: 72, 199, 156; /* TD success-4 #48c79c — hover */ - --green-6: 0, 168, 112; /* TD success-5 #00a870 ★ 主色 */ - --green-7: 7, 141, 92; /* TD success-6 #078d5c — active */ - --green-8: 6, 121, 69; /* TD success-7 #067945 */ - --green-9: 5, 99, 52; /* TD success-8 #056334 */ - --green-10: 4, 79, 42; /* TD success-9 #044f2a */ - - /* ── 警告色:TD warning,主色 -5 #ed7b2f ───────────── */ - --orange-1: 254, 243, 230; /* TD warning-1 #fef3e6 */ - --orange-2: 249, 224, 199; /* TD warning-2 #f9e0c7 */ - --orange-3: 247, 199, 151; /* TD warning-3 #f7c797 */ - --orange-4: 244, 182, 122; /* 推导:TD 3↔4 之间 */ - --orange-5: 242, 153, 95; /* TD warning-4 #f2995f — hover */ - --orange-6: 237, 123, 47; /* TD warning-5 #ed7b2f ★ 主色 */ - --orange-7: 211, 90, 33; /* TD warning-6 #d35a21 — active */ - --orange-8: 186, 67, 27; /* TD warning-7 #ba431b */ - --orange-9: 158, 54, 16; /* TD warning-8 #9e3610 */ - --orange-10: 132, 43, 11; /* TD warning-9 #842b0b */ - - /* ── 危险色:TD error,主色 -6 #e34d59(这一族与 Arco 下标一一对应)── */ - --red-1: 253, 236, 238; /* TD error-1 #fdecee */ - --red-2: 249, 215, 217; /* TD error-2 #f9d7d9 */ - --red-3: 248, 185, 190; /* TD error-3 #f8b9be */ - --red-4: 247, 141, 148; /* TD error-4 #f78d94 */ - --red-5: 243, 109, 120; /* TD error-5 #f36d78 — hover */ - --red-6: 227, 77, 89; /* TD error-6 #e34d59 ★ 主色 */ - --red-7: 201, 53, 63; /* TD error-7 #c9353f — active */ - --red-8: 177, 31, 38; /* TD error-8 #b11f26 */ - --red-9: 149, 17, 20; /* TD error-9 #951114 */ - --red-10: 104, 5, 6; /* TD error-10 #680506 */ - - /* ── 中性灰:TD gray(TD 有 14 阶,Arco 需要 10 阶,按明度对齐)── */ - --gray-1: 243, 243, 243; /* TD gray-1 #f3f3f3 */ - --gray-2: 231, 231, 231; /* TD gray-3 #e7e7e7 */ - --gray-3: 220, 220, 220; /* TD gray-4 #dcdcdc */ - --gray-4: 197, 197, 197; /* TD gray-5 #c5c5c5 */ - --gray-5: 166, 166, 166; /* TD gray-6 #a6a6a6 */ - --gray-6: 139, 139, 139; /* TD gray-7 #8b8b8b */ - --gray-7: 119, 119, 119; /* TD gray-8 #777777 */ - --gray-8: 94, 94, 94; /* TD gray-9 #5e5e5e */ - --gray-9: 56, 56, 56; /* TD gray-11 #383838 */ - --gray-10: 44, 44, 44; /* TD gray-12 #2c2c2c */ - - /* ── 文字:直接用 TD 的 rgba 写法,不经过 gray 色板 ── - (文字是最高频的视觉元素,不绕一层间接引用更可控) */ - --color-text-1: rgba(0, 0, 0, 90%); - --color-text-2: rgba(0, 0, 0, 60%); - --color-text-3: rgba(0, 0, 0, 40%); - --color-text-4: rgba(0, 0, 0, 26%); - - /* ── 描边:TD border-level-1 #e7e7e7 / level-2 #dcdcdc ── */ - --color-border-1: #f3f3f3; - --color-border-2: #e7e7e7; - --color-border-3: #dcdcdc; - --color-border-4: #c5c5c5; - - /* ── 填充 ─────────────────────────────────────────── */ - --color-fill-1: #f3f3f3; - --color-fill-2: #eee; - --color-fill-3: #e7e7e7; - --color-fill-4: #dcdcdc; - - /* ── 背景:页面 #f3f3f3、容器 #fff(TD 的 page / container)── */ - --color-bg-1: #fff; - --color-bg-2: #fff; - --color-bg-3: #fff; - --color-bg-4: #fff; - --color-bg-5: #f3f3f3; - --color-bg-white: #fff; - - /* ── 圆角:Arco 默认 2/4/8,调成更接近 TD 的 4/6/8 ── */ - --border-radius-small: 4px; - --border-radius-medium: 6px; - --border-radius-large: 8px; - - /* ── 主色 RGB 明文,供 rgb(var(--xt-primary)) 这类自定义场景用 ── */ - --xt-primary: 0, 82, 217; - --xt-primary-hover: 71, 135, 240; - --xt-primary-active: 0, 52, 181; - --xt-success: 0, 168, 112; - --xt-warning: 237, 123, 47; - --xt-danger: 227, 77, 89; - --xt-page-bg: #f3f3f3; - --xt-container-bg: #fff; - --xt-text-1: rgba(0, 0, 0, 90%); - --xt-text-2: rgba(0, 0, 0, 60%); - --xt-text-3: rgba(0, 0, 0, 40%); - - /* ──────────────────────────────────────────────────────────────── - 语义别名:显式覆盖,不走 Arco 的 --xxx-N: var(--palette-N) 间接层。 - 实测那一层在 body 上算出来的仍是 Arco 默认色,所以这里直接给值。 - ──────────────────────────────────────────────────────────────── */ - - /* 品牌(腾讯蓝) */ - --primary-1: 236, 242, 254; - --primary-2: 212, 227, 252; - --primary-3: 187, 211, 251; - --primary-4: 150, 187, 248; - --primary-5: 71, 135, 240; - --primary-6: 22,93,255; - --primary-7: 0, 52, 181; - --primary-8: 0, 31, 151; - --primary-9: 0, 22, 122; - --primary-10: 0, 18, 95; - - /* 成功 */ - --success-1: 232, 248, 242; - --success-2: 188, 235, 220; - --success-3: 133, 219, 190; - --success-4: 103, 209, 173; - --success-5: 72, 199, 156; - --success-6: 0, 168, 112; - --success-7: 7, 141, 92; - --success-8: 6, 121, 69; - --success-9: 5, 99, 52; - --success-10: 4, 79, 42; - - /* 警告 */ - --warning-1: 254, 243, 230; - --warning-2: 249, 224, 199; - --warning-3: 247, 199, 151; - --warning-4: 244, 182, 122; - --warning-5: 242, 153, 95; - --warning-6: 237, 123, 47; - --warning-7: 211, 90, 33; - --warning-8: 186, 67, 27; - --warning-9: 158, 54, 16; - --warning-10: 132, 43, 11; - - /* 危险 */ - --danger-1: 253, 236, 238; - --danger-2: 249, 215, 217; - --danger-3: 248, 185, 190; - --danger-4: 247, 141, 148; - --danger-5: 243, 109, 120; - --danger-6: 227, 77, 89; - --danger-7: 201, 53, 63; - --danger-8: 177, 31, 38; - --danger-9: 149, 17, 20; - --danger-10: 104, 5, 6; - - /* 链接色跟随品牌 */ - --link-1: 236, 242, 254; - --link-2: 212, 227, 252; - --link-3: 187, 211, 251; - --link-4: 150, 187, 248; - --link-5: 71, 135, 240; - --link-6: 22,93,255; - --link-7: 0, 52, 181; - --link-8: 0, 31, 151; - --link-9: 0, 22, 122; - --link-10: 0, 18, 95; - - /* 中性色(--color-neutral-N 由它派生,文字/描边/填充都跟着走) */ - --color-neutral-1: 243, 243, 243; - --color-neutral-2: 231, 231, 231; - --color-neutral-3: 220, 220, 220; - --color-neutral-4: 197, 197, 197; - --color-neutral-5: 166, 166, 166; - --color-neutral-6: 139, 139, 139; - --color-neutral-7: 119, 119, 119; - --color-neutral-8: 94, 94, 94; - --color-neutral-9: 56, 56, 56; - --color-neutral-10: 44, 44, 44; -} - -/** - * 暗色模式下保持同一主色体系。 - * Arco 通过 body[arco-theme='dark'] 切换,这里只覆盖品牌色与页面底色, - * 其余(文字/背景/描边)沿用 Arco 自己的暗色派生,避免手写一套不完整的暗色。 - */ -body[arco-theme='dark'] { - --arcoblue-6: 64, 128, 255; /* 暗色下主色提亮,保证对比度 */ - --arcoblue-5: 106, 161, 255; - --arcoblue-7: 22, 93, 255; - --xt-page-bg: #17171a; - --xt-container-bg: #232324; -} diff --git a/frontend-pc/src/styles/base.scss b/frontend-pc/src/styles/base.scss deleted file mode 100644 index 179d0c1..0000000 --- a/frontend-pc/src/styles/base.scss +++ /dev/null @@ -1,53 +0,0 @@ -* { - padding: 0; - margin: 0; -} - -html, -body, -#app { - height: 100%; - padding: 0; - margin: 0; -} - -html { - font-size: 14px; -} - -body { - --margin: 14px; - --padding: 16px; - overflow: hidden; - // font-family: "helvetica neue", helvetica, "pingfang sc", "hiragino sans gb", "microsoft yahei", "\5fae\8f6f\96c5\9ed1", "arial/sans-serif"; - color: var(--color-text-2); - // 页面底色:腾讯云 / TDesign 的 bg-color-page(--td-gray-color-1) - // 用变量而不是写死色值,暗色模式与后续换色都不用改这里 - background-color: var(--xt-page-bg, #f3f3f3); - text-rendering: optimizelegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -body[arco-theme='dark'] { - background-color: #17171a; // body背景颜色-深色模式(Arco 暗色底) -} - -#app { - width: 100vw; - height: 100vh; - overflow: hidden; -} - -ul { - margin-bottom: 0; - list-style: none !important; -} - -a, -a:focus, -a:hover { - color: inherit; - text-decoration: none; - cursor: pointer; -} \ No newline at end of file diff --git a/frontend-pc/src/styles/css/transition.css b/frontend-pc/src/styles/css/transition.css deleted file mode 100644 index faa4a50..0000000 --- a/frontend-pc/src/styles/css/transition.css +++ /dev/null @@ -1,163 +0,0 @@ -/* fade */ -.fade-enter-active, -.fade-leave-active { - transition: opacity 0.3s ease-in-out; -} - -.fade-enter-from, -.fade-leave-to { - opacity: 0; -} - -/* fade-slide */ -.fade-slide-leave-active, -.fade-slide-enter-active { - transition: all 0.3s; -} - -.fade-slide-enter-from { - opacity: 0; - transform: translateX(-30px); -} - -.fade-slide-leave-to { - opacity: 0; - transform: translateX(30px); -} - -/* fade-bottom */ -.fade-bottom-enter-active, -.fade-bottom-leave-active { - transition: opacity 0.25s, transform 0.3s; -} - -.fade-bottom-enter-from { - opacity: 0; - transform: translateY(-10%); -} - -.fade-bottom-leave-to { - opacity: 0; - transform: translateY(10%); -} - -/* fade-scale */ -.fade-scale-leave-active, -.fade-scale-enter-active { - transition: all 0.28s; -} - -.fade-scale-enter-from { - opacity: 0; - transform: scale(1.2); -} - -.fade-scale-leave-to { - opacity: 0; - transform: scale(0.8); -} - -/* zoom-fade */ -.zoom-fade-enter-active, -.zoom-fade-leave-active { - transition: transform 0.2s, opacity 0.3s ease-out; -} - -.zoom-fade-enter-from { - opacity: 0; - transform: scale(0.92); -} - -.zoom-fade-leave-to { - opacity: 0; - transform: scale(1.06); -} - -/* zoom-out */ -.zoom-out-enter-active, -.zoom-out-leave-active { - transition: opacity 0.1s ease-in-out, transform 0.15s ease-out; -} - -.zoom-out-enter-from, -.zoom-out-leave-to { - opacity: 0; - transform: scale(0); -} - -/* elementUI 的 Dialog 动画 */ -@keyframes dialog-fade-in { - 0% { - opacity: 0; - transform: translate3d(0, -20px, 0); - } - - 100% { - opacity: 1; - transform: translate3d(0, 0, 0); - } -} - -@keyframes dialog-fade-out { - 0% { - opacity: 1; - transform: translate3d(0, 0, 0); - } - - 100% { - opacity: 0; - transform: translate3d(0, -20px, 0); - } -} - -@keyframes modal-fade-in { - 0% { - opacity: 0; - } - - 100% { - opacity: 1; - } -} - -@keyframes modal-fade-out { - 0% { - opacity: 1; - } - - 100% { - opacity: 0; - } -} - -.el-dialog-enter-active { - animation: dialog-fade-in 0.3s; -} - -.el-dialog-leave-active { - animation: dialog-fade-out 0.3s; -} - -.el-mask-enter-active { - animation: modal-fade-in 0.3s; -} - -.el-mask-leave-active { - animation: modal-fade-out 0.3s; -} - -/* el-fade-bottom */ -.el-fade-enter-active, -.el-fade-leave-active { - transition: all 0.3s; -} - -.el-fade-enter-from { - opacity: 0; - transform: scale(1.1, 0.2); -} - -.el-fade-leave-to { - opacity: 0; - transform: scale(1.1, 0.2); -} diff --git a/frontend-pc/src/styles/editor.scss b/frontend-pc/src/styles/editor.scss deleted file mode 100644 index ce5b384..0000000 --- a/frontend-pc/src/styles/editor.scss +++ /dev/null @@ -1,37 +0,0 @@ -body { - --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; -} - -body[arco-theme='dark'] { - --w-e-textarea-bg-color: var(--color-bg-2); - --w-e-textarea-color: var(--color-text-1); - --w-e-textarea-border-color: var(--color-border); - --w-e-textarea-slight-border-color: var(--color-border-2); - --w-e-textarea-slight-color: var(--color-text-4); - --w-e-textarea-slight-bg-color: var(--color-bg-2); - --w-e-textarea-selected-border-color: var(--color-primary-light-1); - --w-e-textarea-handler-bg-color: var(--color-primary-light-4); - --w-e-toolbar-color: #ccc; - --w-e-toolbar-bg-color: var(--color-bg-2); - --w-e-toolbar-active-color: #fff; - --w-e-toolbar-active-bg-color: var(--color-secondary-active); - --w-e-toolbar-disabled-color: var(--color-text-4); - --w-e-toolbar-border-color: var(--color-border); - --w-e-modal-button-bg-color: var(--color-fill-2); - --w-e-modal-button-border-color: var(--color-border); -} diff --git a/frontend-pc/src/styles/global.scss b/frontend-pc/src/styles/global.scss deleted file mode 100644 index 5314720..0000000 --- a/frontend-pc/src/styles/global.scss +++ /dev/null @@ -1,382 +0,0 @@ -/* 全局样式 */ -@use './var.scss' as *; - -// hover按钮 -.g-hover-btn { - background-color: transparent; - border: 0 !important; - - &:hover { - background: var(--color-secondary-hover) !important; - } - - &:active { - background: var(--color-secondary-active) !important; - } -} - -// 卡片组件,使body的高度自动撑满 -.g-card { - box-sizing: border-box; - display: flex; - flex-direction: column; - overflow: hidden; - - .arco-card-body { - box-sizing: border-box; - display: flex; - flex: 1; - flex-direction: column; - overflow: hidden; - } -} - -// 页面卡片,右侧有返回按钮 -.g-page-card { - box-sizing: border-box; - display: flex; - flex: 1; - flex-direction: column; - margin: var(--margin); - overflow: hidden; - - .arco-card-body { - box-sizing: border-box; - display: flex; - flex: 1; - flex-direction: column; - overflow: hidden; - } -} - -.g-gradient-bg { - background-image: linear-gradient(to right top, transparent, transparent, - color-mix(in oklab, rgb(var(--primary-6)) 12%, transparent), - color-mix(in oklab, rgb(var(--primary-6)) 12%, transparent)); -} - -// 卡片标题,标题左侧的伪类样式 -.g-card-title { - .arco-card-header-title::before { - position: absolute; - top: 50%; - left: 0; - width: 4px; - height: 50%; - content: ''; - background-color: rgb(var(--warning-5)); - border-radius: 0 4px 4px 0; - transform: translateY(-50%); - } -} - -// 使用场景,页面内容超出高度会自动滚动 -.g-page { - box-sizing: border-box; - flex: 1; - padding: var(--margin); - overflow-y: auto; -} - -// 表格类名 高度自适应 -.g-table { - display: flex; - flex: 1; - flex-direction: column; - overflow: hidden; - - >.arco-spin { - height: auto; - max-height: 100%; - overflow: hidden; - } -} - -// 表格类名,高度自适应,分页始终固定在最底部 -.g-full-table { - display: flex; - flex: 1; - flex-direction: column; - overflow: hidden; - - .arco-table-container { - flex: 1; - overflow: hidden; - } - - .arco-table-scroll-y { - border-right: 1px solid var(--color-border-1); - } - - .arco-table-tr .arco-table-td:last-child, - .arco-table-tr .arco-table-th:last-child { - border-right: none; - } - - .arco-table-content { - .arco-scrollbar { - &:last-child { - flex: 1; - - &:not(.arco-scrollbar-both) { - .arco-table-element { - border-bottom: 1px solid var(--color-border-1); - } - } - } - } - } -} - -.g-full-tabs { - display: flex; - flex: 1; - flex-direction: column; - - .arco-tabs-content { - flex: 1; - padding-top: 0; - - .arco-tabs-content-list { - height: 100%; - } - - .arco-tabs-pane { - height: 100%; - overflow-y: auto; - } - } -} - -// 编辑表格,表头的必填红色星号 -.g-column-require { - .arco-table-th-title { - &::before { - padding-right: 2px; - color: red; - content: '*'; - } - } -} - -.g-full-column { - box-sizing: border-box; - display: flex; - flex: 1; - flex-direction: column; - overflow: hidden; -} - -.g-badge-primary { - .arco-badge-number { - background-color: rgb(var(--primary-5)); - } -} - -.g-badge-success { - .arco-badge-number { - background-color: rgb(var(--success-5)); - } -} - -.g-badge-warning { - .arco-badge-number { - background-color: rgb(var(--warning-5)); - } -} - -.g-row-tool { - gap: 8px; - margin-bottom: 12px; -} - -/** - * 列表页卡片(卡片里装着「工具栏 + 表格 + 分页器」)。 - * - * 为什么必须有它:`GiPageLayout` 的内容区是 `overflow: hidden`(页面本身不滚), - * 而一页 10~20 条比屏幕高 —— 不加这层高度链路,**分页器会被裁到可视区之外**: - * DOM 里明明有(`共 1245 条 … 20 条/页`),人却看不见,看着就像"分页器没做/坏了"。 - * 操作日志页(20 条/页)与收集任务页(加了状态 tab 之后)都踩过这一脚。 - * - * 用法:``,表格必须给 `:scroll="{ x: 宽度, y: '100%' }"`—— - * `y` 是字符串时 Arco 会把表头与表体**拆开**(内部 `splitTable`),表体自己滚、表头钉住; - * 不写 y 的话 `.arco-table-content` 的 `overflow-y` 是 hidden(那是横向滚动条专用的), - * 多出来的行会被直接裁掉且无法滚动。 - */ -.g-list-card { - display: flex; - flex: 1; - flex-direction: column; - min-height: 0; - - .arco-card-body { - display: flex; - flex: 1; - flex-direction: column; - min-height: 0; - overflow: hidden; - } - - // ⚠️ `.arco-spin` 必须在链路里:Arco 用 Spin 包住「表体 + 分页器」 - .arco-table, - .arco-spin { - display: flex; - flex: 1; - flex-direction: column; - min-height: 0; - } - - .arco-table-container { - flex: 1; - min-height: 0; - } - - // 分页器不参与伸缩,永远留在卡片底部 - .arco-table-pagination { - flex: none; - } -} - -// 以下这些类名要放到下面,涉及到css权重优先级问题 -.g-mt { - margin-top: var(--margin); -} - -.g-mb { - margin-bottom: var(--margin); -} - -.g-ml { - margin-left: var(--margin); -} - -.g-mr { - margin-right: var(--margin); -} - -.g-mx { - margin-right: var(--margin); - margin-left: var(--margin); -} - -.g-my { - margin-top: var(--margin); - margin-bottom: var(--margin); -} - -.g-m0 { - margin: 0; -} - -.g-pt { - padding-top: var(--margin); -} - -.g-pb { - padding-bottom: var(--margin); -} - -.g-pl { - padding-left: var(--margin); -} - -.g-pr { - padding-right: var(--margin); -} - -.g-px { - padding-right: var(--padding); - padding-left: var(--padding); -} - -.g-py { - padding-top: var(--padding); - padding-bottom: var(--padding); -} - -.g-p0 { - padding: 0; -} - -.g-round { - border-radius: 100px; -} - -.w-full { - width: 100%; -} - -.h-full { - height: 100%; -} - -.overflow-hidden { - overflow: hidden; -} - -.flex-1 { - flex: 1; -} - -// 一般结合使用 -.flex-column { - flex-direction: column; - overflow: hidden; -} - -// 通用外边距 -.g-margin { - margin: var(--margin); -} - -// 通用内边距 -.g-padding { - padding: var(--padding); -} - -.g-select-input { - flex-shrink: 0; - width: 100px; -} - -.g-search-input { - flex-shrink: 0; - width: 250px; -} - -.g-line-1 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.g-line-2 { - -webkit-line-clamp: 2; - line-clamp: 2; -} - -.g-line-3 { - -webkit-line-clamp: 3; - line-clamp: 3; -} - -.g-line-4 { - -webkit-line-clamp: 4; - line-clamp: 4; -} - -.g-line-5 { - -webkit-line-clamp: 5; - line-clamp: 5; -} - -.g-line-2, -.g-line-3, -.g-line-4, -.g-line-5 { - display: -webkit-box; /* 弹性伸缩盒 */ - overflow: hidden; - text-overflow: ellipsis; - word-break: break-all; - -webkit-box-orient: vertical; /* 设置伸缩盒子元素排列方式 */ -} \ No newline at end of file diff --git a/frontend-pc/src/styles/index.scss b/frontend-pc/src/styles/index.scss index 8e0471c..2969a5b 100644 --- a/frontend-pc/src/styles/index.scss +++ b/frontend-pc/src/styles/index.scss @@ -1,17 +1,249 @@ -// 基础样式 -@use './base.scss'; +/** + * 全局样式(只放**跨页面**的东西)。 + * + * 组件的具体样式写在各自 SFC 的 diff --git a/frontend-pc/src/views/audit/AuditRejectModal.vue b/frontend-pc/src/views/audit/AuditRejectModal.vue index 0bb09c0..6893727 100644 --- a/frontend-pc/src/views/audit/AuditRejectModal.vue +++ b/frontend-pc/src/views/audit/AuditRejectModal.vue @@ -1,65 +1,32 @@ - - - diff --git a/frontend-pc/src/views/audit/FilePreview.vue b/frontend-pc/src/views/audit/FilePreview.vue new file mode 100644 index 0000000..4f3ee13 --- /dev/null +++ b/frontend-pc/src/views/audit/FilePreview.vue @@ -0,0 +1,304 @@ + + + + + diff --git a/frontend-pc/src/views/audit/FilePreviewImage.vue b/frontend-pc/src/views/audit/FilePreviewImage.vue deleted file mode 100644 index 378b5a4..0000000 --- a/frontend-pc/src/views/audit/FilePreviewImage.vue +++ /dev/null @@ -1,115 +0,0 @@ - - - - - diff --git a/frontend-pc/src/views/audit/index.vue b/frontend-pc/src/views/audit/index.vue index 578a0df..ee67f34 100644 --- a/frontend-pc/src/views/audit/index.vue +++ b/frontend-pc/src/views/audit/index.vue @@ -1,170 +1,21 @@ - - - diff --git a/frontend-pc/src/views/cleanup/index.vue b/frontend-pc/src/views/cleanup/index.vue deleted file mode 100644 index 4f1f37a..0000000 --- a/frontend-pc/src/views/cleanup/index.vue +++ /dev/null @@ -1,693 +0,0 @@ - - - - - diff --git a/frontend-pc/src/views/coming-soon/index.vue b/frontend-pc/src/views/coming-soon/index.vue deleted file mode 100644 index e5de047..0000000 --- a/frontend-pc/src/views/coming-soon/index.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - - - diff --git a/frontend-pc/src/views/dashboard/index.vue b/frontend-pc/src/views/dashboard/index.vue index 31208ac..b16e7d0 100644 --- a/frontend-pc/src/views/dashboard/index.vue +++ b/frontend-pc/src/views/dashboard/index.vue @@ -1,333 +1,155 @@ - - - diff --git a/frontend-pc/src/views/dict/index.vue b/frontend-pc/src/views/dict/index.vue index 34d8c1f..6b6e490 100644 --- a/frontend-pc/src/views/dict/index.vue +++ b/frontend-pc/src/views/dict/index.vue @@ -1,445 +1,628 @@ - - - diff --git a/frontend-pc/src/views/error/403.vue b/frontend-pc/src/views/error/403.vue index ef06b46..3bc5f83 100644 --- a/frontend-pc/src/views/error/403.vue +++ b/frontend-pc/src/views/error/403.vue @@ -1,11 +1,10 @@ - - - + diff --git a/frontend-pc/src/views/error/404.vue b/frontend-pc/src/views/error/404.vue index 3233907..db68d2b 100644 --- a/frontend-pc/src/views/error/404.vue +++ b/frontend-pc/src/views/error/404.vue @@ -1,11 +1,10 @@ - - - + diff --git a/frontend-pc/src/views/error/500.vue b/frontend-pc/src/views/error/500.vue deleted file mode 100644 index 53ca351..0000000 --- a/frontend-pc/src/views/error/500.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - - - diff --git a/frontend-pc/src/views/error/components/ErrorPage.vue b/frontend-pc/src/views/error/components/ErrorPage.vue index 1556e9d..40feafb 100644 --- a/frontend-pc/src/views/error/components/ErrorPage.vue +++ b/frontend-pc/src/views/error/components/ErrorPage.vue @@ -1,48 +1,52 @@ - - - diff --git a/frontend-pc/src/views/export/ExportDetailDrawer.vue b/frontend-pc/src/views/export/ExportDetailDrawer.vue index 7c3bced..779e029 100644 --- a/frontend-pc/src/views/export/ExportDetailDrawer.vue +++ b/frontend-pc/src/views/export/ExportDetailDrawer.vue @@ -1,83 +1,23 @@ - - - diff --git a/frontend-pc/src/views/export/index.vue b/frontend-pc/src/views/export/index.vue index 2f78bcd..d853686 100644 --- a/frontend-pc/src/views/export/index.vue +++ b/frontend-pc/src/views/export/index.vue @@ -1,244 +1,26 @@ - - - diff --git a/frontend-pc/src/views/join/JoinAuditModal.vue b/frontend-pc/src/views/join/JoinAuditModal.vue deleted file mode 100644 index 7a731c3..0000000 --- a/frontend-pc/src/views/join/JoinAuditModal.vue +++ /dev/null @@ -1,253 +0,0 @@ - - - - - diff --git a/frontend-pc/src/views/join/index.vue b/frontend-pc/src/views/join/index.vue deleted file mode 100644 index 42b2eb3..0000000 --- a/frontend-pc/src/views/join/index.vue +++ /dev/null @@ -1,272 +0,0 @@ - - - - - diff --git a/frontend-pc/src/views/log/index.vue b/frontend-pc/src/views/log/index.vue index 6a3d285..19e0bbd 100644 --- a/frontend-pc/src/views/log/index.vue +++ b/frontend-pc/src/views/log/index.vue @@ -1,132 +1,17 @@ - - - diff --git a/frontend-pc/src/views/login/LoginLeft.vue b/frontend-pc/src/views/login/LoginLeft.vue deleted file mode 100644 index 3d73acf..0000000 --- a/frontend-pc/src/views/login/LoginLeft.vue +++ /dev/null @@ -1,285 +0,0 @@ - - - - - diff --git a/frontend-pc/src/views/login/LoginPalette.vue b/frontend-pc/src/views/login/LoginPalette.vue deleted file mode 100644 index 4677b80..0000000 --- a/frontend-pc/src/views/login/LoginPalette.vue +++ /dev/null @@ -1,100 +0,0 @@ - - - - - diff --git a/frontend-pc/src/views/login/index.vue b/frontend-pc/src/views/login/index.vue index 9e2970e..007940f 100644 --- a/frontend-pc/src/views/login/index.vue +++ b/frontend-pc/src/views/login/index.vue @@ -1,133 +1,41 @@ - - - diff --git a/frontend-pc/src/views/notify/index.vue b/frontend-pc/src/views/notify/index.vue index a1481a6..ef96ee5 100644 --- a/frontend-pc/src/views/notify/index.vue +++ b/frontend-pc/src/views/notify/index.vue @@ -1,98 +1,17 @@ - - - diff --git a/frontend-pc/src/views/org/OrgFormModal.vue b/frontend-pc/src/views/org/OrgFormModal.vue index 8e58161..2e9fbaf 100644 --- a/frontend-pc/src/views/org/OrgFormModal.vue +++ b/frontend-pc/src/views/org/OrgFormModal.vue @@ -1,60 +1,21 @@ - - - diff --git a/frontend-pc/src/views/org/index.vue b/frontend-pc/src/views/org/index.vue index ba0005c..37398b6 100644 --- a/frontend-pc/src/views/org/index.vue +++ b/frontend-pc/src/views/org/index.vue @@ -1,215 +1,169 @@ - - - diff --git a/frontend-pc/src/views/redirect/index.vue b/frontend-pc/src/views/redirect/index.vue deleted file mode 100644 index a475ae3..0000000 --- a/frontend-pc/src/views/redirect/index.vue +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/frontend-pc/src/views/role/RoleFormModal.vue b/frontend-pc/src/views/role/RoleFormModal.vue new file mode 100644 index 0000000..b04e7be --- /dev/null +++ b/frontend-pc/src/views/role/RoleFormModal.vue @@ -0,0 +1,270 @@ + + + + + diff --git a/frontend-pc/src/views/role/RoleGrantEditor.vue b/frontend-pc/src/views/role/RoleGrantEditor.vue new file mode 100644 index 0000000..1f3b63b --- /dev/null +++ b/frontend-pc/src/views/role/RoleGrantEditor.vue @@ -0,0 +1,294 @@ + + + + + diff --git a/frontend-pc/src/views/role/index.vue b/frontend-pc/src/views/role/index.vue new file mode 100644 index 0000000..dba7711 --- /dev/null +++ b/frontend-pc/src/views/role/index.vue @@ -0,0 +1,472 @@ + + + + + diff --git a/frontend-pc/src/views/task/TaskFormModal.vue b/frontend-pc/src/views/task/TaskFormModal.vue index cda6635..99982e1 100644 --- a/frontend-pc/src/views/task/TaskFormModal.vue +++ b/frontend-pc/src/views/task/TaskFormModal.vue @@ -1,132 +1,48 @@ - - + + diff --git a/frontend-pc/src/views/task/TaskMaterialDrawer.vue b/frontend-pc/src/views/task/TaskMaterialDrawer.vue index 2439041..e8ff8c5 100644 --- a/frontend-pc/src/views/task/TaskMaterialDrawer.vue +++ b/frontend-pc/src/views/task/TaskMaterialDrawer.vue @@ -1,295 +1,4 @@ - - + + diff --git a/frontend-pc/src/views/task/TaskOrgDrawer.vue b/frontend-pc/src/views/task/TaskOrgDrawer.vue index a8ba97b..c71e7ad 100644 --- a/frontend-pc/src/views/task/TaskOrgDrawer.vue +++ b/frontend-pc/src/views/task/TaskOrgDrawer.vue @@ -1,118 +1,64 @@ - - + + diff --git a/frontend-pc/src/views/task/TaskRosterDrawer.vue b/frontend-pc/src/views/task/TaskRosterDrawer.vue index 9547430..0efbf92 100644 --- a/frontend-pc/src/views/task/TaskRosterDrawer.vue +++ b/frontend-pc/src/views/task/TaskRosterDrawer.vue @@ -1,468 +1,6 @@ - - + + diff --git a/frontend-pc/src/views/tenant/index.vue b/frontend-pc/src/views/tenant/index.vue index 48a2a69..46e40b2 100644 --- a/frontend-pc/src/views/tenant/index.vue +++ b/frontend-pc/src/views/tenant/index.vue @@ -1,236 +1,112 @@ - - - diff --git a/frontend-pc/src/views/user/UserFormModal.vue b/frontend-pc/src/views/user/UserFormModal.vue index a5fa1e6..a53103d 100644 --- a/frontend-pc/src/views/user/UserFormModal.vue +++ b/frontend-pc/src/views/user/UserFormModal.vue @@ -1,159 +1,46 @@ - - - diff --git a/frontend-pc/src/views/user/UserImportModal.vue b/frontend-pc/src/views/user/UserImportModal.vue index 90927ce..d76986f 100644 --- a/frontend-pc/src/views/user/UserImportModal.vue +++ b/frontend-pc/src/views/user/UserImportModal.vue @@ -1,182 +1,38 @@ - - - diff --git a/frontend-pc/src/views/user/index.vue b/frontend-pc/src/views/user/index.vue index 1156a94..5ae0239 100644 --- a/frontend-pc/src/views/user/index.vue +++ b/frontend-pc/src/views/user/index.vue @@ -1,255 +1,80 @@ - - - diff --git a/frontend-pc/stylelint.config.js b/frontend-pc/stylelint.config.js deleted file mode 100644 index b73dd56..0000000 --- a/frontend-pc/stylelint.config.js +++ /dev/null @@ -1,44 +0,0 @@ -export default { - ignoreFiles: [ - 'node_modules/**/*', - 'dist/**/*', - 'public/**/*' - ], - plugins: ['stylelint-scss'], - extends: [ - 'stylelint-config-standard', // 基础 CSS 规则 - 'stylelint-config-standard-scss', // SCSS 推荐规则 - 'stylelint-config-recommended-vue/scss', // Vue + SCSS 推荐规则(自动适配 scoped) - 'stylelint-config-recess-order' // 配置 stylelint css 属性书写顺序插件 - ], - rules: { - // 禁用导致错误的规则 - 'scss/double-slash-comment-empty-line-before': null, // 禁止在双斜杠注释前有空行 - 'scss/operator-no-unspaced': null, // 运算符周围需要空格 - 'scss/load-partial-extension': null, // 禁止在@use指令中使用.scss扩展名 - 'scss/double-slash-comment-whitespace-inside': null, // 双斜杠注释内部需要空格 - 'scss/dollar-variable-empty-line-before': null, // $变量前需要空行 - 'scss/at-rule-conditional-no-parentheses': null, // @规则条件不使用括号 - 'no-descending-specificity': null, // 禁止选择器优先级降低 - 'declaration-block-no-duplicate-properties': null, // 禁止声明块中重复的属性 - 'keyframes-name-pattern': null, // 限制keyframes名称格式 - 'number-max-precision': null, // 禁用数字精度限制规则 - 'import-notation': null, // 导入语法格式 - 'font-family-no-missing-generic-family-keyword': null, // 字体族必须包含通用字体族 - 'custom-property-empty-line-before': null, // 自定义属性前空行规则 - 'declaration-empty-line-before': null, // 声明前空行规则 - // 其他基础规则 - 'no-empty-source': null, // 禁止空源码 - 'selector-class-pattern': null // 强制选择器类名的格式 - }, - overrides: [ - { - files: ['**/*.vue'], - customSyntax: 'postcss-html' - }, - { - files: ['**/*.scss'], - customSyntax: 'postcss-scss' - } - ] -} diff --git a/frontend-pc/tsconfig.json b/frontend-pc/tsconfig.json index 36a1f41..636ae68 100644 --- a/frontend-pc/tsconfig.json +++ b/frontend-pc/tsconfig.json @@ -1,42 +1,31 @@ { - "extends": "@vue/tsconfig/tsconfig.json", + "extends": "@vue/tsconfig/tsconfig.dom.json", "compilerOptions": { - "target": "ES2020", // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT' - "jsx": "preserve", // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react' - "jsxFactory": "h", // 当使用经典的JSX运行时,更改.js文件中调用的函数,默认:React.createElement - "jsxFragmentFactory": "Fragment", // 指定 JSX 片段工厂函数 - "lib": [ - "ES2022", - "DOM" - ], // 指定要包含在编译中的库文件。必须含 ES2022 —— - // gi-demo 的 useBreakpoint / useTabsStore 用了 Array.prototype.at(), - // 停在 ES2020 会让 `pnpm typecheck` 报 TS2550(运行期是好的:Vite 按浏览器实际能力打包) - "baseUrl": ".", // 用于解析非相对模块名称的基目录 - "module": "ES2020", // + "target": "ESNext", + "jsx": "preserve", + "baseUrl": ".", + "module": "ESNext", "moduleResolution": "bundler", "paths": { - "@/*": [ - "src/*" - ] // 模块名到基于 baseUrl 的路径映射的列表 + "@/*": ["src/*"], + "~/*": ["./*"] }, - "resolveJsonModule": true, // 是否解析 JSON 文件 - "allowJs": true, // 允许编译 javascript 文件 - "checkJs": true, // 报告 javascript 文件中的错误 - "strict": true, // 开启所有严格的类型检查 - "noImplicitAny": false, - "sourceMap": true, // 是否生成相应的Map映射的文件,默认:false - "esModuleInterop": true, // 是否通过为所有导入模块创建命名空间对象,允许CommonJS和ES模块之间的互操作性 - "skipLibCheck": true, // 是否跳过声明文件的类型检查 - "ignoreDeprecations": "5.0" // 消除 importsNotUsedAsValues 的弃用报错 + "resolveJsonModule": true, + "types": ["vite/client", "node"], + "strict": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "esModuleInterop": true, + "isolatedModules": true, + "skipLibCheck": true }, "include": [ - "src/**/*", + "src/**/*.ts", + "src/**/*.d.ts", + "src/**/*.tsx", "src/**/*.vue", "vite.config.ts" - ], // 指定被编译文件所在的目录 - "exclude": [ - "node_modules", - "dist", - "**/*.js" - ] // 指定不需要被编译的目录 + ], + "exclude": ["node_modules", "dist"] } diff --git a/frontend-pc/vite.config.ts b/frontend-pc/vite.config.ts index c611e94..d4a4a10 100644 --- a/frontend-pc/vite.config.ts +++ b/frontend-pc/vite.config.ts @@ -1,115 +1,41 @@ import { fileURLToPath, URL } from 'node:url' import vue from '@vitejs/plugin-vue' -import vueJsx from '@vitejs/plugin-vue-jsx' -import AutoImport from 'unplugin-auto-import/vite' -import IconsResolver from 'unplugin-icons/resolver' -import Icons from 'unplugin-icons/vite' -import { ArcoResolver } from 'unplugin-vue-components/resolvers' -import Components from 'unplugin-vue-components/vite' import { defineConfig, loadEnv } from 'vite' -import vitePluginCompression from 'vite-plugin-compression' -import VueDevTools from 'vite-plugin-vue-devtools' +/** + * 管理端(Vue 3 + Naive UI)构建配置。 + * + * ⚠️ 与上一版(Gi Admin Pro + Arco)的差别:**没有** unplugin-auto-import / + * unplugin-vue-components / Arco 解析器。理由有两个: + * 1. 组件与 API 全部**显式 import** —— vue-tsc 看到的就是真的, + * 不依赖"生成但不一定提交/更新"的 `components.d.ts`; + * 2. 少一层构建期魔法:出问题时读代码就能定位。 + */ export default defineConfig(({ mode }) => { - const env = loadEnv(mode, process.cwd()) as ImportMetaEnv + const env = loadEnv(mode, process.cwd()) return { - // 开发或生产环境服务的公共基础路径 - base: env.VITE_BASE, - // 路径别名 + base: env.VITE_BASE || '/', resolve: { alias: { '~': fileURLToPath(new URL('./', import.meta.url)), '@': fileURLToPath(new URL('./src', import.meta.url)) - }, - // 确保正确解析模块 - dedupe: ['vue', '@arco-design/web-vue'] - }, - // 依赖优化配置 - optimizeDeps: { - include: ['@arco-design/color'] - }, - // 引入scss全局样式变量 - css: { - preprocessorOptions: { - scss: { - additionalData: `@use "@/styles/var.scss" as *;` - } } }, server: { - // 服务启动时是否自动打开浏览器 - open: false, - // 本地跨域代理 -> 代理到服务器的接口地址 + port: 5173, proxy: { + // 本地开发:/api → Spring Boot 8080(本仓库没有 Mock,见 AGENTS.md) '/api': { - target: env.VITE_API_BASE_URL, // 后台服务器地址 - changeOrigin: true, // 是否允许不同源 - secure: false, // 支持https - rewrite: (path) => path.replace(/^\/api/, '/api') + target: env.VITE_API_BASE_URL, + changeOrigin: true } } }, - plugins: [ - vue(), - vueJsx(), - VueDevTools(), - AutoImport({ - // 自动导入vue相关函数,如: ref、reactive、toRef等 - imports: ['vue', 'vue-router', { - // vue 3.5.x - vue: ['useTemplateRef', 'onWatcherCleanup', 'useId'] - }], - dts: 'src/auto-import.d.ts' - }), - Components({ - // 指定组件位置,默认是 src/components 自动导入自定义组件 - dirs: ['src/components/Gi*'], - extensions: ['vue', 'tsx'], - // 配置文件生成位置 - dts: 'src/components.d.ts', - // Arco Design Vue 按需导入配置(使用官方推荐的 ArcoResolver) - resolvers: [ - ArcoResolver({ - sideEffect: false - }), - // Iconify 图标按需导入 - IconsResolver({ - prefix: 'icon', - enabledCollections: ['icon-park-outline'] - }) - ] - }), - // Iconify 图标插件配置 - Icons({ - compiler: 'vue3', - autoInstall: true, - defaultClass: 'iconify-icon', - defaultStyle: 'vertical-align: middle;' - }), - // Gzip 压缩配置 - vitePluginCompression({ - verbose: true, // 是否在控制台输出压缩结果 - disable: false, // 是否禁用压缩 - deleteOriginFile: false, // 压缩后是否删除原文件 - threshold: 10240, // 压缩阈值,单位:字节,超过此大小的文件才会被压缩(10KB) - algorithm: 'gzip', // 压缩算法,可选 'gzip' | 'brotliCompress' | 'deflate' | 'deflateRaw' - ext: '.gz', // 压缩文件扩展名 - filter: /\.(js|mjs|json|css|html|svg|ico|eot|otf|ttf|ttc|woff|woff2)$/i // 需要压缩的文件类型 - }) - ], - // 构建 build: { - chunkSizeWarningLimit: 2000 + outDir: 'dist', + chunkSizeWarningLimit: 1500 }, - // 生产环境优化 - esbuild: - mode === 'development' - ? undefined - : { - pure: ['console.log'], - drop: ['debugger'], - legalComments: 'none' - } + plugins: [vue()] } }) diff --git a/scripts/extract-pc-icons.py b/scripts/extract-pc-icons.py new file mode 100644 index 0000000..af5324c --- /dev/null +++ b/scripts/extract-pc-icons.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +"""从 @iconify-json/icon-park-outline 里抽出**本项目实际用到的那几十个图标**, +生成 `frontend-pc/src/icons/icon-data.ts`。 + +为什么不直接依赖 `@iconify/vue` + 在线 API: + 1. 这套系统要能**内网离线部署**,运行时去 iconify 拉图标会白屏; + 2. 整包 `icons.json` 有 4MB+,为了 20 个图标打进产物不划算。 + +所以:构建前跑一次本脚本(图标集升级时重跑),产物是一个纯数据的 TS 文件。 +""" +from __future__ import annotations + +import json +import os + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +SRC = os.path.join(ROOT, "frontend-pc", "node_modules", + "@iconify-json", "icon-park-outline", "icons.json") +OUT = os.path.join(ROOT, "frontend-pc", "src", "icons", "icon-data.ts") + +# 键 = 前端用的短名;值 = iconify 集合里的图标名 +WANTED = { + # ── 菜单与分组 ───────────────────────────── + "dashboard": "dashboard-one", + "collect": "application-two", + "setting": "setting-two", + "org-group": "every-user", + "platform": "server", + "task": "list-two", + "audit": "check-one", + "export": "download-two", + "notify": "remind", + "dict": "list-two", + "org": "tree-list", + "user": "peoples", + "role": "permissions", + "tenant": "school", + "log": "log", + # ── 通用动作 ─────────────────────────────── + "fold": "menu-fold-one", + "unfold": "menu-unfold-one", + "account": "user", + "logout": "logout", + "refresh": "refresh", + "plus": "plus", + "search": "search", + "close": "close", + "check": "check", + "warning": "caution", + "info": "info", + "download": "download", + "upload": "upload", + "edit": "edit", + "delete": "delete", + "more": "more", + "arrow-down": "down", + "file": "file-text", + "photo": "picture-one", + "idcard": "id-card", + "school-roll": "book-one", + "work-proof": "doc-detail", +} + + +def main() -> int: + if not os.path.exists(SRC): + print(f"找不到图标集:{SRC}\n(先 cd frontend-pc && pnpm install)") + return 1 + + with open(SRC, encoding="utf-8") as f: + data = json.load(f) + icons = data.get("icons") or {} + aliases = data.get("aliases") or {} + + out: dict[str, dict] = {} + missing: list[str] = [] + for short, name in WANTED.items(): + node = icons.get(name) + if node is None and name in aliases: + parent = aliases[name].get("parent") + node = icons.get(parent) if parent else None + if node is None: + missing.append(name) + continue + out[short] = { + "body": node["body"], + "width": node.get("width", data.get("width", 48)), + "height": node.get("height", data.get("height", 48)), + } + + os.makedirs(os.path.dirname(OUT), exist_ok=True) + lines = [ + "/**", + " * 菜单与通用图标的数据(由 scripts/extract-pc-icons.py 生成,**不要手改**)。", + " *", + f" * 来源:@iconify-json/icon-park-outline({data.get('info', {}).get('name', '?')}),", + f" * 只抽了本项目实际用到的 {len(out)} 个,避免把 4MB 的整包打进来。", + " */", + "", + "export interface IconData {", + " body: string", + " width: number", + " height: number", + "}", + "", + "export const ICONS: Record = {", + ] + for short in WANTED: + if short not in out: + continue + node = out[short] + body = node["body"].replace("\\", "\\\\").replace("'", "\\'") + lines.append(f" '{short}': {{") + lines.append(f" body: '{body}',") + lines.append(f" width: {node['width']},") + lines.append(f" height: {node['height']}") + lines.append(" },") + lines.append("}") + lines.append("") + + with open(OUT, "w", encoding="utf-8", newline="\n") as f: + f.write("\n".join(lines)) + + print(f"已生成 {os.path.relpath(OUT, ROOT)}:{len(out)} 个图标") + if missing: + print("⚠️ 集合里没有这些图标(已跳过,请改名或换一个):") + for m in missing: + print(f" - {m}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())