chore: 递增版本号并标记停更版本

- 版本号: 中段递增、末段归零

- README: laravel 与 spring-boot-3 更新状态改为 ❌️
This commit is contained in:
Ray.Hao
2026-09-25 19:56:52 +08:00
parent dc1436e3e5
commit 3ccbdb7c06
13 changed files with 1235 additions and 687 deletions

View File

@@ -129,10 +129,10 @@ npx skills add https://github.com/youlaitech/youlai-skills --skill vue-admin
| [youlai-gin](https://gitee.com/youlaiorg/youlai-gin) | Go + Gorm | Go | ✅️ |
| [youlai-django](https://gitee.com/youlaiorg/youlai-django) | Django + DRF | Python | ✅️ |
| [youlai-fastapi](https://gitee.com/youlaiorg/youlai-fastapi) | FastAPI + SQLAlchemy | Python | ✅️ |
| [youlai-laravel](https://gitee.com/youlaiorg/youlai-laravel) | Laravel + Eloquent | PHP | ✅️ |
| [youlai-think](https://gitee.com/youlaiorg/youlai-think) | ThinkPHP + ThinkORM | PHP | ✅️ |
| [youlai-aspnet](https://gitee.com/youlaiorg/youlai-aspnet) | ASP.NET Core + EF Core | C# | ✅️ |
| [youlai-axum](https://gitee.com/youlaiorg/youlai-axum) | Axum + SeaORM | Rust | ✅️ |
| [youlai-laravel](https://gitee.com/youlaiorg/youlai-laravel) | Laravel + Eloquent | PHP | ❌️ |
> All nine backends share the same **RESTful API** and **database schema**, frontends can switch seamlessly.
@@ -145,7 +145,7 @@ npx skills add https://github.com/youlaitech/youlai-skills --skill vue-admin
| [youlai-boot-flex](https://gitee.com/youlaiorg/youlai-boot-flex) | youlai-boot | Repo | Switched to MyBatis-Flex | ✅️ |
| [youlai-boot (db-pg)](https://gitee.com/youlaiorg/youlai-boot/tree/db-pg) | youlai-boot | Branch | PostgreSQL database branch | ✅️ |
| [youlai-boot (multi-module)](https://gitee.com/youlaiorg/youlai-boot/tree/multi-module) | youlai-boot | Branch | Multi-module project split | ✅️ |
| [youlai-boot (spring-boot-3)](https://gitee.com/youlaiorg/youlai-boot/tree/spring-boot-3) | youlai-boot | Branch | Spring Boot 3 compatible branch | ✅️ |
| [youlai-boot (spring-boot-3)](https://gitee.com/youlaiorg/youlai-boot/tree/spring-boot-3) | youlai-boot | Branch | Spring Boot 3 compatible branch | ❌️ |
## Development Guide

View File

@@ -131,10 +131,10 @@ npx skills add https://github.com/youlaitech/youlai-skills --skill vue-admin
| [youlai-gin](https://gitee.com/youlaiorg/youlai-gin) | Go + Gorm | Go | ✅️ |
| [youlai-django](https://gitee.com/youlaiorg/youlai-django) | Django + DRF | Python | ✅️ |
| [youlai-fastapi](https://gitee.com/youlaiorg/youlai-fastapi) | FastAPI + SQLAlchemy | Python | ✅️ |
| [youlai-laravel](https://gitee.com/youlaiorg/youlai-laravel) | Laravel + Eloquent | PHP | ✅️ |
| [youlai-think](https://gitee.com/youlaiorg/youlai-think) | ThinkPHP + ThinkORM | PHP | ✅️ |
| [youlai-aspnet](https://gitee.com/youlaiorg/youlai-aspnet) | ASP.NET Core + EF Core | C# | ✅️ |
| [youlai-axum](https://gitee.com/youlaiorg/youlai-axum) | Axum + SeaORM | Rust | ✅️ |
| [youlai-laravel](https://gitee.com/youlaiorg/youlai-laravel) | Laravel + Eloquent | PHP | ❌️ |
> 九种后端共享同一套 **RESTful API 规范** 和 **数据库结构**,前端可无缝切换。
@@ -147,7 +147,7 @@ npx skills add https://github.com/youlaitech/youlai-skills --skill vue-admin
| [youlai-boot-flex](https://gitee.com/youlaiorg/youlai-boot-flex) | youlai-boot | 独立仓库 | 改用 MyBatis-Flex | ✅️ |
| [youlai-boot (db-pg)](https://gitee.com/youlaiorg/youlai-boot/tree/db-pg) | youlai-boot | 分支 | PostgreSQL 数据库分支 | ✅️ |
| [youlai-boot (multi-module)](https://gitee.com/youlaiorg/youlai-boot/tree/multi-module) | youlai-boot | 分支 | 多模块工程拆分 | ✅️ |
| [youlai-boot (spring-boot-3)](https://gitee.com/youlaiorg/youlai-boot/tree/spring-boot-3) | youlai-boot | 分支 | Spring Boot 3 兼容分支 | ✅️ |
| [youlai-boot (spring-boot-3)](https://gitee.com/youlaiorg/youlai-boot/tree/spring-boot-3) | youlai-boot | 分支 | Spring Boot 3 兼容分支 | ❌️ |
## 开发指南

View File

@@ -577,7 +577,7 @@ export default defineMock([
id: "105",
parentId: "2",
name: "用户查询",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -592,7 +592,7 @@ export default defineMock([
id: "31",
parentId: "2",
name: "用户新增",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -607,7 +607,7 @@ export default defineMock([
id: "32",
parentId: "2",
name: "用户编辑",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -622,7 +622,7 @@ export default defineMock([
id: "33",
parentId: "2",
name: "用户删除",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -637,7 +637,7 @@ export default defineMock([
id: "88",
parentId: "2",
name: "重置密码",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -652,7 +652,7 @@ export default defineMock([
id: "106",
parentId: "2",
name: "用户导入",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -667,7 +667,7 @@ export default defineMock([
id: "107",
parentId: "2",
name: "用户导出",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -698,7 +698,7 @@ export default defineMock([
id: "70",
parentId: "3",
name: "角色新增",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -713,7 +713,7 @@ export default defineMock([
id: "71",
parentId: "3",
name: "角色编辑",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -728,7 +728,7 @@ export default defineMock([
id: "72",
parentId: "3",
name: "角色删除",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -759,7 +759,7 @@ export default defineMock([
id: "73",
parentId: "4",
name: "菜单新增",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -774,7 +774,7 @@ export default defineMock([
id: "74",
parentId: "4",
name: "菜单编辑",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -789,7 +789,7 @@ export default defineMock([
id: "75",
parentId: "4",
name: "菜单删除",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -820,7 +820,7 @@ export default defineMock([
id: "76",
parentId: "5",
name: "部门新增",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -835,7 +835,7 @@ export default defineMock([
id: "77",
parentId: "5",
name: "部门编辑",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -850,7 +850,7 @@ export default defineMock([
id: "78",
parentId: "5",
name: "部门删除",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -881,7 +881,7 @@ export default defineMock([
id: "79",
parentId: "6",
name: "字典新增",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -896,7 +896,7 @@ export default defineMock([
id: "81",
parentId: "6",
name: "字典编辑",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -911,7 +911,7 @@ export default defineMock([
id: "84",
parentId: "6",
name: "字典删除",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -942,7 +942,7 @@ export default defineMock([
id: "136",
parentId: "135",
name: "字典项新增",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -957,7 +957,7 @@ export default defineMock([
id: "137",
parentId: "135",
name: "字典项编辑",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -972,7 +972,7 @@ export default defineMock([
id: "138",
parentId: "135",
name: "字典项删除",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -1018,7 +1018,7 @@ export default defineMock([
id: "121",
parentId: "120",
name: "查询系统配置",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -1033,7 +1033,7 @@ export default defineMock([
id: "122",
parentId: "120",
name: "新增系统配置",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -1048,7 +1048,7 @@ export default defineMock([
id: "123",
parentId: "120",
name: "修改系统配置",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -1063,7 +1063,7 @@ export default defineMock([
id: "124",
parentId: "120",
name: "删除系统配置",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -1078,7 +1078,7 @@ export default defineMock([
id: "125",
parentId: "120",
name: "刷新系统配置",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -1109,7 +1109,7 @@ export default defineMock([
id: "127",
parentId: "126",
name: "查询",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -1124,7 +1124,7 @@ export default defineMock([
id: "128",
parentId: "126",
name: "新增",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -1139,7 +1139,7 @@ export default defineMock([
id: "129",
parentId: "126",
name: "编辑",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -1154,7 +1154,7 @@ export default defineMock([
id: "130",
parentId: "126",
name: "删除",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -1169,7 +1169,7 @@ export default defineMock([
id: "133",
parentId: "126",
name: "发布",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,
@@ -1184,7 +1184,7 @@ export default defineMock([
id: "134",
parentId: "126",
name: "撤回",
type: "F",
type: "B",
routeName: null,
routePath: "",
component: null,

View File

@@ -29,10 +29,10 @@ function handleClipboard() {
// 使用 Clipboard API
navigator.clipboard.writeText(props.text).then(
() => {
ElMessage.success("Copy successfully");
ElMessage.success("复制成功");
},
() => {
ElMessage.warning("Copy failed");
ElMessage.warning("复制失败");
}
);
} else {
@@ -47,9 +47,9 @@ function handleClipboard() {
const successful = document.execCommand("copy");
if (successful) {
ElMessage.success("Copy successfully!");
ElMessage.success("复制成功");
} else {
ElMessage.warning("Copy failed!");
ElMessage.warning("复制失败");
}
} finally {
document.body.removeChild(input);

View File

@@ -0,0 +1,49 @@
<template>
<div class="section-title">
<span>{{ title }}</span>
<span v-if="tip" class="section-title__tip">{{ tip }}</span>
</div>
</template>
<script setup lang="ts">
/**
* 分区标题
*
* 表单中划分区块的小标题:竖线 + 标题,标题右侧可跟说明文字。
*/
defineOptions({ name: "SectionTitle" });
defineProps<{
/** 分区标题 */
title: string;
/** 标题右侧说明 */
tip?: string;
}>();
</script>
<style scoped lang="scss">
.section-title {
display: flex;
gap: 6px;
align-items: center;
margin: 2px 0 14px;
font-size: 13px;
font-weight: 600;
color: var(--el-text-color-primary);
&::before {
flex: none;
width: 3px;
height: 12px;
content: "";
background: var(--el-color-primary);
border-radius: 2px;
}
}
.section-title__tip {
font-size: 12px;
font-weight: 400;
color: var(--el-text-color-secondary);
}
</style>

View File

@@ -12,7 +12,7 @@ import { setupStore } from "@/stores";
import { setupI18n } from "@/lang";
import { setupFormCreate } from "@/plugins/form-create";
import * as ElementPlusIcons from "@element-plus/icons-vue";
import { setupPermissionGuard } from "@/router/guards/permission";
import { setupPermissionGuard } from "@/router/guards";
import { useSse } from "@/utils/sse";
import { useDictStoreHook } from "@/stores/dict";

View File

@@ -6,9 +6,9 @@ import { useTenantStoreHook } from "@/stores/tenant";
import { isTenantEnabled } from "@/utils/tenant";
/**
* 路由权限守卫
* 路由守卫
*
* 处理登录验证、动态路由生成、404检测等
* 处理登录验证、动态路由生成、404 检测、页面标题与进度条
*/
export function setupPermissionGuard() {
// 白名单支持前缀匹配:/f 命中所有公开表单分享页 /f/:formKey
@@ -84,7 +84,9 @@ export function setupPermissionGuard() {
});
}
/** 初始化多租户上下文,未启用或失败时静默跳过 */
/**
* 初始化多租户上下文,未启用或失败时静默跳过
*/
async function initTenantContext(): Promise<void> {
if (!isTenantEnabled()) return;

133
src/router/views.ts Normal file
View File

@@ -0,0 +1,133 @@
/**
* 视图组件注册表
*
* 维护「菜单 component 字符串 → src/views 视图文件」的映射,路由注册与菜单表单共用。
* 表单的「页面路径」选项树也由此派生:跳过错误页与页面内部组件,index.vue 归并到目录。
*/
/** 全部视图模块,键形如 /src/views/system/user/index.vue */
export const viewModules = import.meta.glob("/src/views/**/*.vue");
const VIEW_PREFIX = "/src/views/";
/** 视图文件路径集合,元素形如 system/user/index */
export const viewPaths = new Set(
Object.keys(viewModules).map((key) => key.slice(VIEW_PREFIX.length).replace(/\.vue$/, ""))
);
/**
* 规整组件路径:去首尾空白、开头斜杠与 .vue 后缀
*/
export function normalizeViewPath(componentPath: string): string {
return componentPath
.trim()
.replace(/^\/+/, "")
.replace(/\.vue$/i, "");
}
/**
* 解析组件:支持 system/user 与 system/user/index 两种写法,未命中回退 404
*/
export function resolveComponent(componentPath: string) {
const path = normalizeViewPath(componentPath);
return (
viewModules[`${VIEW_PREFIX}${path}.vue`] ||
viewModules[`${VIEW_PREFIX}${path}/index.vue`] ||
viewModules[`${VIEW_PREFIX}error/404.vue`]
);
}
/**
* 判断组件路径是否存在对应视图文件,目录写法命中其 index.vue
*/
export function isViewComponent(componentPath?: string): boolean {
if (!componentPath) return false;
const path = normalizeViewPath(componentPath);
return viewPaths.has(path) || viewPaths.has(`${path}/index`);
}
/** 选项树节点 */
export interface PageComponentNode {
/** 表单取值:真实文件路径,目录无 index.vue 时不可选 */
value?: string;
label: string;
children?: PageComponentNode[];
disabled?: boolean;
}
function isSelectable(path: string): boolean {
const segments = path.split("/");
if (segments.includes("components")) return false;
return segments[0] !== "error" && path !== "redirect";
}
function buildTree(): PageComponentNode[] {
const tree: PageComponentNode[] = [];
[...viewPaths].filter(isSelectable).forEach((path) => insertPath(tree, path));
sortTree(tree);
markDisabled(tree);
return tree;
}
/**
* 将视图路径插入选项树,index.vue 归并到所在目录
*/
function insertPath(tree: PageComponentNode[], path: string): void {
const segments = path.split("/");
if (segments[segments.length - 1] === "index") segments.pop();
let level = tree;
segments.forEach((segment, index) => {
const isLeaf = index === segments.length - 1;
let node = level.find((item) => item.label === segment);
if (!node) {
node = { label: segment };
level.push(node);
}
if (isLeaf) {
// 取值始终是真实文件路径,与历史数据和列表展示一致
node.value = path;
return;
}
node.children ??= [];
level = node.children;
});
}
/**
* 目录下没有 index.vue 时不可选,只用于展开
*/
function markDisabled(nodes: PageComponentNode[]): void {
nodes.forEach((node) => {
if (!node.value) node.disabled = true;
if (node.children) markDisabled(node.children);
});
}
function sortTree(nodes: PageComponentNode[]): void {
nodes.sort((a, b) => a.label.localeCompare(b.label));
nodes.forEach((node) => node.children && sortTree(node.children));
}
/** 页面组件选项树 */
export const pageComponentTree = buildTree();
/**
* 节点筛选:名称与文件路径都参与匹配,输入 index 可筛出所有主页
*/
export function filterPageComponent(
query: string,
node: { label?: string; value?: string }
): boolean {
if (!query) return true;
const keyword = query.toLowerCase();
return (
(node.label ?? "").toLowerCase().includes(keyword) ||
(node.value ?? "").toLowerCase().includes(keyword)
);
}

View File

@@ -1,13 +1,14 @@
import type { RouteRecordRaw } from "vue-router";
import { constantRoutes } from "@/router";
import { resolveComponent } from "@/router/views";
import { store } from "@/stores";
import router from "@/router";
import { useUserStoreHook } from "@/stores/user";
import { isExternal } from "@/utils";
import { isExternal, joinRoutePath } from "@/utils";
import MenuAPI from "@/api/system/menu";
import type { RouteItem } from "@/api/system/menu";
const modules = import.meta.glob("../views/**/*.vue");
const Layout = () => import("../layouts/index.vue");
export const usePermissionStore = defineStore("permission", () => {
@@ -21,7 +22,7 @@ export const usePermissionStore = defineStore("permission", () => {
async function generateRoutes(): Promise<RouteRecordRaw[]> {
try {
const routeData = await MenuAPI.getRoutes();
const menuRoutes = transformRoutes(routeData);
const menuRoutes = buildRoutes(routeData);
const registerRoutes = filterRoutes(menuRoutes);
routes.value = [...constantRoutes, ...menuRoutes];
@@ -132,49 +133,115 @@ export const usePermissionStore = defineStore("permission", () => {
};
});
/**
* 将后端路由数据转为 Vue Router 配置
*/
const transformRoutes = (routes: RouteItem[], isTopLevel: boolean = true): RouteRecordRaw[] => {
return routes.map((route) => {
const { children, ...args } = route;
const componentPath = route.component;
// 非顶层目录壳去掉 Layout 组件,仅保留路由结构
const resolvedComponent = isTopLevel || componentPath !== "Layout" ? componentPath : undefined;
const normalizedRoute = { ...args } as RouteRecordRaw;
if (!resolvedComponent) {
normalizedRoute.component = undefined;
} else {
normalizedRoute.component =
resolvedComponent === "Layout" ? Layout : resolveComponent(resolvedComponent);
}
if (children && children.length > 0) {
normalizedRoute.children = transformRoutes(children, false);
}
return normalizedRoute;
});
};
// 目录菜单在 component 上的占位值,表示该级只作路由容器
const LAYOUT_COMPONENT = "Layout";
/**
* 解析组件
* 后端菜单树还原为 Vue Router 路由树
*
* 支持 xxx.vue 与 xxx/index.vue 两种写法,未命中时回退到 404
* 顶层菜单统一由 Layout 承载:目录本身是容器,页面下沉为 path 为空的子路由;
* 路径前缀与默认跳转在此推导,菜单数据只描述业务信息。
*/
function resolveComponent(componentPath: string) {
const normalized = componentPath
.trim()
.replace(/^\/+/, "")
.replace(/\.vue$/i, "");
return (
modules[`../views/${normalized}.vue`] ||
modules[`../views/${normalized}/index.vue`] ||
modules[`../views/error/404.vue`]
);
const buildRoutes = (menus: RouteItem[]): RouteRecordRaw[] => menus.map(buildTopLevelRoute);
/**
* 顶层菜单路由:套 Layout 壳,保证页面具备侧边栏与顶栏
*/
function buildTopLevelRoute(menu: RouteItem): RouteRecordRaw {
// 新标签页外链不注册路由,保留原始数据供侧边栏直接跳转
if (isExternalLink(menu)) return toExternalRoute(menu, menu.path as string);
const path = joinRoutePath("", menu.path);
const meta = { ...menu.meta };
// 目录:自身就是侧边栏分组,标题与图标保留在壳层
if (isContainer(menu)) {
return {
path,
name: path,
component: Layout,
meta,
redirect: menu.redirect || firstVisiblePath(menu.children, path),
children: buildChildRoutes(menu.children, path),
};
}
// 页面:壳层只承载显示状态,标题与图标下沉到页面子路由,避免面包屑与标签页多出一层
return {
path,
name: path,
component: Layout,
meta: { hidden: meta.hidden },
children: [
{
path: "",
name: menu.name,
component: resolveComponent(menu.component as string),
meta,
children: buildChildRoutes(menu.children, path),
},
],
};
}
/**
* 子级菜单路由:目录保留为路径分组,页面挂载实际组件
*/
function buildChildRoutes(menus: RouteItem[] | undefined, basePath: string): RouteRecordRaw[] {
return (menus ?? []).map((menu) => {
const fullPath = joinRoutePath(basePath, menu.path);
if (isExternalLink(menu)) return toExternalRoute(menu, fullPath);
if (isContainer(menu)) {
const children = buildChildRoutes(menu.children, fullPath);
return {
path: menu.path ?? "",
meta: { ...menu.meta },
children,
...(children.length
? { redirect: menu.redirect || firstVisiblePath(menu.children, fullPath) }
: {}),
};
}
return {
path: menu.path ?? "",
name: menu.name,
component: resolveComponent(menu.component as string),
meta: { ...menu.meta },
};
});
}
/**
* 新标签页外链路由:不参与路由注册,仅保留侧边栏跳转所需信息
*/
function toExternalRoute(menu: RouteItem, path: string): RouteRecordRaw {
const { children: _children, ...rest } = menu;
return { ...rest, path } as RouteRecordRaw;
}
/**
* 目录菜单:没有页面组件,只作路由容器
*/
function isContainer(menu: RouteItem): boolean {
return !menu.component || menu.component === LAYOUT_COMPONENT;
}
/**
* 新标签页外链:外链地址直接写在路由路径上且不带组件
*/
function isExternalLink(menu: RouteItem): boolean {
return !menu.component && isExternal(menu.path ?? "");
}
/**
* 默认跳转:第一个可见子菜单的完整路径
*/
function firstVisiblePath(menus: RouteItem[] | undefined, basePath: string): string | undefined {
const first = (menus ?? []).find((menu) => !menu.meta?.hidden && !isExternal(menu.path ?? ""));
return first ? joinRoutePath(basePath, first.path) : undefined;
}
/**

View File

@@ -11,5 +11,8 @@ export { formatGrowthRate, formatFileSize, formatNumber, formatCurrency } from "
// 文件下载
export { downloadFile } from "./download";
// 路由路径
export { joinRoutePath } from "./route";
// 本地存储
export { Storage } from "./storage";

19
src/utils/route.ts Normal file
View File

@@ -0,0 +1,19 @@
/**
* 路由路径工具
*/
import { isExternal } from "./validate";
/**
* 拼接路由路径
*
* 外链与绝对路径原样返回,相对路径挂到父级之后;顶级相对路径自动补前导斜杠。
*
* @param basePath 父级完整路径
* @param path 当前段路径
*/
export function joinRoutePath(basePath: string, path?: string): string {
if (!path) return basePath || "/";
if (isExternal(path) || path.startsWith("/")) return path;
return `${basePath.replace(/\/+$/, "")}/${path}`;
}

View File

@@ -549,12 +549,12 @@ onMounted(() => {
gap: 6px;
align-items: center;
max-width: 100%;
}
&__text {
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.form-name__text {
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>

File diff suppressed because it is too large Load Diff