Merge branch 'main' into fix-downloader
This commit is contained in:
commit
1d9cd88dd7
1
.gitignore
vendored
1
.gitignore
vendored
@ -49,3 +49,4 @@ vite.config.ts.*
|
|||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
.history
|
.history
|
||||||
|
.cursor
|
||||||
|
|||||||
@ -140,8 +140,12 @@ pnpm build
|
|||||||
|
|
||||||
## 貢献者
|
## 貢献者
|
||||||
|
|
||||||
|
<a href="https://openomy.app/github/vbenjs/vue-vben-admin" target="_blank" style="display: block; width: 100%;" align="center">
|
||||||
|
<img src="https://openomy.app/svg?repo=vbenjs/vue-vben-admin&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
|
||||||
|
</a>
|
||||||
|
|
||||||
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
|
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
|
||||||
<img alt="Contributors" src="https://opencollective.com/vbenjs/contributors.svg?button=false" />
|
<img alt="Contributors" src="https://contrib.rocks/image?repo=vbenjs/vue-vben-admin" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
## Discord
|
## Discord
|
||||||
|
|||||||
@ -140,8 +140,12 @@ If you think this project is helpful to you, you can help the author buy a cup o
|
|||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
|
<a href="https://openomy.app/github/vbenjs/vue-vben-admin" target="_blank" style="display: block; width: 100%;" align="center">
|
||||||
|
<img src="https://openomy.app/svg?repo=vbenjs/vue-vben-admin&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
|
||||||
|
</a>
|
||||||
|
|
||||||
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
|
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
|
||||||
<img alt="Contributors" src="https://opencollective.com/vbenjs/contributors.svg?button=false" />
|
<img alt="Contributors" src="https://contrib.rocks/image?repo=vbenjs/vue-vben-admin" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
## Discord
|
## Discord
|
||||||
|
|||||||
@ -140,8 +140,12 @@ pnpm build
|
|||||||
|
|
||||||
## 贡献者
|
## 贡献者
|
||||||
|
|
||||||
|
<a href="https://openomy.app/github/vbenjs/vue-vben-admin" target="_blank" style="display: block; width: 100%;" align="center">
|
||||||
|
<img src="https://openomy.app/svg?repo=vbenjs/vue-vben-admin&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
|
||||||
|
</a>
|
||||||
|
|
||||||
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
|
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
|
||||||
<img alt="Contributors" src="https://opencollective.com/vbenjs/contributors.svg?button=false" />
|
<img alt="Contributors" src="https://contrib.rocks/image?repo=vbenjs/vue-vben-admin" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
## Discord
|
## Discord
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
|
import { eventHandler } from 'h3';
|
||||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
import { unAuthorizedResponse } from '~/utils/response';
|
import { MOCK_CODES } from '~/utils/mock-data';
|
||||||
|
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
|
||||||
|
|
||||||
export default eventHandler((event) => {
|
export default eventHandler((event) => {
|
||||||
const userinfo = verifyAccessToken(event);
|
const userinfo = verifyAccessToken(event);
|
||||||
|
|||||||
@ -1,9 +1,15 @@
|
|||||||
|
import { defineEventHandler, readBody, setResponseStatus } from 'h3';
|
||||||
import {
|
import {
|
||||||
clearRefreshTokenCookie,
|
clearRefreshTokenCookie,
|
||||||
setRefreshTokenCookie,
|
setRefreshTokenCookie,
|
||||||
} from '~/utils/cookie-utils';
|
} from '~/utils/cookie-utils';
|
||||||
import { generateAccessToken, generateRefreshToken } from '~/utils/jwt-utils';
|
import { generateAccessToken, generateRefreshToken } from '~/utils/jwt-utils';
|
||||||
import { forbiddenResponse } from '~/utils/response';
|
import { MOCK_USERS } from '~/utils/mock-data';
|
||||||
|
import {
|
||||||
|
forbiddenResponse,
|
||||||
|
useResponseError,
|
||||||
|
useResponseSuccess,
|
||||||
|
} from '~/utils/response';
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const { password, username } = await readBody(event);
|
const { password, username } = await readBody(event);
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
|
import { defineEventHandler } from 'h3';
|
||||||
import {
|
import {
|
||||||
clearRefreshTokenCookie,
|
clearRefreshTokenCookie,
|
||||||
getRefreshTokenFromCookie,
|
getRefreshTokenFromCookie,
|
||||||
} from '~/utils/cookie-utils';
|
} from '~/utils/cookie-utils';
|
||||||
|
import { useResponseSuccess } from '~/utils/response';
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const refreshToken = getRefreshTokenFromCookie(event);
|
const refreshToken = getRefreshTokenFromCookie(event);
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
|
import { defineEventHandler } from 'h3';
|
||||||
import {
|
import {
|
||||||
clearRefreshTokenCookie,
|
clearRefreshTokenCookie,
|
||||||
getRefreshTokenFromCookie,
|
getRefreshTokenFromCookie,
|
||||||
setRefreshTokenCookie,
|
setRefreshTokenCookie,
|
||||||
} from '~/utils/cookie-utils';
|
} from '~/utils/cookie-utils';
|
||||||
import { verifyRefreshToken } from '~/utils/jwt-utils';
|
import { generateAccessToken, verifyRefreshToken } from '~/utils/jwt-utils';
|
||||||
|
import { MOCK_USERS } from '~/utils/mock-data';
|
||||||
import { forbiddenResponse } from '~/utils/response';
|
import { forbiddenResponse } from '~/utils/response';
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
import { eventHandler, setHeader } from 'h3';
|
||||||
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
|
import { unAuthorizedResponse } from '~/utils/response';
|
||||||
|
|
||||||
export default eventHandler(async (event) => {
|
export default eventHandler(async (event) => {
|
||||||
const userinfo = verifyAccessToken(event);
|
const userinfo = verifyAccessToken(event);
|
||||||
if (!userinfo) {
|
if (!userinfo) {
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
|
import { eventHandler } from 'h3';
|
||||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
import { unAuthorizedResponse } from '~/utils/response';
|
import { MOCK_MENUS } from '~/utils/mock-data';
|
||||||
|
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
|
||||||
|
|
||||||
export default eventHandler(async (event) => {
|
export default eventHandler(async (event) => {
|
||||||
const userinfo = verifyAccessToken(event);
|
const userinfo = verifyAccessToken(event);
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
import { eventHandler, getQuery, setResponseStatus } from 'h3';
|
||||||
|
import { useResponseError } from '~/utils/response';
|
||||||
|
|
||||||
export default eventHandler((event) => {
|
export default eventHandler((event) => {
|
||||||
const { status } = getQuery(event);
|
const { status } = getQuery(event);
|
||||||
setResponseStatus(event, Number(status));
|
setResponseStatus(event, Number(status));
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { eventHandler } from 'h3';
|
||||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
import {
|
import {
|
||||||
sleep,
|
sleep,
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { eventHandler } from 'h3';
|
||||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
import {
|
import {
|
||||||
sleep,
|
sleep,
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { eventHandler } from 'h3';
|
||||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
import {
|
import {
|
||||||
sleep,
|
sleep,
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
|
import { eventHandler } from 'h3';
|
||||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
|
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { eventHandler } from 'h3';
|
||||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
import { MOCK_MENU_LIST } from '~/utils/mock-data';
|
import { MOCK_MENU_LIST } from '~/utils/mock-data';
|
||||||
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
|
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
|
import { eventHandler, getQuery } from 'h3';
|
||||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
import { MOCK_MENU_LIST } from '~/utils/mock-data';
|
import { MOCK_MENU_LIST } from '~/utils/mock-data';
|
||||||
import { unAuthorizedResponse } from '~/utils/response';
|
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
|
||||||
|
|
||||||
const namesMap: Record<string, any> = {};
|
const namesMap: Record<string, any> = {};
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
|
import { eventHandler, getQuery } from 'h3';
|
||||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
import { MOCK_MENU_LIST } from '~/utils/mock-data';
|
import { MOCK_MENU_LIST } from '~/utils/mock-data';
|
||||||
import { unAuthorizedResponse } from '~/utils/response';
|
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
|
||||||
|
|
||||||
const pathMap: Record<string, any> = { '/': 0 };
|
const pathMap: Record<string, any> = { '/': 0 };
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
|
import { eventHandler, getQuery } from 'h3';
|
||||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
import { getMenuIds, MOCK_MENU_LIST } from '~/utils/mock-data';
|
import { getMenuIds, MOCK_MENU_LIST } from '~/utils/mock-data';
|
||||||
import { unAuthorizedResponse, usePageResponseSuccess } from '~/utils/response';
|
import { unAuthorizedResponse, usePageResponseSuccess } from '~/utils/response';
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
|
import { eventHandler, getQuery } from 'h3';
|
||||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
import { unAuthorizedResponse, usePageResponseSuccess } from '~/utils/response';
|
import {
|
||||||
|
sleep,
|
||||||
|
unAuthorizedResponse,
|
||||||
|
usePageResponseSuccess,
|
||||||
|
} from '~/utils/response';
|
||||||
|
|
||||||
function generateMockDataList(count: number) {
|
function generateMockDataList(count: number) {
|
||||||
const dataList = [];
|
const dataList = [];
|
||||||
@ -44,30 +49,69 @@ export default eventHandler(async (event) => {
|
|||||||
await sleep(600);
|
await sleep(600);
|
||||||
|
|
||||||
const { page, pageSize, sortBy, sortOrder } = getQuery(event);
|
const { page, pageSize, sortBy, sortOrder } = getQuery(event);
|
||||||
|
// 规范化分页参数,处理 string[]
|
||||||
|
const pageRaw = Array.isArray(page) ? page[0] : page;
|
||||||
|
const pageSizeRaw = Array.isArray(pageSize) ? pageSize[0] : pageSize;
|
||||||
|
const pageNumber = Math.max(
|
||||||
|
1,
|
||||||
|
Number.parseInt(String(pageRaw ?? '1'), 10) || 1,
|
||||||
|
);
|
||||||
|
const pageSizeNumber = Math.min(
|
||||||
|
100,
|
||||||
|
Math.max(1, Number.parseInt(String(pageSizeRaw ?? '10'), 10) || 10),
|
||||||
|
);
|
||||||
const listData = structuredClone(mockData);
|
const listData = structuredClone(mockData);
|
||||||
if (sortBy && Reflect.has(listData[0], sortBy as string)) {
|
|
||||||
|
// 规范化 query 入参,兼容 string[]
|
||||||
|
const sortKeyRaw = Array.isArray(sortBy) ? sortBy[0] : sortBy;
|
||||||
|
const sortOrderRaw = Array.isArray(sortOrder) ? sortOrder[0] : sortOrder;
|
||||||
|
// 检查 sortBy 是否是 listData 元素的合法属性键
|
||||||
|
if (
|
||||||
|
typeof sortKeyRaw === 'string' &&
|
||||||
|
listData[0] &&
|
||||||
|
Object.prototype.hasOwnProperty.call(listData[0], sortKeyRaw)
|
||||||
|
) {
|
||||||
|
// 定义数组元素的类型
|
||||||
|
type ItemType = (typeof listData)[0];
|
||||||
|
const sortKey = sortKeyRaw as keyof ItemType; // 将 sortBy 断言为合法键
|
||||||
|
const isDesc = sortOrderRaw === 'desc';
|
||||||
listData.sort((a, b) => {
|
listData.sort((a, b) => {
|
||||||
if (sortOrder === 'asc') {
|
const aValue = a[sortKey] as unknown;
|
||||||
if (sortBy === 'price') {
|
const bValue = b[sortKey] as unknown;
|
||||||
return (
|
|
||||||
Number.parseFloat(a[sortBy as string]) -
|
let result = 0;
|
||||||
Number.parseFloat(b[sortBy as string])
|
|
||||||
);
|
if (typeof aValue === 'number' && typeof bValue === 'number') {
|
||||||
|
result = aValue - bValue;
|
||||||
|
} else if (aValue instanceof Date && bValue instanceof Date) {
|
||||||
|
result = aValue.getTime() - bValue.getTime();
|
||||||
|
} else if (typeof aValue === 'boolean' && typeof bValue === 'boolean') {
|
||||||
|
if (aValue === bValue) {
|
||||||
|
result = 0;
|
||||||
} else {
|
} else {
|
||||||
return a[sortBy as string] > b[sortBy as string] ? 1 : -1;
|
result = aValue ? 1 : -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (sortBy === 'price') {
|
const aStr = String(aValue);
|
||||||
return (
|
const bStr = String(bValue);
|
||||||
Number.parseFloat(b[sortBy as string]) -
|
const aNum = Number(aStr);
|
||||||
Number.parseFloat(a[sortBy as string])
|
const bNum = Number(bStr);
|
||||||
);
|
result =
|
||||||
} else {
|
Number.isFinite(aNum) && Number.isFinite(bNum)
|
||||||
return a[sortBy as string] < b[sortBy as string] ? 1 : -1;
|
? aNum - bNum
|
||||||
}
|
: aStr.localeCompare(bStr, undefined, {
|
||||||
|
numeric: true,
|
||||||
|
sensitivity: 'base',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return isDesc ? -result : result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return usePageResponseSuccess(page as string, pageSize as string, listData);
|
return usePageResponseSuccess(
|
||||||
|
String(pageNumber),
|
||||||
|
String(pageSizeNumber),
|
||||||
|
listData,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1 +1,3 @@
|
|||||||
|
import { defineEventHandler } from 'h3';
|
||||||
|
|
||||||
export default defineEventHandler(() => 'Test get handler');
|
export default defineEventHandler(() => 'Test get handler');
|
||||||
|
|||||||
@ -1 +1,3 @@
|
|||||||
|
import { defineEventHandler } from 'h3';
|
||||||
|
|
||||||
export default defineEventHandler(() => 'Test post handler');
|
export default defineEventHandler(() => 'Test post handler');
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
|
import { eventHandler } from 'h3';
|
||||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
import { unAuthorizedResponse } from '~/utils/response';
|
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
|
||||||
|
|
||||||
export default eventHandler((event) => {
|
export default eventHandler((event) => {
|
||||||
const userinfo = verifyAccessToken(event);
|
const userinfo = verifyAccessToken(event);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
|
import { eventHandler } from 'h3';
|
||||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||||
import { unAuthorizedResponse } from '~/utils/response';
|
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
|
||||||
|
|
||||||
export default eventHandler((event) => {
|
export default eventHandler((event) => {
|
||||||
const userinfo = verifyAccessToken(event);
|
const userinfo = verifyAccessToken(event);
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { defineEventHandler } from 'h3';
|
||||||
import { forbiddenResponse, sleep } from '~/utils/response';
|
import { forbiddenResponse, sleep } from '~/utils/response';
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { defineEventHandler } from 'h3';
|
||||||
|
|
||||||
export default defineEventHandler(() => {
|
export default defineEventHandler(() => {
|
||||||
return `
|
return `
|
||||||
<h1>Hello Vben Admin</h1>
|
<h1>Hello Vben Admin</h1>
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import type { EventHandlerRequest, H3Event } from 'h3';
|
import type { EventHandlerRequest, H3Event } from 'h3';
|
||||||
|
|
||||||
|
import { deleteCookie, getCookie, setCookie } from 'h3';
|
||||||
|
|
||||||
export function clearRefreshTokenCookie(event: H3Event<EventHandlerRequest>) {
|
export function clearRefreshTokenCookie(event: H3Event<EventHandlerRequest>) {
|
||||||
deleteCookie(event, 'jwt', {
|
deleteCookie(event, 'jwt', {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
import type { EventHandlerRequest, H3Event } from 'h3';
|
import type { EventHandlerRequest, H3Event } from 'h3';
|
||||||
|
|
||||||
|
import type { UserInfo } from './mock-data';
|
||||||
|
|
||||||
|
import { getHeader } from 'h3';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
import { UserInfo } from './mock-data';
|
import { MOCK_USERS } from './mock-data';
|
||||||
|
|
||||||
// TODO: Replace with your own secret key
|
// TODO: Replace with your own secret key
|
||||||
const ACCESS_TOKEN_SECRET = 'access_token_secret';
|
const ACCESS_TOKEN_SECRET = 'access_token_secret';
|
||||||
@ -31,12 +34,22 @@ export function verifyAccessToken(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = authHeader.split(' ')[1];
|
const tokenParts = authHeader.split(' ');
|
||||||
|
if (tokenParts.length !== 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const token = tokenParts[1] as string;
|
||||||
try {
|
try {
|
||||||
const decoded = jwt.verify(token, ACCESS_TOKEN_SECRET) as UserPayload;
|
const decoded = jwt.verify(
|
||||||
|
token,
|
||||||
|
ACCESS_TOKEN_SECRET,
|
||||||
|
) as unknown as UserPayload;
|
||||||
|
|
||||||
const username = decoded.username;
|
const username = decoded.username;
|
||||||
const user = MOCK_USERS.find((item) => item.username === username);
|
const user = MOCK_USERS.find((item) => item.username === username);
|
||||||
|
if (!user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const { password: _pwd, ...userinfo } = user;
|
const { password: _pwd, ...userinfo } = user;
|
||||||
return userinfo;
|
return userinfo;
|
||||||
} catch {
|
} catch {
|
||||||
@ -50,7 +63,12 @@ export function verifyRefreshToken(
|
|||||||
try {
|
try {
|
||||||
const decoded = jwt.verify(token, REFRESH_TOKEN_SECRET) as UserPayload;
|
const decoded = jwt.verify(token, REFRESH_TOKEN_SECRET) as UserPayload;
|
||||||
const username = decoded.username;
|
const username = decoded.username;
|
||||||
const user = MOCK_USERS.find((item) => item.username === username);
|
const user = MOCK_USERS.find(
|
||||||
|
(item) => item.username === username,
|
||||||
|
) as UserInfo;
|
||||||
|
if (!user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const { password: _pwd, ...userinfo } = user;
|
const { password: _pwd, ...userinfo } = user;
|
||||||
return userinfo;
|
return userinfo;
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@ -276,7 +276,7 @@ export const MOCK_MENU_LIST = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: 20_401,
|
id: 20_401,
|
||||||
pid: 201,
|
pid: 202,
|
||||||
name: 'SystemDeptCreate',
|
name: 'SystemDeptCreate',
|
||||||
status: 1,
|
status: 1,
|
||||||
type: 'button',
|
type: 'button',
|
||||||
@ -285,7 +285,7 @@ export const MOCK_MENU_LIST = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 20_402,
|
id: 20_402,
|
||||||
pid: 201,
|
pid: 202,
|
||||||
name: 'SystemDeptEdit',
|
name: 'SystemDeptEdit',
|
||||||
status: 1,
|
status: 1,
|
||||||
type: 'button',
|
type: 'button',
|
||||||
@ -294,7 +294,7 @@ export const MOCK_MENU_LIST = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 20_403,
|
id: 20_403,
|
||||||
pid: 201,
|
pid: 202,
|
||||||
name: 'SystemDeptDelete',
|
name: 'SystemDeptDelete',
|
||||||
status: 1,
|
status: 1,
|
||||||
type: 'button',
|
type: 'button',
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import type { EventHandlerRequest, H3Event } from 'h3';
|
import type { EventHandlerRequest, H3Event } from 'h3';
|
||||||
|
|
||||||
|
import { setResponseStatus } from 'h3';
|
||||||
|
|
||||||
export function useResponseSuccess<T = any>(data: T) {
|
export function useResponseSuccess<T = any>(data: T) {
|
||||||
return {
|
return {
|
||||||
code: 0,
|
code: 0,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/web-antd",
|
"name": "@vben/web-antd",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://vben.pro",
|
"homepage": "https://vben.pro",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { computed, ref, watch } from 'vue';
|
|||||||
import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
|
import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
|
||||||
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
|
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
|
||||||
import { useWatermark } from '@vben/hooks';
|
import { useWatermark } from '@vben/hooks';
|
||||||
import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons';
|
import { BookOpenText, CircleHelp, SvgGithubIcon } from '@vben/icons';
|
||||||
import {
|
import {
|
||||||
BasicLayout,
|
BasicLayout,
|
||||||
LockScreen,
|
LockScreen,
|
||||||
@ -76,7 +76,7 @@ const menus = computed(() => [
|
|||||||
target: '_blank',
|
target: '_blank',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: MdiGithub,
|
icon: SvgGithubIcon,
|
||||||
text: 'GitHub',
|
text: 'GitHub',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -106,11 +106,16 @@ function handleMakeAll() {
|
|||||||
notifications.value.forEach((item) => (item.isRead = true));
|
notifications.value.forEach((item) => (item.isRead = true));
|
||||||
}
|
}
|
||||||
watch(
|
watch(
|
||||||
() => preferences.app.watermark,
|
() => ({
|
||||||
async (enable) => {
|
enable: preferences.app.watermark,
|
||||||
|
content: preferences.app.watermarkContent,
|
||||||
|
}),
|
||||||
|
async ({ enable, content }) => {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
await updateWatermark({
|
await updateWatermark({
|
||||||
content: `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
|
content:
|
||||||
|
content ||
|
||||||
|
`${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
destroyWatermark();
|
destroyWatermark();
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/web-ele",
|
"name": "@vben/web-ele",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://vben.pro",
|
"homepage": "https://vben.pro",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { computed, ref, watch } from 'vue';
|
|||||||
import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
|
import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
|
||||||
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
|
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
|
||||||
import { useWatermark } from '@vben/hooks';
|
import { useWatermark } from '@vben/hooks';
|
||||||
import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons';
|
import { BookOpenText, CircleHelp, SvgGithubIcon } from '@vben/icons';
|
||||||
import {
|
import {
|
||||||
BasicLayout,
|
BasicLayout,
|
||||||
LockScreen,
|
LockScreen,
|
||||||
@ -76,7 +76,7 @@ const menus = computed(() => [
|
|||||||
target: '_blank',
|
target: '_blank',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: MdiGithub,
|
icon: SvgGithubIcon,
|
||||||
text: 'GitHub',
|
text: 'GitHub',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -106,11 +106,16 @@ function handleMakeAll() {
|
|||||||
notifications.value.forEach((item) => (item.isRead = true));
|
notifications.value.forEach((item) => (item.isRead = true));
|
||||||
}
|
}
|
||||||
watch(
|
watch(
|
||||||
() => preferences.app.watermark,
|
() => ({
|
||||||
async (enable) => {
|
enable: preferences.app.watermark,
|
||||||
|
content: preferences.app.watermarkContent,
|
||||||
|
}),
|
||||||
|
async ({ enable, content }) => {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
await updateWatermark({
|
await updateWatermark({
|
||||||
content: `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
|
content:
|
||||||
|
content ||
|
||||||
|
`${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
destroyWatermark();
|
destroyWatermark();
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/web-naive",
|
"name": "@vben/web-naive",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://vben.pro",
|
"homepage": "https://vben.pro",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { computed, ref, watch } from 'vue';
|
|||||||
import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
|
import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
|
||||||
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
|
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
|
||||||
import { useWatermark } from '@vben/hooks';
|
import { useWatermark } from '@vben/hooks';
|
||||||
import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons';
|
import { BookOpenText, CircleHelp, SvgGithubIcon } from '@vben/icons';
|
||||||
import {
|
import {
|
||||||
BasicLayout,
|
BasicLayout,
|
||||||
LockScreen,
|
LockScreen,
|
||||||
@ -76,7 +76,7 @@ const menus = computed(() => [
|
|||||||
target: '_blank',
|
target: '_blank',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: MdiGithub,
|
icon: SvgGithubIcon,
|
||||||
text: 'GitHub',
|
text: 'GitHub',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -107,11 +107,16 @@ function handleMakeAll() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => preferences.app.watermark,
|
() => ({
|
||||||
async (enable) => {
|
enable: preferences.app.watermark,
|
||||||
|
content: preferences.app.watermarkContent,
|
||||||
|
}),
|
||||||
|
async ({ enable, content }) => {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
await updateWatermark({
|
await updateWatermark({
|
||||||
content: `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
|
content:
|
||||||
|
content ||
|
||||||
|
`${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
destroyWatermark();
|
destroyWatermark();
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/docs",
|
"name": "@vben/docs",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vitepress build",
|
"build": "vitepress build",
|
||||||
|
|||||||
@ -90,30 +90,52 @@ import { h } from 'vue';
|
|||||||
import { globalShareState, IconPicker } from '@vben/common-ui';
|
import { globalShareState, IconPicker } from '@vben/common-ui';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import {
|
const AutoComplete = defineAsyncComponent(
|
||||||
AutoComplete,
|
() => import('ant-design-vue/es/auto-complete'),
|
||||||
Button,
|
);
|
||||||
Checkbox,
|
const Button = defineAsyncComponent(() => import('ant-design-vue/es/button'));
|
||||||
CheckboxGroup,
|
const Checkbox = defineAsyncComponent(
|
||||||
DatePicker,
|
() => import('ant-design-vue/es/checkbox'),
|
||||||
Divider,
|
);
|
||||||
Input,
|
const CheckboxGroup = defineAsyncComponent(() =>
|
||||||
InputNumber,
|
import('ant-design-vue/es/checkbox').then((res) => res.CheckboxGroup),
|
||||||
InputPassword,
|
);
|
||||||
Mentions,
|
const DatePicker = defineAsyncComponent(
|
||||||
notification,
|
() => import('ant-design-vue/es/date-picker'),
|
||||||
Radio,
|
);
|
||||||
RadioGroup,
|
const Divider = defineAsyncComponent(() => import('ant-design-vue/es/divider'));
|
||||||
RangePicker,
|
const Input = defineAsyncComponent(() => import('ant-design-vue/es/input'));
|
||||||
Rate,
|
const InputNumber = defineAsyncComponent(
|
||||||
Select,
|
() => import('ant-design-vue/es/input-number'),
|
||||||
Space,
|
);
|
||||||
Switch,
|
const InputPassword = defineAsyncComponent(() =>
|
||||||
Textarea,
|
import('ant-design-vue/es/input').then((res) => res.InputPassword),
|
||||||
TimePicker,
|
);
|
||||||
TreeSelect,
|
const Mentions = defineAsyncComponent(
|
||||||
Upload,
|
() => import('ant-design-vue/es/mentions'),
|
||||||
} from 'ant-design-vue';
|
);
|
||||||
|
const Radio = defineAsyncComponent(() => import('ant-design-vue/es/radio'));
|
||||||
|
const RadioGroup = defineAsyncComponent(() =>
|
||||||
|
import('ant-design-vue/es/radio').then((res) => res.RadioGroup),
|
||||||
|
);
|
||||||
|
const RangePicker = defineAsyncComponent(() =>
|
||||||
|
import('ant-design-vue/es/date-picker').then((res) => res.RangePicker),
|
||||||
|
);
|
||||||
|
const Rate = defineAsyncComponent(() => import('ant-design-vue/es/rate'));
|
||||||
|
const Select = defineAsyncComponent(() => import('ant-design-vue/es/select'));
|
||||||
|
const Space = defineAsyncComponent(() => import('ant-design-vue/es/space'));
|
||||||
|
const Switch = defineAsyncComponent(() => import('ant-design-vue/es/switch'));
|
||||||
|
const Textarea = defineAsyncComponent(() =>
|
||||||
|
import('ant-design-vue/es/input').then((res) => res.Textarea),
|
||||||
|
);
|
||||||
|
const TimePicker = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/time-picker'),
|
||||||
|
);
|
||||||
|
const TreeSelect = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/tree-select'),
|
||||||
|
);
|
||||||
|
const Upload = defineAsyncComponent(() => import('ant-design-vue/es/upload'));
|
||||||
|
|
||||||
|
|
||||||
const withDefaultPlaceholder = <T extends Component>(
|
const withDefaultPlaceholder = <T extends Component>(
|
||||||
component: T,
|
component: T,
|
||||||
@ -304,7 +326,7 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单
|
|||||||
|
|
||||||
| 属性名 | 描述 | 类型 | 默认值 |
|
| 属性名 | 描述 | 类型 | 默认值 |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| layout | 表单项布局 | `'horizontal' \| 'vertical'` | `horizontal` |
|
| layout | 表单项布局 | `'horizontal' \| 'vertical'\| 'inline'` | `horizontal` |
|
||||||
| showCollapseButton | 是否显示折叠按钮 | `boolean` | `false` |
|
| showCollapseButton | 是否显示折叠按钮 | `boolean` | `false` |
|
||||||
| wrapperClass | 表单的布局,基于tailwindcss | `any` | - |
|
| wrapperClass | 表单的布局,基于tailwindcss | `any` | - |
|
||||||
| actionWrapperClass | 表单操作区域class | `any` | - |
|
| actionWrapperClass | 表单操作区域class | `any` | - |
|
||||||
@ -451,6 +473,8 @@ export interface FormSchema<
|
|||||||
fieldName: string;
|
fieldName: string;
|
||||||
/** 帮助信息 */
|
/** 帮助信息 */
|
||||||
help?: CustomRenderType;
|
help?: CustomRenderType;
|
||||||
|
/** 是否隐藏表单项 */
|
||||||
|
hide?: boolean;
|
||||||
/** 表单的标签(如果是一个string,会用于默认必选规则的消息提示) */
|
/** 表单的标签(如果是一个string,会用于默认必选规则的消息提示) */
|
||||||
label?: CustomRenderType;
|
label?: CustomRenderType;
|
||||||
/** 自定义组件内部渲染 */
|
/** 自定义组件内部渲染 */
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/commitlint-config",
|
"name": "@vben/commitlint-config",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/stylelint-config",
|
"name": "@vben/stylelint-config",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/node-utils",
|
"name": "@vben/node-utils",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/tailwind-config",
|
"name": "@vben/tailwind-config",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/tsconfig",
|
"name": "@vben/tsconfig",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/vite-config",
|
"name": "@vben/vite-config",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vben-admin-monorepo",
|
"name": "vben-admin-monorepo",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"private": true,
|
"private": true,
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"monorepo",
|
"monorepo",
|
||||||
@ -98,7 +98,7 @@
|
|||||||
"node": ">=20.10.0",
|
"node": ">=20.10.0",
|
||||||
"pnpm": ">=9.12.0"
|
"pnpm": ">=9.12.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.12.4",
|
"packageManager": "pnpm@10.14.0",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"peerDependencyRules": {
|
"peerDependencyRules": {
|
||||||
"allowedVersions": {
|
"allowedVersions": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/design",
|
"name": "@vben-core/design",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
.side-content {
|
.side-content {
|
||||||
animation-duration: 0.2s;
|
animation-duration: 0.3s;
|
||||||
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@
|
|||||||
@keyframes slide-down {
|
@keyframes slide-down {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-10px);
|
transform: translateY(50px);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
@ -49,7 +49,7 @@
|
|||||||
@keyframes slide-left {
|
@keyframes slide-left {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(-10px);
|
transform: translateX(-50px);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
@ -61,7 +61,7 @@
|
|||||||
@keyframes slide-right {
|
@keyframes slide-right {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(-10px);
|
transform: translateX(50px);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
@ -73,7 +73,7 @@
|
|||||||
@keyframes slide-up {
|
@keyframes slide-up {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(10px);
|
transform: translateY(-50px);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
@ -85,3 +85,17 @@
|
|||||||
.z-popup {
|
.z-popup {
|
||||||
z-index: var(--popup-z-index);
|
z-index: var(--popup-z-index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes shrink {
|
||||||
|
0% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/icons",
|
"name": "@vben-core/icons",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -32,6 +32,7 @@ export {
|
|||||||
Grip,
|
Grip,
|
||||||
GripVertical,
|
GripVertical,
|
||||||
Menu as IconDefault,
|
Menu as IconDefault,
|
||||||
|
Inbox,
|
||||||
Info,
|
Info,
|
||||||
InspectionPanel,
|
InspectionPanel,
|
||||||
Languages,
|
Languages,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/shared",
|
"name": "@vben-core/shared",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -30,7 +30,7 @@ function openWindow(url: string, options: OpenWindowOptions = {}): void {
|
|||||||
function openRouteInNewWindow(path: string) {
|
function openRouteInNewWindow(path: string) {
|
||||||
const { hash, origin } = location;
|
const { hash, origin } = location;
|
||||||
const fullPath = path.startsWith('/') ? path : `/${path}`;
|
const fullPath = path.startsWith('/') ? path : `/${path}`;
|
||||||
const url = `${origin}${hash ? '/#' : ''}${fullPath}`;
|
const url = `${origin}${hash && !fullPath.startsWith('/#') ? '/#' : ''}${fullPath}`;
|
||||||
openWindow(url, { target: '_blank' });
|
openWindow(url, { target: '_blank' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/typings",
|
"name": "@vben-core/typings",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/composables",
|
"name": "@vben-core/composables",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -22,6 +22,7 @@ exports[`defaultPreferences immutability test > should not modify the config obj
|
|||||||
"enableCheckUpdates": true,
|
"enableCheckUpdates": true,
|
||||||
"enablePreferences": true,
|
"enablePreferences": true,
|
||||||
"enableRefreshToken": false,
|
"enableRefreshToken": false,
|
||||||
|
"enableStickyPreferencesNavigationBar": true,
|
||||||
"isMobile": false,
|
"isMobile": false,
|
||||||
"layout": "sidebar-nav",
|
"layout": "sidebar-nav",
|
||||||
"locale": "zh-CN",
|
"locale": "zh-CN",
|
||||||
@ -29,6 +30,7 @@ exports[`defaultPreferences immutability test > should not modify the config obj
|
|||||||
"name": "Vben Admin",
|
"name": "Vben Admin",
|
||||||
"preferencesButtonPosition": "auto",
|
"preferencesButtonPosition": "auto",
|
||||||
"watermark": false,
|
"watermark": false,
|
||||||
|
"watermarkContent": "",
|
||||||
"zIndex": 200,
|
"zIndex": 200,
|
||||||
},
|
},
|
||||||
"breadcrumb": {
|
"breadcrumb": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/preferences",
|
"name": "@vben-core/preferences",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { Preferences } from './types';
|
import type { Preferences } from "./types";
|
||||||
|
|
||||||
const defaultPreferences: Preferences = {
|
const defaultPreferences: Preferences = {
|
||||||
app: {
|
app: {
|
||||||
@ -22,6 +22,7 @@ const defaultPreferences: Preferences = {
|
|||||||
enableCheckUpdates: true,
|
enableCheckUpdates: true,
|
||||||
enablePreferences: true,
|
enablePreferences: true,
|
||||||
enableRefreshToken: false,
|
enableRefreshToken: false,
|
||||||
|
enableStickyPreferencesNavigationBar: true,
|
||||||
isMobile: false,
|
isMobile: false,
|
||||||
layout: 'sidebar-nav',
|
layout: 'sidebar-nav',
|
||||||
locale: 'zh-CN',
|
locale: 'zh-CN',
|
||||||
@ -29,7 +30,9 @@ const defaultPreferences: Preferences = {
|
|||||||
name: 'Vben Admin',
|
name: 'Vben Admin',
|
||||||
preferencesButtonPosition: 'auto',
|
preferencesButtonPosition: 'auto',
|
||||||
watermark: false,
|
watermark: false,
|
||||||
|
watermarkContent: '',
|
||||||
zIndex: 200,
|
zIndex: 200,
|
||||||
|
|
||||||
},
|
},
|
||||||
breadcrumb: {
|
breadcrumb: {
|
||||||
enable: true,
|
enable: true,
|
||||||
|
|||||||
@ -59,6 +59,10 @@ interface AppPreferences {
|
|||||||
* @zh_CN 是否开启refreshToken
|
* @zh_CN 是否开启refreshToken
|
||||||
*/
|
*/
|
||||||
enableRefreshToken: boolean;
|
enableRefreshToken: boolean;
|
||||||
|
/**
|
||||||
|
* @zh_CN 是否开启首选项导航栏吸顶效果
|
||||||
|
*/
|
||||||
|
enableStickyPreferencesNavigationBar: boolean;
|
||||||
/** 是否移动端 */
|
/** 是否移动端 */
|
||||||
isMobile: boolean;
|
isMobile: boolean;
|
||||||
/** 布局方式 */
|
/** 布局方式 */
|
||||||
@ -75,6 +79,10 @@ interface AppPreferences {
|
|||||||
* @zh_CN 是否开启水印
|
* @zh_CN 是否开启水印
|
||||||
*/
|
*/
|
||||||
watermark: boolean;
|
watermark: boolean;
|
||||||
|
/**
|
||||||
|
* @zh_CN 水印文案
|
||||||
|
*/
|
||||||
|
watermarkContent: string;
|
||||||
/** z-index */
|
/** z-index */
|
||||||
zIndex: number;
|
zIndex: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/form-ui",
|
"name": "@vben-core/form-ui",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -82,11 +82,11 @@ const actionWrapperClass = computed(() => {
|
|||||||
|
|
||||||
const cls = [
|
const cls = [
|
||||||
'flex',
|
'flex',
|
||||||
'w-full',
|
|
||||||
'items-center',
|
'items-center',
|
||||||
'gap-3',
|
'gap-3',
|
||||||
props.compact ? 'pb-2' : 'pb-4',
|
props.compact ? 'pb-2' : 'pb-4',
|
||||||
props.layout === 'vertical' ? 'self-end' : 'self-center',
|
props.layout === 'vertical' ? 'self-end' : 'self-center',
|
||||||
|
props.layout === 'inline' ? '' : 'w-full',
|
||||||
props.actionWrapperClass,
|
props.actionWrapperClass,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -342,13 +342,12 @@ export class FormApi {
|
|||||||
isObject(obj[key]) &&
|
isObject(obj[key]) &&
|
||||||
!isDayjsObject(obj[key]) &&
|
!isDayjsObject(obj[key]) &&
|
||||||
!isDate(obj[key])
|
!isDate(obj[key])
|
||||||
? fieldMergeFn(obj[key], value)
|
? fieldMergeFn(value, obj[key])
|
||||||
: value;
|
: value;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
const filteredFields = fieldMergeFn(fields, form.values);
|
const filteredFields = fieldMergeFn(fields, form.values);
|
||||||
this.handleStringToArrayFields(filteredFields);
|
|
||||||
form.setValues(filteredFields, shouldValidate);
|
form.setValues(filteredFields, shouldValidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,7 +357,6 @@ export class FormApi {
|
|||||||
const form = await this.getForm();
|
const form = await this.getForm();
|
||||||
await form.submitForm();
|
await form.submitForm();
|
||||||
const rawValues = toRaw(await this.getValues());
|
const rawValues = toRaw(await this.getValues());
|
||||||
this.handleArrayToStringFields(rawValues);
|
|
||||||
await this.state?.handleSubmit?.(rawValues);
|
await this.state?.handleSubmit?.(rawValues);
|
||||||
|
|
||||||
return rawValues;
|
return rawValues;
|
||||||
@ -458,16 +456,31 @@ export class FormApi {
|
|||||||
return this.form;
|
return this.form;
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleArrayToStringFields = (originValues: Record<string, any>) => {
|
private handleMultiFields = (originValues: Record<string, any>) => {
|
||||||
const arrayToStringFields = this.state?.arrayToStringFields;
|
const arrayToStringFields = this.state?.arrayToStringFields;
|
||||||
if (!arrayToStringFields || !Array.isArray(arrayToStringFields)) {
|
if (!arrayToStringFields || !Array.isArray(arrayToStringFields)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const processFields = (fields: string[], separator: string = ',') => {
|
const processFields = (fields: string[], separator: string = ',') => {
|
||||||
this.processFields(fields, separator, originValues, (value, sep) =>
|
this.processFields(fields, separator, originValues, (value, sep) => {
|
||||||
Array.isArray(value) ? value.join(sep) : value,
|
if (Array.isArray(value)) {
|
||||||
);
|
return value.join(sep);
|
||||||
|
} else if (typeof value === 'string') {
|
||||||
|
// 处理空字符串的情况
|
||||||
|
if (value === '') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
// 处理复杂分隔符的情况
|
||||||
|
const escapedSeparator = sep.replaceAll(
|
||||||
|
/[.*+?^${}()|[\]\\]/g,
|
||||||
|
String.raw`\$&`,
|
||||||
|
);
|
||||||
|
return value.split(new RegExp(escapedSeparator));
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理简单数组格式 ['field1', 'field2', ';'] 或 ['field1', 'field2']
|
// 处理简单数组格式 ['field1', 'field2', ';'] 或 ['field1', 'field2']
|
||||||
@ -503,8 +516,7 @@ export class FormApi {
|
|||||||
const values = { ...originValues };
|
const values = { ...originValues };
|
||||||
const fieldMappingTime = this.state?.fieldMappingTime;
|
const fieldMappingTime = this.state?.fieldMappingTime;
|
||||||
|
|
||||||
this.handleStringToArrayFields(values);
|
this.handleMultiFields(values);
|
||||||
|
|
||||||
if (!fieldMappingTime || !Array.isArray(fieldMappingTime)) {
|
if (!fieldMappingTime || !Array.isArray(fieldMappingTime)) {
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
@ -550,65 +562,6 @@ export class FormApi {
|
|||||||
return values;
|
return values;
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleStringToArrayFields = (originValues: Record<string, any>) => {
|
|
||||||
const arrayToStringFields = this.state?.arrayToStringFields;
|
|
||||||
if (!arrayToStringFields || !Array.isArray(arrayToStringFields)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const processFields = (fields: string[], separator: string = ',') => {
|
|
||||||
this.processFields(fields, separator, originValues, (value, sep) => {
|
|
||||||
if (typeof value !== 'string') {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
// 处理空字符串的情况
|
|
||||||
if (value === '') {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
// 处理复杂分隔符的情况
|
|
||||||
const escapedSeparator = sep.replaceAll(
|
|
||||||
/[.*+?^${}()|[\]\\]/g,
|
|
||||||
String.raw`\$&`,
|
|
||||||
);
|
|
||||||
return value.split(new RegExp(escapedSeparator));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理简单数组格式 ['field1', 'field2', ';'] 或 ['field1', 'field2']
|
|
||||||
if (arrayToStringFields.every((item) => typeof item === 'string')) {
|
|
||||||
const lastItem =
|
|
||||||
arrayToStringFields[arrayToStringFields.length - 1] || '';
|
|
||||||
const fields =
|
|
||||||
lastItem.length === 1
|
|
||||||
? arrayToStringFields.slice(0, -1)
|
|
||||||
: arrayToStringFields;
|
|
||||||
const separator = lastItem.length === 1 ? lastItem : ',';
|
|
||||||
processFields(fields, separator);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理嵌套数组格式 [['field1'], ';']
|
|
||||||
arrayToStringFields.forEach((fieldConfig) => {
|
|
||||||
if (Array.isArray(fieldConfig)) {
|
|
||||||
const [fields, separator = ','] = fieldConfig;
|
|
||||||
if (Array.isArray(fields)) {
|
|
||||||
processFields(fields, separator);
|
|
||||||
} else if (typeof originValues[fields] === 'string') {
|
|
||||||
const value = originValues[fields];
|
|
||||||
if (value === '') {
|
|
||||||
originValues[fields] = [];
|
|
||||||
} else {
|
|
||||||
const escapedSeparator = separator.replaceAll(
|
|
||||||
/[.*+?^${}()|[\]\\]/g,
|
|
||||||
String.raw`\$&`,
|
|
||||||
);
|
|
||||||
originValues[fields] = value.split(new RegExp(escapedSeparator));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
private processFields = (
|
private processFields = (
|
||||||
fields: string[],
|
fields: string[],
|
||||||
separator: string,
|
separator: string,
|
||||||
|
|||||||
@ -41,6 +41,7 @@ const {
|
|||||||
emptyStateValue,
|
emptyStateValue,
|
||||||
fieldName,
|
fieldName,
|
||||||
formFieldProps,
|
formFieldProps,
|
||||||
|
hide,
|
||||||
label,
|
label,
|
||||||
labelClass,
|
labelClass,
|
||||||
labelWidth,
|
labelWidth,
|
||||||
@ -59,7 +60,7 @@ const values = useFormValues();
|
|||||||
const errors = useFieldError(fieldName);
|
const errors = useFieldError(fieldName);
|
||||||
const fieldComponentRef = useTemplateRef<HTMLInputElement>('fieldComponentRef');
|
const fieldComponentRef = useTemplateRef<HTMLInputElement>('fieldComponentRef');
|
||||||
const formApi = formRenderProps.form;
|
const formApi = formRenderProps.form;
|
||||||
const compact = formRenderProps.compact;
|
const compact = computed(() => formRenderProps.compact);
|
||||||
const isInValid = computed(() => errors.value?.length > 0);
|
const isInValid = computed(() => errors.value?.length > 0);
|
||||||
|
|
||||||
const FieldComponent = computed(() => {
|
const FieldComponent = computed(() => {
|
||||||
@ -95,7 +96,7 @@ const currentRules = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const visible = computed(() => {
|
const visible = computed(() => {
|
||||||
return isIf.value && isShow.value;
|
return !hide && isIf.value && isShow.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
const shouldRequired = computed(() => {
|
const shouldRequired = computed(() => {
|
||||||
@ -283,7 +284,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FormField
|
<FormField
|
||||||
v-if="isIf"
|
v-if="!hide && isIf"
|
||||||
v-bind="fieldProps"
|
v-bind="fieldProps"
|
||||||
v-slot="slotProps"
|
v-slot="slotProps"
|
||||||
:name="fieldName"
|
:name="fieldName"
|
||||||
|
|||||||
@ -42,11 +42,11 @@ const emits = defineEmits<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const wrapperClass = computed(() => {
|
const wrapperClass = computed(() => {
|
||||||
const cls = ['flex flex-col'];
|
const cls = ['flex'];
|
||||||
if (props.layout === 'vertical') {
|
if (props.layout === 'inline') {
|
||||||
cls.push(props.compact ? 'gap-x-2' : 'gap-x-4');
|
cls.push('flex-wrap gap-x-2');
|
||||||
} else {
|
} else {
|
||||||
cls.push('gap-2');
|
cls.push(props.compact ? 'gap-x-2' : 'gap-x-4', 'flex-col grid');
|
||||||
}
|
}
|
||||||
return cn(...cls, props.wrapperClass);
|
return cn(...cls, props.wrapperClass);
|
||||||
});
|
});
|
||||||
@ -170,7 +170,7 @@ const computedSchema = computed(
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<component :is="formComponent" v-bind="formComponentProps">
|
<component :is="formComponent" v-bind="formComponentProps">
|
||||||
<div ref="wrapperRef" :class="wrapperClass" class="grid">
|
<div ref="wrapperRef" :class="wrapperClass">
|
||||||
<template v-for="cSchema in computedSchema" :key="cSchema.fieldName">
|
<template v-for="cSchema in computedSchema" :key="cSchema.fieldName">
|
||||||
<!-- <div v-if="$slots[cSchema.fieldName]" :class="cSchema.formItemClass">
|
<!-- <div v-if="$slots[cSchema.fieldName]" :class="cSchema.formItemClass">
|
||||||
<slot :definition="cSchema" :name="cSchema.fieldName"> </slot>
|
<slot :definition="cSchema" :name="cSchema.fieldName"> </slot>
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import type { ClassType, MaybeComputedRef } from '@vben-core/typings';
|
|||||||
|
|
||||||
import type { FormApi } from './form-api';
|
import type { FormApi } from './form-api';
|
||||||
|
|
||||||
export type FormLayout = 'horizontal' | 'vertical';
|
export type FormLayout = 'horizontal' | 'inline' | 'vertical';
|
||||||
|
|
||||||
export type BaseFormComponentType =
|
export type BaseFormComponentType =
|
||||||
| 'DefaultButton'
|
| 'DefaultButton'
|
||||||
@ -255,6 +255,8 @@ export interface FormSchema<
|
|||||||
fieldName: string;
|
fieldName: string;
|
||||||
/** 帮助信息 */
|
/** 帮助信息 */
|
||||||
help?: CustomRenderType;
|
help?: CustomRenderType;
|
||||||
|
/** 是否隐藏表单项 */
|
||||||
|
hide?: boolean;
|
||||||
/** 表单项 */
|
/** 表单项 */
|
||||||
label?: CustomRenderType;
|
label?: CustomRenderType;
|
||||||
// 自定义组件内部渲染
|
// 自定义组件内部渲染
|
||||||
@ -277,7 +279,8 @@ export interface FormRenderProps<
|
|||||||
*/
|
*/
|
||||||
arrayToStringFields?: ArrayToStringFields;
|
arrayToStringFields?: ArrayToStringFields;
|
||||||
/**
|
/**
|
||||||
* 是否展开,在showCollapseButton=true下生效
|
* 是否折叠,在showCollapseButton=true下生效
|
||||||
|
* true:折叠 false:展开
|
||||||
*/
|
*/
|
||||||
collapsed?: boolean;
|
collapsed?: boolean;
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/layout-ui",
|
"name": "@vben-core/layout-ui",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {
|
|||||||
useLayoutFooterStyle,
|
useLayoutFooterStyle,
|
||||||
useLayoutHeaderStyle,
|
useLayoutHeaderStyle,
|
||||||
} from '@vben-core/composables';
|
} from '@vben-core/composables';
|
||||||
import { Menu } from '@vben-core/icons';
|
import { IconifyIcon } from '@vben-core/icons';
|
||||||
import { VbenIconButton } from '@vben-core/shadcn-ui';
|
import { VbenIconButton } from '@vben-core/shadcn-ui';
|
||||||
import { ELEMENT_ID_MAIN_CONTENT } from '@vben-core/shared/constants';
|
import { ELEMENT_ID_MAIN_CONTENT } from '@vben-core/shared/constants';
|
||||||
|
|
||||||
@ -559,7 +559,8 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
|
|||||||
class="my-0 mr-1 rounded-md"
|
class="my-0 mr-1 rounded-md"
|
||||||
@click="handleHeaderToggle"
|
@click="handleHeaderToggle"
|
||||||
>
|
>
|
||||||
<Menu class="size-4" />
|
<IconifyIcon v-if="showSidebar" icon="ep:fold" />
|
||||||
|
<IconifyIcon v-else icon="ep:expand" />
|
||||||
</VbenIconButton>
|
</VbenIconButton>
|
||||||
</template>
|
</template>
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/menu-ui",
|
"name": "@vben-core/menu-ui",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -107,7 +107,6 @@ export class ModalApi {
|
|||||||
this.store.setState((prev) => ({
|
this.store.setState((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
submitting: false,
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,7 +161,11 @@ export class ModalApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open() {
|
open() {
|
||||||
this.store.setState((prev) => ({ ...prev, isOpen: true }));
|
this.store.setState((prev) => ({
|
||||||
|
...prev,
|
||||||
|
isOpen: true,
|
||||||
|
submitting: false,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
setData<T>(payload: T) {
|
setData<T>(payload: T) {
|
||||||
|
|||||||
@ -180,7 +180,7 @@ function escapeKeyDown(e: KeyboardEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handerOpenAutoFocus(e: Event) {
|
function handleOpenAutoFocus(e: Event) {
|
||||||
if (!openAutoFocus.value) {
|
if (!openAutoFocus.value) {
|
||||||
e?.preventDefault();
|
e?.preventDefault();
|
||||||
}
|
}
|
||||||
@ -209,6 +209,12 @@ const getForceMount = computed(() => {
|
|||||||
return !unref(destroyOnClose) && unref(firstOpened);
|
return !unref(destroyOnClose) && unref(firstOpened);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleOpened = () => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
props.modalApi?.onOpened();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
function handleClosed() {
|
function handleClosed() {
|
||||||
isClosed.value = true;
|
isClosed.value = true;
|
||||||
props.modalApi?.onClosed();
|
props.modalApi?.onClosed();
|
||||||
@ -253,8 +259,8 @@ function handleClosed() {
|
|||||||
@escape-key-down="escapeKeyDown"
|
@escape-key-down="escapeKeyDown"
|
||||||
@focus-outside="handleFocusOutside"
|
@focus-outside="handleFocusOutside"
|
||||||
@interact-outside="interactOutside"
|
@interact-outside="interactOutside"
|
||||||
@open-auto-focus="handerOpenAutoFocus"
|
@open-auto-focus="handleOpenAutoFocus"
|
||||||
@opened="() => modalApi?.onOpened()"
|
@opened="handleOpened"
|
||||||
@pointer-down-outside="pointerDownOutside"
|
@pointer-down-outside="pointerDownOutside"
|
||||||
>
|
>
|
||||||
<DialogHeader
|
<DialogHeader
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/shadcn-ui",
|
"name": "@vben-core/shadcn-ui",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"#main": "./dist/index.mjs",
|
"#main": "./dist/index.mjs",
|
||||||
"#module": "./dist/index.mjs",
|
"#module": "./dist/index.mjs",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
|
|||||||
@ -21,7 +21,10 @@ isFullscreen.value = !!(
|
|||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<VbenIconButton @click="toggle">
|
<VbenIconButton
|
||||||
|
class="hover:animate-[shrink_0.3s_ease-in-out]"
|
||||||
|
@click="toggle"
|
||||||
|
>
|
||||||
<Minimize v-if="isFullscreen" class="text-foreground size-4" />
|
<Minimize v-if="isFullscreen" class="text-foreground size-4" />
|
||||||
<Maximize v-else class="text-foreground size-4" />
|
<Maximize v-else class="text-foreground size-4" />
|
||||||
</VbenIconButton>
|
</VbenIconButton>
|
||||||
|
|||||||
@ -59,9 +59,9 @@ function handleComplete(e: string[]) {
|
|||||||
async function handleSend(e: Event) {
|
async function handleSend(e: Event) {
|
||||||
try {
|
try {
|
||||||
e?.preventDefault();
|
e?.preventDefault();
|
||||||
await handleSendCode();
|
|
||||||
countdown.value = maxTime;
|
countdown.value = maxTime;
|
||||||
startCountdown();
|
startCountdown();
|
||||||
|
await handleSendCode();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to send code:', error);
|
console.error('Failed to send code:', error);
|
||||||
// Consider emitting an error event or showing a notification
|
// Consider emitting an error event or showing a notification
|
||||||
|
|||||||
@ -35,16 +35,24 @@ const tabsIndicatorStyle = computed(() => {
|
|||||||
width: `${(100 / props.tabs.length).toFixed(0)}%`,
|
width: `${(100 / props.tabs.length).toFixed(0)}%`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function activeClass(tab: string): string[] {
|
||||||
|
return tab === activeTab.value ? ['!font-bold', 'text-primary'] : [];
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Tabs v-model="activeTab" :default-value="getDefaultValue">
|
<Tabs v-model="activeTab" :default-value="getDefaultValue">
|
||||||
<TabsList :style="tabsStyle" class="bg-accent relative grid w-full">
|
<TabsList
|
||||||
|
:style="tabsStyle"
|
||||||
|
class="bg-accent !outline-heavy relative grid w-full !outline !outline-2"
|
||||||
|
>
|
||||||
<TabsIndicator :style="tabsIndicatorStyle" />
|
<TabsIndicator :style="tabsIndicatorStyle" />
|
||||||
<template v-for="tab in tabs" :key="tab.value">
|
<template v-for="tab in tabs" :key="tab.value">
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
:value="tab.value"
|
:value="tab.value"
|
||||||
class="z-20 inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium disabled:pointer-events-none disabled:opacity-50"
|
:class="activeClass(tab.value)"
|
||||||
|
class="hover:text-primary z-20 inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium disabled:pointer-events-none disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{{ tab.label }}
|
{{ tab.label }}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
|
|||||||
@ -23,7 +23,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|||||||
v-bind="forwardedProps"
|
v-bind="forwardedProps"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'absolute bottom-0 left-0 z-10 h-full w-1/2 translate-x-[--radix-tabs-indicator-position] rounded-full px-0 py-1 pr-1 transition-[width,transform] duration-300',
|
'absolute bottom-0 left-0 z-10 h-full w-1/2 translate-x-[--radix-tabs-indicator-position] rounded-full px-0 py-1 pr-0.5 transition-[width,transform] duration-300',
|
||||||
props.class,
|
props.class,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@ -31,7 +31,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||||||
v-bind="forwarded"
|
v-bind="forwarded"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'focus-visible:ring-ring data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground border-border peer h-4 w-4 shrink-0 rounded-sm border transition focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50',
|
'focus-visible:ring-ring data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground border-border hover:border-primary peer h-4 w-4 shrink-0 rounded-sm border transition focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50',
|
||||||
props.class,
|
props.class,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@ -8,12 +8,7 @@ import { computed, ref } from 'vue';
|
|||||||
import { cn } from '@vben-core/shared/utils';
|
import { cn } from '@vben-core/shared/utils';
|
||||||
|
|
||||||
import { X } from 'lucide-vue-next';
|
import { X } from 'lucide-vue-next';
|
||||||
import {
|
import { DialogClose, DialogContent, useForwardPropsEmits } from 'radix-vue';
|
||||||
DialogClose,
|
|
||||||
DialogContent,
|
|
||||||
DialogPortal,
|
|
||||||
useForwardPropsEmits,
|
|
||||||
} from 'radix-vue';
|
|
||||||
|
|
||||||
import DialogOverlay from './DialogOverlay.vue';
|
import DialogOverlay from './DialogOverlay.vue';
|
||||||
|
|
||||||
@ -87,7 +82,7 @@ defineExpose({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<DialogPortal :to="appendTo">
|
<Teleport defer :to="appendTo">
|
||||||
<Transition name="fade">
|
<Transition name="fade">
|
||||||
<DialogOverlay
|
<DialogOverlay
|
||||||
v-if="open && modal"
|
v-if="open && modal"
|
||||||
@ -132,5 +127,5 @@ defineExpose({
|
|||||||
<X class="h-4 w-4" />
|
<X class="h-4 w-4" />
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</DialogPortal>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -24,7 +24,7 @@ const modelValue = useVModel(props, 'modelValue', emits, {
|
|||||||
v-model="modelValue"
|
v-model="modelValue"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50',
|
'border-input bg-background ring-offset-background placeholder:text-muted-foreground/50 focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50',
|
||||||
props.class,
|
props.class,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { computed, ref } from 'vue';
|
|||||||
|
|
||||||
import { cn } from '@vben-core/shared/utils';
|
import { cn } from '@vben-core/shared/utils';
|
||||||
|
|
||||||
import { DialogContent, DialogPortal, useForwardPropsEmits } from 'radix-vue';
|
import { DialogContent, useForwardPropsEmits } from 'radix-vue';
|
||||||
|
|
||||||
import { sheetVariants } from './sheet';
|
import { sheetVariants } from './sheet';
|
||||||
import SheetOverlay from './SheetOverlay.vue';
|
import SheetOverlay from './SheetOverlay.vue';
|
||||||
@ -73,7 +73,7 @@ function onAnimationEnd(event: AnimationEvent) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<DialogPortal :to="appendTo">
|
<Teleport defer :to="appendTo">
|
||||||
<Transition name="fade">
|
<Transition name="fade">
|
||||||
<SheetOverlay
|
<SheetOverlay
|
||||||
v-if="open && modal"
|
v-if="open && modal"
|
||||||
@ -103,5 +103,5 @@ function onAnimationEnd(event: AnimationEvent) {
|
|||||||
<Cross2Icon class="h-5 w-" />
|
<Cross2Icon class="h-5 w-" />
|
||||||
</DialogClose> -->
|
</DialogClose> -->
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</DialogPortal>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -21,7 +21,7 @@ const delegatedProps = computed(() => {
|
|||||||
v-bind="delegatedProps"
|
v-bind="delegatedProps"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'bg-muted text-muted-foreground inline-flex h-9 items-center justify-center rounded-lg p-1',
|
'bg-muted text-muted-foreground inline-flex h-9 items-center justify-center rounded-md p-1',
|
||||||
props.class,
|
props.class,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@ -1,2 +1,4 @@
|
|||||||
export { default as VbenTree } from './tree.vue';
|
export { default as VbenTree } from './tree.vue';
|
||||||
|
export type { TreeProps } from './types';
|
||||||
|
export { treePropsDefaults } from './types';
|
||||||
export type { FlattenedItem } from 'radix-vue';
|
export type { FlattenedItem } from 'radix-vue';
|
||||||
|
|||||||
@ -14,25 +14,9 @@ import { cn, get } from '@vben-core/shared/utils';
|
|||||||
import { TreeItem, TreeRoot } from 'radix-vue';
|
import { TreeItem, TreeRoot } from 'radix-vue';
|
||||||
|
|
||||||
import { Checkbox } from '../checkbox';
|
import { Checkbox } from '../checkbox';
|
||||||
|
import { treePropsDefaults } from './types';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<TreeProps>(), {
|
const props = withDefaults(defineProps<TreeProps>(), treePropsDefaults());
|
||||||
allowClear: false,
|
|
||||||
autoCheckParent: true,
|
|
||||||
bordered: false,
|
|
||||||
checkStrictly: false,
|
|
||||||
defaultExpandedKeys: () => [],
|
|
||||||
defaultExpandedLevel: 0,
|
|
||||||
disabled: false,
|
|
||||||
disabledField: 'disabled',
|
|
||||||
expanded: () => [],
|
|
||||||
iconField: 'icon',
|
|
||||||
labelField: 'label',
|
|
||||||
multiple: false,
|
|
||||||
showIcon: true,
|
|
||||||
transition: true,
|
|
||||||
valueField: 'value',
|
|
||||||
childrenField: 'children',
|
|
||||||
});
|
|
||||||
|
|
||||||
const emits = defineEmits<{
|
const emits = defineEmits<{
|
||||||
expand: [value: FlattenedItem<Recordable<any>>];
|
expand: [value: FlattenedItem<Recordable<any>>];
|
||||||
@ -41,7 +25,9 @@ const emits = defineEmits<{
|
|||||||
|
|
||||||
interface InnerFlattenItem<T = Recordable<any>, P = number | string> {
|
interface InnerFlattenItem<T = Recordable<any>, P = number | string> {
|
||||||
hasChildren: boolean;
|
hasChildren: boolean;
|
||||||
|
id: P;
|
||||||
level: number;
|
level: number;
|
||||||
|
parentId: null | P;
|
||||||
parents: P[];
|
parents: P[];
|
||||||
value: T;
|
value: T;
|
||||||
}
|
}
|
||||||
@ -50,24 +36,25 @@ function flatten<T = Recordable<any>, P = number | string>(
|
|||||||
items: T[],
|
items: T[],
|
||||||
childrenField: string = 'children',
|
childrenField: string = 'children',
|
||||||
level = 0,
|
level = 0,
|
||||||
|
parentId: null | P = null,
|
||||||
parents: P[] = [],
|
parents: P[] = [],
|
||||||
): InnerFlattenItem<T, P>[] {
|
): InnerFlattenItem<T, P>[] {
|
||||||
const result: InnerFlattenItem<T, P>[] = [];
|
const result: InnerFlattenItem<T, P>[] = [];
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
const children = get(item, childrenField) as Array<T>;
|
const children = get(item, childrenField) as Array<T>;
|
||||||
const val = {
|
const id = get(item, props.valueField) as P;
|
||||||
|
const val: InnerFlattenItem<T, P> = {
|
||||||
hasChildren: Array.isArray(children) && children.length > 0,
|
hasChildren: Array.isArray(children) && children.length > 0,
|
||||||
|
id,
|
||||||
level,
|
level,
|
||||||
|
parentId,
|
||||||
parents: [...parents],
|
parents: [...parents],
|
||||||
value: item,
|
value: item,
|
||||||
};
|
};
|
||||||
result.push(val);
|
result.push(val);
|
||||||
if (val.hasChildren)
|
if (val.hasChildren)
|
||||||
result.push(
|
result.push(
|
||||||
...flatten(children, childrenField, level + 1, [
|
...flatten(children, childrenField, level + 1, id, [...parents, id]),
|
||||||
...parents,
|
|
||||||
get(item, props.valueField),
|
|
||||||
]),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
@ -171,6 +158,24 @@ function collapseAll() {
|
|||||||
expanded.value = [];
|
expanded.value = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkAll() {
|
||||||
|
if (!props.multiple) return;
|
||||||
|
modelValue.value = [
|
||||||
|
...new Set(
|
||||||
|
flattenData.value
|
||||||
|
.filter((item) => !get(item.value, props.disabledField))
|
||||||
|
.map((item) => get(item.value, props.valueField)),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
updateTreeValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
function unCheckAll() {
|
||||||
|
if (!props.multiple) return;
|
||||||
|
modelValue.value = [];
|
||||||
|
updateTreeValue();
|
||||||
|
}
|
||||||
|
|
||||||
function isNodeDisabled(item: FlattenedItem<Recordable<any>>) {
|
function isNodeDisabled(item: FlattenedItem<Recordable<any>>) {
|
||||||
return props.disabled || get(item.value, props.disabledField);
|
return props.disabled || get(item.value, props.disabledField);
|
||||||
}
|
}
|
||||||
@ -195,12 +200,51 @@ function onSelect(item: FlattenedItem<Recordable<any>>, isSelected: boolean) {
|
|||||||
get(i.value, props.valueField) === get(item.value, props.valueField)
|
get(i.value, props.valueField) === get(item.value, props.valueField)
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
?.parents?.forEach((p) => {
|
?.parents?.filter((item) => !get(item, props.disabledField))
|
||||||
|
?.forEach((p) => {
|
||||||
if (Array.isArray(modelValue.value) && !modelValue.value.includes(p)) {
|
if (Array.isArray(modelValue.value) && !modelValue.value.includes(p)) {
|
||||||
modelValue.value.push(p);
|
modelValue.value.push(p);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
!props.checkStrictly &&
|
||||||
|
props.multiple &&
|
||||||
|
props.autoCheckParent &&
|
||||||
|
!isSelected
|
||||||
|
) {
|
||||||
|
flattenData.value
|
||||||
|
.find((i) => {
|
||||||
|
return (
|
||||||
|
get(i.value, props.valueField) === get(item.value, props.valueField)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
?.parents?.filter((item) => !get(item, props.disabledField))
|
||||||
|
?.reverse()
|
||||||
|
.forEach((p) => {
|
||||||
|
const children = flattenData.value.filter((i) => {
|
||||||
|
return (
|
||||||
|
i.parents.length > 0 &&
|
||||||
|
i.parents.includes(p) &&
|
||||||
|
i.id !== item._id &&
|
||||||
|
i.parentId === p
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (Array.isArray(modelValue.value)) {
|
||||||
|
const hasSelectedChild = children.some((child) =>
|
||||||
|
(modelValue.value as unknown[]).includes(
|
||||||
|
get(child.value, props.valueField),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (!hasSelectedChild) {
|
||||||
|
const index = modelValue.value.indexOf(p);
|
||||||
|
if (index !== -1) {
|
||||||
|
modelValue.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
updateTreeValue();
|
updateTreeValue();
|
||||||
emits('select', item);
|
emits('select', item);
|
||||||
}
|
}
|
||||||
@ -210,6 +254,8 @@ defineExpose({
|
|||||||
collapseNodes,
|
collapseNodes,
|
||||||
expandAll,
|
expandAll,
|
||||||
expandNodes,
|
expandNodes,
|
||||||
|
checkAll,
|
||||||
|
unCheckAll,
|
||||||
expandToLevel,
|
expandToLevel,
|
||||||
getItemByValue,
|
getItemByValue,
|
||||||
});
|
});
|
||||||
@ -230,15 +276,41 @@ defineExpose({
|
|||||||
v-slot="{ flattenItems }"
|
v-slot="{ flattenItems }"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'text-blackA11 container select-none list-none rounded-lg p-2 text-sm font-medium',
|
'text-blackA11 container select-none list-none rounded-lg text-sm font-medium',
|
||||||
$attrs.class as unknown as ClassType,
|
$attrs.class as unknown as ClassType,
|
||||||
bordered ? 'border' : '',
|
bordered ? 'border' : '',
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div class="w-full" v-if="$slots.header">
|
<div
|
||||||
|
:class="
|
||||||
|
cn('my-0.5 flex w-full items-center p-1', bordered ? 'border-b' : '')
|
||||||
|
"
|
||||||
|
v-if="$slots.header"
|
||||||
|
>
|
||||||
<slot name="header"> </slot>
|
<slot name="header"> </slot>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
:class="
|
||||||
|
cn('my-0.5 flex w-full items-center p-1', bordered ? 'border-b' : '')
|
||||||
|
"
|
||||||
|
v-if="treeData.length > 0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex size-5 flex-1 cursor-pointer items-center"
|
||||||
|
@click="() => (expanded?.length > 0 ? collapseAll() : expandAll())"
|
||||||
|
>
|
||||||
|
<ChevronRight
|
||||||
|
:class="{ 'rotate-90': expanded?.length > 0 }"
|
||||||
|
class="text-foreground/80 hover:text-foreground size-4 cursor-pointer transition"
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
v-if="multiple"
|
||||||
|
@click.stop
|
||||||
|
@update:checked="(checked) => (checked ? checkAll() : unCheckAll())"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<TransitionGroup :name="transition ? 'fade' : ''">
|
<TransitionGroup :name="transition ? 'fade' : ''">
|
||||||
<TreeItem
|
<TreeItem
|
||||||
v-for="item in flattenItems"
|
v-for="item in flattenItems"
|
||||||
@ -250,11 +322,11 @@ defineExpose({
|
|||||||
handleToggle,
|
handleToggle,
|
||||||
}"
|
}"
|
||||||
:key="item._id"
|
:key="item._id"
|
||||||
:style="{ 'padding-left': `${item.level - 0.5}rem` }"
|
:style="{ 'margin-left': `${item.level - 1}rem` }"
|
||||||
:class="
|
:class="
|
||||||
cn('cursor-pointer', getNodeClass?.(item), {
|
cn('cursor-pointer', getNodeClass?.(item), {
|
||||||
'data-[selected]:bg-accent': !multiple,
|
'data-[selected]:bg-accent': !multiple,
|
||||||
'cursor-not-allowed': isNodeDisabled(item),
|
'text-foreground/50 cursor-not-allowed': isNodeDisabled(item),
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
v-bind="
|
v-bind="
|
||||||
@ -284,7 +356,7 @@ defineExpose({
|
|||||||
!isNodeDisabled(item) && onToggle(item);
|
!isNodeDisabled(item) && onToggle(item);
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
class="tree-node focus:ring-grass8 my-0.5 flex items-center rounded px-2 py-1 outline-none focus:ring-2"
|
class="tree-node focus:ring-grass8 my-0.5 flex items-center rounded p-1 outline-none focus:ring-2"
|
||||||
>
|
>
|
||||||
<ChevronRight
|
<ChevronRight
|
||||||
v-if="
|
v-if="
|
||||||
@ -292,7 +364,7 @@ defineExpose({
|
|||||||
Array.isArray(item.value[childrenField]) &&
|
Array.isArray(item.value[childrenField]) &&
|
||||||
item.value[childrenField].length > 0
|
item.value[childrenField].length > 0
|
||||||
"
|
"
|
||||||
class="size-4 cursor-pointer transition"
|
class="text-foreground/80 hover:text-foreground size-4 cursor-pointer transition"
|
||||||
:class="{ 'rotate-90': isExpanded }"
|
:class="{ 'rotate-90': isExpanded }"
|
||||||
@click.stop="
|
@click.stop="
|
||||||
() => {
|
() => {
|
||||||
@ -301,52 +373,56 @@ defineExpose({
|
|||||||
}
|
}
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<div v-else class="h-4 w-4">
|
<div v-else class="h-4 w-4"></div>
|
||||||
<!-- <IconifyIcon v-if="item.value.icon" :icon="item.value.icon" /> -->
|
<div class="flex items-center gap-1">
|
||||||
</div>
|
<Checkbox
|
||||||
<Checkbox
|
v-if="multiple"
|
||||||
v-if="multiple"
|
:checked="isSelected && !isNodeDisabled(item)"
|
||||||
:checked="isSelected && !isNodeDisabled(item)"
|
:disabled="isNodeDisabled(item)"
|
||||||
:disabled="isNodeDisabled(item)"
|
:indeterminate="isIndeterminate && !isNodeDisabled(item)"
|
||||||
:indeterminate="isIndeterminate && !isNodeDisabled(item)"
|
@click="
|
||||||
@click="
|
(event: MouseEvent) => {
|
||||||
(event: MouseEvent) => {
|
if (isNodeDisabled(item)) {
|
||||||
if (isNodeDisabled(item)) {
|
event.preventDefault();
|
||||||
event.preventDefault();
|
event.stopPropagation();
|
||||||
event.stopPropagation();
|
return;
|
||||||
return;
|
}
|
||||||
|
handleSelect();
|
||||||
}
|
}
|
||||||
handleSelect();
|
"
|
||||||
}
|
/>
|
||||||
"
|
<div
|
||||||
/>
|
class="flex items-center gap-1"
|
||||||
<div
|
@click="
|
||||||
class="flex items-center gap-1 pl-2"
|
(event: MouseEvent) => {
|
||||||
@click="
|
if (isNodeDisabled(item)) {
|
||||||
(event: MouseEvent) => {
|
event.preventDefault();
|
||||||
if (isNodeDisabled(item)) {
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
return;
|
||||||
event.stopPropagation();
|
}
|
||||||
return;
|
handleSelect();
|
||||||
}
|
}
|
||||||
event.stopPropagation();
|
"
|
||||||
event.preventDefault();
|
>
|
||||||
handleSelect();
|
<slot name="node" v-bind="item">
|
||||||
}
|
<IconifyIcon
|
||||||
"
|
class="size-4"
|
||||||
>
|
v-if="showIcon && get(item.value, iconField)"
|
||||||
<slot name="node" v-bind="item">
|
:icon="get(item.value, iconField)"
|
||||||
<IconifyIcon
|
/>
|
||||||
class="size-4"
|
{{ get(item.value, labelField) }}
|
||||||
v-if="showIcon && get(item.value, iconField)"
|
</slot>
|
||||||
:icon="get(item.value, iconField)"
|
</div>
|
||||||
/>
|
|
||||||
{{ get(item.value, labelField) }}
|
|
||||||
</slot>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="h-4 w-4"></div>
|
||||||
</TreeItem>
|
</TreeItem>
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
<div class="w-full" v-if="$slots.footer">
|
<div
|
||||||
|
:class="
|
||||||
|
cn('my-0.5 flex w-full items-center p-1', bordered ? 'border-t' : '')
|
||||||
|
"
|
||||||
|
v-if="$slots.footer"
|
||||||
|
>
|
||||||
<slot name="footer"> </slot>
|
<slot name="footer"> </slot>
|
||||||
</div>
|
</div>
|
||||||
</TreeRoot>
|
</TreeRoot>
|
||||||
|
|||||||
@ -40,3 +40,23 @@ export interface TreeProps {
|
|||||||
/** 值字段 */
|
/** 值字段 */
|
||||||
valueField?: string;
|
valueField?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function treePropsDefaults() {
|
||||||
|
return {
|
||||||
|
allowClear: false,
|
||||||
|
autoCheckParent: true,
|
||||||
|
bordered: false,
|
||||||
|
checkStrictly: false,
|
||||||
|
defaultExpandedKeys: () => [],
|
||||||
|
defaultExpandedLevel: 0,
|
||||||
|
disabled: false,
|
||||||
|
disabledField: 'disabled',
|
||||||
|
iconField: 'icon',
|
||||||
|
labelField: 'label',
|
||||||
|
multiple: false,
|
||||||
|
showIcon: true,
|
||||||
|
transition: true,
|
||||||
|
valueField: 'value',
|
||||||
|
childrenField: 'children',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben-core/tabs-ui",
|
"name": "@vben-core/tabs-ui",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/constants",
|
"name": "@vben/constants",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/access",
|
"name": "@vben/access",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/common-ui",
|
"name": "@vben/common-ui",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -9,6 +9,7 @@ export * from './loading';
|
|||||||
export * from './page';
|
export * from './page';
|
||||||
export * from './resize';
|
export * from './resize';
|
||||||
export * from './tippy';
|
export * from './tippy';
|
||||||
|
export * from './tree';
|
||||||
export * from '@vben-core/form-ui';
|
export * from '@vben-core/form-ui';
|
||||||
export * from '@vben-core/popup-ui';
|
export * from '@vben-core/popup-ui';
|
||||||
|
|
||||||
@ -27,7 +28,6 @@ export {
|
|||||||
VbenPinInput,
|
VbenPinInput,
|
||||||
VbenSelect,
|
VbenSelect,
|
||||||
VbenSpinner,
|
VbenSpinner,
|
||||||
VbenTree,
|
|
||||||
} from '@vben-core/shadcn-ui';
|
} from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
export type { FlattenedItem } from '@vben-core/shadcn-ui';
|
export type { FlattenedItem } from '@vben-core/shadcn-ui';
|
||||||
|
|||||||
@ -25,7 +25,7 @@ const footerRef = useTemplateRef<HTMLDivElement>('footerRef');
|
|||||||
const contentStyle = computed<StyleValue>(() => {
|
const contentStyle = computed<StyleValue>(() => {
|
||||||
if (autoContentHeight) {
|
if (autoContentHeight) {
|
||||||
return {
|
return {
|
||||||
height: `calc(var(${CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT}) - ${headerHeight.value}px - ${typeof heightOffset === 'number' ? `${heightOffset}px` : heightOffset})`,
|
height: `calc(var(${CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT}) - ${headerHeight.value}px - ${footerHeight.value}px - ${typeof heightOffset === 'number' ? `${heightOffset}px` : heightOffset})`,
|
||||||
overflowY: shouldAutoHeight.value ? 'auto' : 'unset',
|
overflowY: shouldAutoHeight.value ? 'auto' : 'unset',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="relative">
|
<div class="relative flex min-h-full flex-col">
|
||||||
<div
|
<div
|
||||||
v-if="
|
v-if="
|
||||||
description ||
|
description ||
|
||||||
@ -89,16 +89,10 @@ onMounted(() => {
|
|||||||
<div :class="cn('h-full p-4', contentClass)" :style="contentStyle">
|
<div :class="cn('h-full p-4', contentClass)" :style="contentStyle">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="$slots.footer"
|
v-if="$slots.footer"
|
||||||
ref="footerRef"
|
ref="footerRef"
|
||||||
:class="
|
:class="cn('bg-card align-center flex px-6 py-4', footerClass)"
|
||||||
cn(
|
|
||||||
'bg-card align-center absolute bottom-0 left-0 right-0 flex px-6 py-4',
|
|
||||||
footerClass,
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<slot name="footer"></slot>
|
<slot name="footer"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
1
packages/effects/common-ui/src/components/tree/index.ts
Normal file
1
packages/effects/common-ui/src/components/tree/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as Tree } from './tree.vue';
|
||||||
25
packages/effects/common-ui/src/components/tree/tree.vue
Normal file
25
packages/effects/common-ui/src/components/tree/tree.vue
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { TreeProps } from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
|
import { Inbox } from '@vben/icons';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
import { treePropsDefaults, VbenTree } from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<TreeProps>(), treePropsDefaults());
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VbenTree v-if="props.treeData?.length > 0" v-bind="props">
|
||||||
|
<template v-for="(_, key) in $slots" :key="key" #[key]="slotProps">
|
||||||
|
<slot :name="key" v-bind="slotProps"> </slot>
|
||||||
|
</template>
|
||||||
|
</VbenTree>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex-col-center text-muted-foreground cursor-pointer rounded-lg border p-10 text-sm font-medium"
|
||||||
|
>
|
||||||
|
<Inbox class="size-10" />
|
||||||
|
<div class="mt-1">{{ $t('common.noData') }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@ -35,6 +35,10 @@ interface Props {
|
|||||||
* @zh_CN 按钮文本
|
* @zh_CN 按钮文本
|
||||||
*/
|
*/
|
||||||
submitButtonText?: string;
|
submitButtonText?: string;
|
||||||
|
/**
|
||||||
|
* @zh_CN 是否显示返回按钮
|
||||||
|
*/
|
||||||
|
showBack?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@ -43,6 +47,7 @@ defineOptions({
|
|||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
loading: false,
|
loading: false,
|
||||||
|
showBack: true,
|
||||||
loginPath: '/auth/login',
|
loginPath: '/auth/login',
|
||||||
submitButtonText: '',
|
submitButtonText: '',
|
||||||
subTitle: '',
|
subTitle: '',
|
||||||
@ -110,7 +115,12 @@ defineExpose({
|
|||||||
{{ submitButtonText || $t('common.login') }}
|
{{ submitButtonText || $t('common.login') }}
|
||||||
</slot>
|
</slot>
|
||||||
</VbenButton>
|
</VbenButton>
|
||||||
<VbenButton class="mt-4 w-full" variant="outline" @click="goToLogin()">
|
<VbenButton
|
||||||
|
v-if="showBack"
|
||||||
|
class="mt-4 w-full"
|
||||||
|
variant="outline"
|
||||||
|
@click="goToLogin()"
|
||||||
|
>
|
||||||
{{ $t('common.back') }}
|
{{ $t('common.back') }}
|
||||||
</VbenButton>
|
</VbenButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
import { RiDingding } from '@vben/icons';
|
import { SvgDingDingIcon } from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import { alert, useVbenModal } from '@vben-core/popup-ui';
|
import { alert, useVbenModal } from '@vben-core/popup-ui';
|
||||||
@ -96,7 +96,7 @@ const handleLogin = () => {
|
|||||||
:tooltip="$t('authentication.dingdingLogin')"
|
:tooltip="$t('authentication.dingdingLogin')"
|
||||||
tooltip-side="top"
|
tooltip-side="top"
|
||||||
>
|
>
|
||||||
<RiDingding />
|
<SvgDingDingIcon />
|
||||||
</VbenIconButton>
|
</VbenIconButton>
|
||||||
<Modal>
|
<Modal>
|
||||||
<div id="dingding_qrcode_login_element"></div>
|
<div id="dingding_qrcode_login_element"></div>
|
||||||
|
|||||||
@ -35,6 +35,10 @@ interface Props {
|
|||||||
* @zh_CN 描述
|
* @zh_CN 描述
|
||||||
*/
|
*/
|
||||||
description?: string;
|
description?: string;
|
||||||
|
/**
|
||||||
|
* @zh_CN 是否显示返回按钮
|
||||||
|
*/
|
||||||
|
showBack?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@ -44,6 +48,7 @@ defineOptions({
|
|||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
description: '',
|
description: '',
|
||||||
loading: false,
|
loading: false,
|
||||||
|
showBack: true,
|
||||||
loginPath: '/auth/login',
|
loginPath: '/auth/login',
|
||||||
submitButtonText: '',
|
submitButtonText: '',
|
||||||
subTitle: '',
|
subTitle: '',
|
||||||
@ -88,7 +93,12 @@ function goToLogin() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<VbenButton class="mt-4 w-full" variant="outline" @click="goToLogin()">
|
<VbenButton
|
||||||
|
v-if="showBack"
|
||||||
|
class="mt-4 w-full"
|
||||||
|
variant="outline"
|
||||||
|
@click="goToLogin()"
|
||||||
|
>
|
||||||
{{ $t('common.back') }}
|
{{ $t('common.back') }}
|
||||||
</VbenButton>
|
</VbenButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAppConfig } from '@vben/hooks';
|
import { useAppConfig } from '@vben/hooks';
|
||||||
import { MdiGithub, MdiGoogle, MdiQqchat, MdiWechat } from '@vben/icons';
|
import {
|
||||||
|
SvgGithubIcon,
|
||||||
|
SvgGoogleIcon,
|
||||||
|
SvgQQChatIcon,
|
||||||
|
SvgWeChatIcon,
|
||||||
|
} from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import { VbenIconButton } from '@vben-core/shadcn-ui';
|
import { VbenIconButton } from '@vben-core/shadcn-ui';
|
||||||
@ -32,28 +37,28 @@ const {
|
|||||||
tooltip-side="top"
|
tooltip-side="top"
|
||||||
class="mb-3"
|
class="mb-3"
|
||||||
>
|
>
|
||||||
<MdiWechat />
|
<SvgWeChatIcon />
|
||||||
</VbenIconButton>
|
</VbenIconButton>
|
||||||
<VbenIconButton
|
<VbenIconButton
|
||||||
:tooltip="$t('authentication.qqLogin')"
|
:tooltip="$t('authentication.qqLogin')"
|
||||||
tooltip-side="top"
|
tooltip-side="top"
|
||||||
class="mb-3"
|
class="mb-3"
|
||||||
>
|
>
|
||||||
<MdiQqchat />
|
<SvgQQChatIcon />
|
||||||
</VbenIconButton>
|
</VbenIconButton>
|
||||||
<VbenIconButton
|
<VbenIconButton
|
||||||
:tooltip="$t('authentication.githubLogin')"
|
:tooltip="$t('authentication.githubLogin')"
|
||||||
tooltip-side="top"
|
tooltip-side="top"
|
||||||
class="mb-3"
|
class="mb-3"
|
||||||
>
|
>
|
||||||
<MdiGithub />
|
<SvgGithubIcon />
|
||||||
</VbenIconButton>
|
</VbenIconButton>
|
||||||
<VbenIconButton
|
<VbenIconButton
|
||||||
:tooltip="$t('authentication.googleLogin')"
|
:tooltip="$t('authentication.googleLogin')"
|
||||||
tooltip-side="top"
|
tooltip-side="top"
|
||||||
class="mb-3"
|
class="mb-3"
|
||||||
>
|
>
|
||||||
<MdiGoogle />
|
<SvgGoogleIcon />
|
||||||
</VbenIconButton>
|
</VbenIconButton>
|
||||||
<DingdingLogin
|
<DingdingLogin
|
||||||
v-if="dingdingAuthConfig"
|
v-if="dingdingAuthConfig"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/hooks",
|
"name": "@vben/hooks",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/layouts",
|
"name": "@vben/layouts",
|
||||||
"version": "5.5.8",
|
"version": "5.5.9",
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -50,7 +50,7 @@ const { authPanelCenter, authPanelLeft, authPanelRight, isDark } =
|
|||||||
<AuthenticationFormView
|
<AuthenticationFormView
|
||||||
v-if="authPanelLeft"
|
v-if="authPanelLeft"
|
||||||
class="min-h-full w-2/5 flex-1"
|
class="min-h-full w-2/5 flex-1"
|
||||||
transition-name="slide-left"
|
data-side="left"
|
||||||
>
|
>
|
||||||
<template v-if="copyright" #copyright>
|
<template v-if="copyright" #copyright>
|
||||||
<slot name="copyright">
|
<slot name="copyright">
|
||||||
@ -86,7 +86,14 @@ const { authPanelCenter, authPanelLeft, authPanelRight, isDark } =
|
|||||||
class="bg-background-deep absolute inset-0 h-full w-full dark:bg-[#070709]"
|
class="bg-background-deep absolute inset-0 h-full w-full dark:bg-[#070709]"
|
||||||
>
|
>
|
||||||
<div class="login-background absolute left-0 top-0 size-full"></div>
|
<div class="login-background absolute left-0 top-0 size-full"></div>
|
||||||
<div class="flex-col-center -enter-x mr-20 h-full">
|
<div
|
||||||
|
:key="authPanelLeft ? 'left' : authPanelRight ? 'right' : 'center'"
|
||||||
|
class="flex-col-center mr-20 h-full"
|
||||||
|
:class="{
|
||||||
|
'enter-x': authPanelLeft,
|
||||||
|
'-enter-x': authPanelRight,
|
||||||
|
}"
|
||||||
|
>
|
||||||
<template v-if="sloganImage">
|
<template v-if="sloganImage">
|
||||||
<img
|
<img
|
||||||
:alt="appName"
|
:alt="appName"
|
||||||
@ -110,6 +117,7 @@ const { authPanelCenter, authPanelLeft, authPanelRight, isDark } =
|
|||||||
<div class="login-background absolute left-0 top-0 size-full"></div>
|
<div class="login-background absolute left-0 top-0 size-full"></div>
|
||||||
<AuthenticationFormView
|
<AuthenticationFormView
|
||||||
class="md:bg-background shadow-primary/5 shadow-float w-full rounded-3xl pb-20 md:w-2/3 lg:w-1/2 xl:w-[36%]"
|
class="md:bg-background shadow-primary/5 shadow-float w-full rounded-3xl pb-20 md:w-2/3 lg:w-1/2 xl:w-[36%]"
|
||||||
|
data-side="bottom"
|
||||||
>
|
>
|
||||||
<template v-if="copyright" #copyright>
|
<template v-if="copyright" #copyright>
|
||||||
<slot name="copyright">
|
<slot name="copyright">
|
||||||
@ -125,7 +133,8 @@ const { authPanelCenter, authPanelLeft, authPanelRight, isDark } =
|
|||||||
<!-- 右侧认证面板 -->
|
<!-- 右侧认证面板 -->
|
||||||
<AuthenticationFormView
|
<AuthenticationFormView
|
||||||
v-if="authPanelRight"
|
v-if="authPanelRight"
|
||||||
class="min-h-full w-[34%] flex-1"
|
class="min-h-full w-2/5 flex-1"
|
||||||
|
data-side="right"
|
||||||
>
|
>
|
||||||
<template v-if="copyright" #copyright>
|
<template v-if="copyright" #copyright>
|
||||||
<slot name="copyright">
|
<slot name="copyright">
|
||||||
|
|||||||
@ -2,6 +2,10 @@
|
|||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'AuthenticationFormView',
|
name: 'AuthenticationFormView',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
dataSide?: 'bottom' | 'left' | 'right' | 'top';
|
||||||
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -16,7 +20,8 @@ defineOptions({
|
|||||||
<component
|
<component
|
||||||
:is="Component"
|
:is="Component"
|
||||||
:key="route.fullPath"
|
:key="route.fullPath"
|
||||||
class="enter-x mt-6 w-full sm:mx-auto md:max-w-md"
|
class="side-content mt-6 w-full sm:mx-auto md:max-w-md"
|
||||||
|
:data-side="dataSide"
|
||||||
/>
|
/>
|
||||||
</KeepAlive>
|
</KeepAlive>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|||||||
@ -158,7 +158,9 @@ function clickLogo() {
|
|||||||
function autoCollapseMenuByRouteMeta(route: RouteLocationNormalizedLoaded) {
|
function autoCollapseMenuByRouteMeta(route: RouteLocationNormalizedLoaded) {
|
||||||
// 只在双列模式下生效
|
// 只在双列模式下生效
|
||||||
if (
|
if (
|
||||||
preferences.app.layout === 'sidebar-mixed-nav' &&
|
['header-mixed-nav', 'sidebar-mixed-nav'].includes(
|
||||||
|
preferences.app.layout,
|
||||||
|
) &&
|
||||||
route.meta &&
|
route.meta &&
|
||||||
route.meta.hideInMenu
|
route.meta.hideInMenu
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -29,7 +29,8 @@ function useNavigation() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const route = routeMetaMap.get(path);
|
const route = routeMetaMap.get(path);
|
||||||
return route?.meta?.openInNewWindow ?? false;
|
// 如果有外链或者设置了在新窗口打开,返回 true
|
||||||
|
return !!(route?.meta?.link || route?.meta?.openInNewWindow);
|
||||||
};
|
};
|
||||||
|
|
||||||
const resolveHref = (path: string): string => {
|
const resolveHref = (path: string): string => {
|
||||||
@ -39,7 +40,13 @@ function useNavigation() {
|
|||||||
const navigation = async (path: string) => {
|
const navigation = async (path: string) => {
|
||||||
try {
|
try {
|
||||||
const route = routeMetaMap.get(path);
|
const route = routeMetaMap.get(path);
|
||||||
const { openInNewWindow = false, query = {} } = route?.meta ?? {};
|
const { openInNewWindow = false, query = {}, link } = route?.meta ?? {};
|
||||||
|
|
||||||
|
// 检查是否有外链
|
||||||
|
if (link && typeof link === 'string') {
|
||||||
|
openWindow(link, { target: '_blank' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isHttpUrl(path)) {
|
if (isHttpUrl(path)) {
|
||||||
openWindow(path, { target: '_blank' });
|
openWindow(path, { target: '_blank' });
|
||||||
|
|||||||
@ -31,7 +31,7 @@ async function handleUpdate(value: string | undefined) {
|
|||||||
:model-value="preferences.app.locale"
|
:model-value="preferences.app.locale"
|
||||||
@update:model-value="handleUpdate"
|
@update:model-value="handleUpdate"
|
||||||
>
|
>
|
||||||
<VbenIconButton>
|
<VbenIconButton class="hover:animate-[shrink_0.3s_ease-in-out]">
|
||||||
<Languages class="text-foreground size-4" />
|
<Languages class="text-foreground size-4" />
|
||||||
</VbenIconButton>
|
</VbenIconButton>
|
||||||
</VbenDropdownRadioMenu>
|
</VbenDropdownRadioMenu>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user