style(projects): format code

This commit is contained in:
Soybean 2023-12-14 21:45:29 +08:00
parent a176dc443e
commit a748166399
127 changed files with 2472 additions and 3006 deletions

View File

@ -1,9 +1,10 @@
import type { ProxyOptions } from 'vite'; import type { ProxyOptions } from 'vite';
import { createServiceConfig, createProxyPattern } from '../../env.config'; import { createProxyPattern, createServiceConfig } from '../../env.config';
/** /**
* set http proxy * Set http proxy
* @param env - the current env *
* @param env - The current env
*/ */
export function createViteProxy(env: Env.ImportMeta) { export function createViteProxy(env: Env.ImportMeta) {
const isEnableHttpProxy = env.VITE_HTTP_PROXY === 'Y'; const isEnableHttpProxy = env.VITE_HTTP_PROXY === 'Y';

View File

@ -1,3 +1,4 @@
import process from 'node:process';
import path from 'node:path'; import path from 'node:path';
import unocss from '@unocss/vite'; import unocss from '@unocss/vite';
import presetIcons from '@unocss/preset-icons'; import presetIcons from '@unocss/preset-icons';
@ -8,9 +9,7 @@ export function setupUnocss(viteEnv: Env.ImportMeta) {
const localIconPath = path.join(process.cwd(), 'src/assets/svg-icon'); const localIconPath = path.join(process.cwd(), 'src/assets/svg-icon');
/** /** The name of the local icon collection */
* the name of the local icon collection
*/
const collectionName = VITE_ICON_LOCAL_PREFIX.replace(`${VITE_ICON_PREFIX}-`, ''); const collectionName = VITE_ICON_LOCAL_PREFIX.replace(`${VITE_ICON_PREFIX}-`, '');
return unocss({ return unocss({

View File

@ -1,3 +1,4 @@
import process from 'node:process';
import path from 'node:path'; import path from 'node:path';
import type { PluginOption } from 'vite'; import type { PluginOption } from 'vite';
import Icons from 'unplugin-icons/vite'; import Icons from 'unplugin-icons/vite';
@ -12,9 +13,7 @@ export function setupUnplugin(viteEnv: Env.ImportMeta) {
const localIconPath = path.join(process.cwd(), 'src/assets/svg-icon'); const localIconPath = path.join(process.cwd(), 'src/assets/svg-icon');
/** /** The name of the local icon collection */
* the name of the local icon collection
*/
const collectionName = VITE_ICON_LOCAL_PREFIX.replace(`${VITE_ICON_PREFIX}-`, ''); const collectionName = VITE_ICON_LOCAL_PREFIX.replace(`${VITE_ICON_PREFIX}-`, '');
const plugins: PluginOption[] = [ const plugins: PluginOption[] = [

View File

@ -1,6 +1,7 @@
/** /**
* create service config by current env * Create service config by current env
* @param env the current env *
* @param env The current env
*/ */
export function createServiceConfig(env: Env.ImportMeta) { export function createServiceConfig(env: Env.ImportMeta) {
const mockURL = 'https://mock.apifox.com/m1/3109515-0-default'; const mockURL = 'https://mock.apifox.com/m1/3109515-0-default';
@ -32,8 +33,9 @@ export function createServiceConfig(env: Env.ImportMeta) {
} }
/** /**
* get proxy pattern of service url * Get proxy pattern of service url
* @param key if not set, will use the default key *
* @param key If not set, will use the default key
*/ */
export function createProxyPattern(key?: App.Service.OtherBaseURLKey) { export function createProxyPattern(key?: App.Service.OtherBaseURLKey) {
if (!key) { if (!key) {

View File

@ -1,26 +1,26 @@
{ {
"name": "soybean-admin", "name": "soybean-admin",
"type": "module",
"version": "1.0.0", "version": "1.0.0",
"packageManager": "pnpm@8.10.5",
"description": "A fresh and elegant admin template, based on Vue3、Vite3、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite3、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。", "description": "A fresh and elegant admin template, based on Vue3、Vite3、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite3、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。",
"author": { "author": {
"name": "Soybean", "name": "Soybean",
"email": "soybeanjs@outlook.com", "email": "soybeanjs@outlook.com",
"url": "https://github.com/soybeanjs" "url": "https://github.com/soybeanjs"
}, },
"type": "module",
"packageManager": "pnpm@8.10.5",
"scripts": { "scripts": {
"dev": "vite",
"build": "run-s typecheck build-only", "build": "run-s typecheck build-only",
"preview": "vite preview",
"build-only": "vite build", "build-only": "vite build",
"typecheck": "vue-tsc --noEmit --skipLibCheck",
"lint": "eslint . --fix",
"format": "sa prettier-write",
"commit": "sa git-commit",
"cleanup": "sa cleanup", "cleanup": "sa cleanup",
"update-pkg": "sa update-pkg", "commit": "sa git-commit",
"prepare": "simple-git-hooks" "dev": "vite",
"format": "sa prettier-write",
"lint": "eslint . --fix",
"prepare": "simple-git-hooks",
"preview": "vite preview",
"typecheck": "vue-tsc --noEmit --skipLibCheck",
"update-pkg": "sa update-pkg"
}, },
"dependencies": { "dependencies": {
"@better-scroll/core": "2.5.1", "@better-scroll/core": "2.5.1",
@ -30,45 +30,48 @@
"@sa/materials": "workspace:*", "@sa/materials": "workspace:*",
"@sa/request": "workspace:*", "@sa/request": "workspace:*",
"@sa/utils": "workspace:*", "@sa/utils": "workspace:*",
"@vueuse/core": "10.6.1", "@vueuse/core": "10.7.0",
"clipboard": "2.0.11", "clipboard": "2.0.11",
"dayjs": "1.11.10", "dayjs": "1.11.10",
"lodash-es": "4.17.21", "lodash-es": "4.17.21",
"naive-ui": "2.35.0", "naive-ui": "2.35.0",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"pinia": "2.1.7", "pinia": "2.1.7",
"vue": "3.3.8", "vue": "3.3.11",
"vue-i18n": "9.7.0", "vue-i18n": "9.8.0",
"vue-router": "4.2.5" "vue-router": "4.2.5"
}, },
"devDependencies": { "devDependencies": {
"@elegant-router/vue": "0.3.1", "@elegant-router/vue": "0.3.1",
"@iconify/json": "2.2.143", "@iconify/json": "2.2.157",
"@sa/scripts": "workspace:*", "@sa/scripts": "workspace:*",
"@sa/uno-preset": "workspace:*", "@sa/uno-preset": "workspace:*",
"@soybeanjs/eslint-config": "^1.1.2", "@soybeanjs/eslint-config": "1.1.3",
"@types/lodash-es": "4.17.11", "@types/lodash-es": "4.17.12",
"@types/node": "20.9.1", "@types/node": "20.10.4",
"@types/nprogress": "0.2.3", "@types/nprogress": "0.2.3",
"@unocss/preset-icons": "0.57.5", "@unocss/preset-icons": "0.58.0",
"@unocss/preset-uno": "0.57.5", "@unocss/preset-uno": "0.58.0",
"@unocss/transformer-directives": "0.57.5", "@unocss/transformer-directives": "0.58.0",
"@unocss/transformer-variant-group": "0.57.5", "@unocss/transformer-variant-group": "0.58.0",
"@unocss/vite": "0.57.5", "@unocss/vite": "0.58.0",
"@vitejs/plugin-vue": "4.5.0", "@vitejs/plugin-vue": "4.5.2",
"@vitejs/plugin-vue-jsx": "3.1.0", "@vitejs/plugin-vue-jsx": "3.1.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"eslint": "8.55.0",
"eslint-plugin-vue": "9.19.2",
"npm-run-all": "4.1.5", "npm-run-all": "4.1.5",
"sass": "1.69.5", "sass": "1.69.5",
"simple-git-hooks": "2.9.0", "simple-git-hooks": "2.9.0",
"typescript": "5.2.2", "typescript": "5.3.3",
"unplugin-icons": "0.17.4", "unplugin-icons": "0.18.1",
"unplugin-vue-components": "0.25.2", "unplugin-vue-components": "0.26.0",
"vite": "5.0.0", "vite": "5.0.8",
"vite-plugin-progress": "0.0.7", "vite-plugin-progress": "0.0.7",
"vite-plugin-svg-icons": "2.0.1", "vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-devtools": "1.0.0-rc.5", "vite-plugin-vue-devtools": "1.0.0-rc.8",
"vue-tsc": "1.8.22" "vue-eslint-parser": "9.3.2",
"vue-tsc": "1.8.25"
}, },
"simple-git-hooks": { "simple-git-hooks": {
"commit-msg": "pnpm sa git-commit-verify", "commit-msg": "pnpm sa git-commit-verify",

View File

@ -6,9 +6,7 @@
}, },
"typesVersions": { "typesVersions": {
"*": { "*": {
"*": [ "*": ["./src/*"]
"./src/*"
]
} }
}, },
"dependencies": { "dependencies": {

View File

@ -1,12 +1,13 @@
import { getColorPaletteFamily } from './palette'; import { getColorPaletteFamily } from './palette';
import { getColorName } from './name'; import { getColorName } from './name';
import type { ColorPalette, ColorPaletteNumber, ColorPaletteItem, ColorPaletteFamily } from './type'; import type { ColorPalette, ColorPaletteFamily, ColorPaletteItem, ColorPaletteNumber } from './type';
import defaultPalettes from './json/palette.json'; import defaultPalettes from './json/palette.json';
/** /**
* get color palette by provided color and color name * Get color palette by provided color and color name
* @param color the provided color *
* @param colorName color name * @param color The provided color
* @param colorName Color name
*/ */
export function getColorPalette(color: string, colorName: string) { export function getColorPalette(color: string, colorName: string) {
const colorPaletteFamily = getColorPaletteFamily(color, colorName); const colorPaletteFamily = getColorPaletteFamily(color, colorName);
@ -31,10 +32,11 @@ export function getColorPalette(color: string, colorName: string) {
} }
/** /**
* get color by color palette number * Get color by color palette number
* @param color color *
* @param num color palette number * @param color Color
* @return color hexcode * @param num Color palette number
* @returns Color hexcode
*/ */
export function getColorByColorPaletteNumber(color: string, num: ColorPaletteNumber) { export function getColorByColorPaletteNumber(color: string, num: ColorPaletteNumber) {
const colorPalette = getColorPalette(color, color); const colorPalette = getColorPalette(color, color);
@ -46,9 +48,7 @@ export function getColorByColorPaletteNumber(color: string, num: ColorPaletteNum
export default getColorPalette; export default getColorPalette;
/** /** The builtin color palettes */
* the builtin color palettes
*/
const colorPalettes = defaultPalettes as ColorPaletteFamily[]; const colorPalettes = defaultPalettes as ColorPaletteFamily[];
export { getColorName, colorPalettes }; export { getColorName, colorPalettes };

View File

@ -1,4 +1,4 @@
import { getHex, getRgb, getHsl } from './color'; import { getHex, getHsl, getRgb } from './color';
import colorNames from './json/color-name.json'; import colorNames from './json/color-name.json';
export function getColorName(color: string) { export function getColorName(color: string) {

View File

@ -1,4 +1,4 @@
import { isValidColor, getHsl, getDeltaE, transformHslToHex } from './color'; import { getDeltaE, getHsl, isValidColor, transformHslToHex } from './color';
import { getColorName } from './name'; import { getColorName } from './name';
import type { ColorPaletteFamily, ColorPaletteFamilyWithNearestPalette } from './type'; import type { ColorPaletteFamily, ColorPaletteFamilyWithNearestPalette } from './type';
import defaultPalettes from './json/palette.json'; import defaultPalettes from './json/palette.json';

View File

@ -1,35 +1,24 @@
/** /** The color palette number */
* the color palette number
*/
export type ColorPaletteNumber = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950; export type ColorPaletteNumber = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950;
/** /** The color palette item */
* the color palette item
*/
export type ColorPaletteItem = { export type ColorPaletteItem = {
/** /** The color hexcode */
* the color hexcode
*/
hexcode: string; hexcode: string;
/** /**
* the color number * The color number
*
* @link {@link ColorPaletteNumber} * @link {@link ColorPaletteNumber}
*/ */
number: ColorPaletteNumber; number: ColorPaletteNumber;
/** /** The color name */
* the color name
*/
name: string; name: string;
}; };
export type ColorPaletteFamily = { export type ColorPaletteFamily = {
/** /** The color palette family key */
* the color palette family key
*/
key: string; key: string;
/** /** The color palette family's palettes */
* the color palette family's palettes
*/
palettes: ColorPaletteItem[]; palettes: ColorPaletteItem[];
}; };
@ -47,17 +36,14 @@ export type ColorPaletteFamilyWithNearestPalette = ColorPaletteFamily & {
}; };
export type ColorPalette = ColorPaletteFamily & { export type ColorPalette = ColorPaletteFamily & {
/** /** The color map of the palette */
* the color map of the palette
*/
colorMap: Map<ColorPaletteNumber, ColorPaletteItem>; colorMap: Map<ColorPaletteNumber, ColorPaletteItem>;
/** /**
* the main color of the palette * The main color of the palette
* @description which number is 500 *
* Which number is 500
*/ */
main: ColorPaletteItemWithName; main: ColorPaletteItemWithName;
/** /** The match color of the palette */
* the match color of the palette
*/
match: ColorPaletteItemWithName; match: ColorPaletteItemWithName;
}; };

View File

@ -1,3 +1,4 @@
import process from 'node:process';
import path from 'node:path'; import path from 'node:path';
import { defineConfig } from 'vitepress'; import { defineConfig } from 'vitepress';

View File

@ -45,11 +45,7 @@
var(--vp-c-brand-darker) var(--vp-c-brand-darker)
); );
--vp-home-hero-image-background-image: linear-gradient( --vp-home-hero-image-background-image: linear-gradient(-45deg, var(--vp-c-brand-lightest) 30%, var(--vp-c-brand) 50%);
-45deg,
var(--vp-c-brand-lightest) 30%,
var(--vp-c-brand) 50%
);
--vp-home-hero-image-filter: blur(40px); --vp-home-hero-image-filter: blur(40px);
} }

View File

@ -2,11 +2,11 @@
"name": "@sa/docs", "name": "@sa/docs",
"version": "1.0.0", "version": "1.0.0",
"scripts": { "scripts": {
"dev": "vitepress dev",
"build": "vitepress build", "build": "vitepress build",
"dev": "vitepress dev",
"serve": "vitepress serve" "serve": "vitepress serve"
}, },
"devDependencies": { "devDependencies": {
"vitepress": "1.0.0-rc.27" "vitepress": "1.0.0-rc.31"
} }
} }

View File

@ -6,9 +6,7 @@
}, },
"typesVersions": { "typesVersions": {
"*": { "*": {
"*": [ "*": ["./src/*"]
"./src/*"
]
} }
} }
} }

View File

@ -1,8 +1,9 @@
import { ref } from 'vue'; import { ref } from 'vue';
/** /**
* boolean * Boolean
* @param initValue init value *
* @param initValue Init value
*/ */
export default function useBoolean(initValue = false) { export default function useBoolean(initValue = false) {
const bool = ref(initValue); const bool = ref(initValue);

View File

@ -2,61 +2,60 @@ import { inject, provide } from 'vue';
import type { InjectionKey } from 'vue'; import type { InjectionKey } from 'vue';
/** /**
* use context * Use context
* @param contextName context name *
* @param fn context function
* @example * @example
* ```ts * ```ts
* // there are three vue files: A.vue, B.vue, C.vue, and A.vue is the parent component of B.vue and C.vue * // there are three vue files: A.vue, B.vue, C.vue, and A.vue is the parent component of B.vue and C.vue
* *
* // context.ts * // context.ts
* import { ref } from 'vue'; * import { ref } from 'vue';
* import { useContext } from '@sa/hooks'; * import { useContext } from '@sa/hooks';
* *
* export const { setupStore, useStore } = useContext('demo', () => { * export const { setupStore, useStore } = useContext('demo', () => {
* const count = ref(0); * const count = ref(0);
* *
* function increment() { * function increment() {
* count.value++; * count.value++;
* } * }
* *
* function decrement() { * function decrement() {
* count.value--; * count.value--;
* } * }
* *
* return { * return {
* count, * count,
* increment, * increment,
* decrement * decrement
* }; * };
* }) * })
* ``` * ``` // A.vue
* ```vue
* <template>
* <div>A</div>
* </template>
* <script setup lang="ts">
* import { setupStore } from './context';
* *
* // A.vue * setupStore();
* ```vue * // const { increment } = setupStore(); // also can control the store in the parent component
* <template> * </script>
* <div>A</div> * ``` // B.vue
* </template> * ```vue
* <script setup lang="ts"> * <template>
* import { setupStore } from './context'; * <div>B</div>
* </template>
* <script setup lang="ts">
* import { useStore } from './context';
* *
* setupStore(); * const { count, increment } = useStore();
* // const { increment } = setupStore(); // also can control the store in the parent component * </script>
* </script> * ```;
* ```
* // B.vue
* ```vue
* <template>
* <div>B</div>
* </template>
* <script setup lang="ts">
* import { useStore } from './context';
* *
* const { count, increment } = useStore(); * // C.vue is same as B.vue
* </script>
* ```
* *
* // C.vue is same as B.vue * @param contextName Context name
* @param fn Context function
*/ */
export default function useContext<T extends (...args: any[]) => any>(contextName: string, fn: T) { export default function useContext<T extends (...args: any[]) => any>(contextName: string, fn: T) {
type Context = ReturnType<T>; type Context = ReturnType<T>;
@ -69,20 +68,14 @@ export default function useContext<T extends (...args: any[]) => any>(contextNam
} }
return { return {
/** /** Setup store in the parent component */
* setup store in the parent component
*/
setupStore, setupStore,
/** /** Use store in the child component */
* use store in the child component
*/
useStore useStore
}; };
} }
/** /** Create context */
* create context
*/
function createContext<T>(contextName: string) { function createContext<T>(contextName: string) {
const injectKey: InjectionKey<T> = Symbol(contextName); const injectKey: InjectionKey<T> = Symbol(contextName);

View File

@ -1,8 +1,9 @@
import useBoolean from './use-boolean'; import useBoolean from './use-boolean';
/** /**
* loading * Loading
* @param initValue init value *
* @param initValue Init value
*/ */
export default function useLoading(initValue = false) { export default function useLoading(initValue = false) {
const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(initValue); const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(initValue);

View File

@ -2,33 +2,27 @@ import { h } from 'vue';
import type { Component } from 'vue'; import type { Component } from 'vue';
/** /**
* svg icon render hook * Svg icon render hook
* @param SvgIcon svg icon component *
* @param SvgIcon Svg icon component
*/ */
export default function useSvgIconRender(SvgIcon: Component) { export default function useSvgIconRender(SvgIcon: Component) {
interface IconConfig { interface IconConfig {
/** /** Iconify icon name */
* iconify icon name
*/
icon?: string; icon?: string;
/** /** Local icon name */
* local icon name
*/
localIcon?: string; localIcon?: string;
/** /** Icon color */
* icon color
*/
color?: string; color?: string;
/** /** Icon size */
* icon size
*/
fontSize?: number; fontSize?: number;
} }
type IconStyle = Partial<Pick<CSSStyleDeclaration, 'color' | 'fontSize'>>; type IconStyle = Partial<Pick<CSSStyleDeclaration, 'color' | 'fontSize'>>;
/** /**
* svg icon VNode * Svg icon VNode
*
* @param config * @param config
*/ */
const SvgIconVNode = (config: IconConfig) => { const SvgIconVNode = (config: IconConfig) => {

View File

@ -1,19 +1,19 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".", "baseUrl": ".",
"module": "ESNext", "module": "ESNext",
"target": "ESNext",
"lib": ["DOM", "ESNext"],
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "preserve",
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"noUnusedLocals": true, "types": ["node"],
"strict": true,
"strictNullChecks": true, "strictNullChecks": true,
"forceConsistentCasingInFileNames": true, "noUnusedLocals": true,
"types": ["node"] "allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
}, },
"include": ["src/**/*"], "include": ["src/**/*"],
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]

View File

@ -6,9 +6,7 @@
}, },
"typesVersions": { "typesVersions": {
"*": { "*": {
"*": [ "*": ["./src/*"]
"./src/*"
]
} }
}, },
"dependencies": { "dependencies": {

View File

@ -1,4 +1,4 @@
import AdminLayout, { LAYOUT_SCROLL_EL_ID, LAYOUT_MAX_Z_INDEX } from './libs/admin-layout'; import AdminLayout, { LAYOUT_MAX_Z_INDEX, LAYOUT_SCROLL_EL_ID } from './libs/admin-layout';
import PageTab from './libs/page-tab'; import PageTab from './libs/page-tab';
import SimpleScrollbar from './libs/simple-scrollbar'; import SimpleScrollbar from './libs/simple-scrollbar';
import ColorPicker from './libs/color-picker'; import ColorPicker from './libs/color-picker';

View File

@ -14,4 +14,5 @@ declare const styles: {
readonly 'sider-padding-top': string; readonly 'sider-padding-top': string;
readonly 'sider-padding-bottom': string; readonly 'sider-padding-bottom': string;
}; };
export = styles;
export default styles;

View File

@ -1,5 +1,5 @@
import AdminLayout from './index.vue'; import AdminLayout from './index.vue';
import { LAYOUT_SCROLL_EL_ID, LAYOUT_MAX_Z_INDEX } from './shared'; import { LAYOUT_MAX_Z_INDEX, LAYOUT_SCROLL_EL_ID } from './shared';
export default AdminLayout; export default AdminLayout;
export { LAYOUT_SCROLL_EL_ID, LAYOUT_MAX_Z_INDEX }; export { LAYOUT_SCROLL_EL_ID, LAYOUT_MAX_Z_INDEX };

View File

@ -1,129 +1,7 @@
<template>
<div :class="['relative h-full', commonClass]" :style="cssVars">
<div
:id="isWrapperScroll ? scrollElId : undefined"
:class="['flex flex-col h-full', commonClass, scrollWrapperClass, { 'overflow-y-auto': isWrapperScroll }]"
>
<!-- Header -->
<template v-if="showHeader">
<header
v-show="!fullContent"
:class="[
style['layout-header'],
'flex-shrink-0',
commonClass,
headerClass,
headerLeftGapClass,
{ 'absolute top-0 left-0 w-full': fixedHeaderAndTab }
]"
>
<slot name="header"></slot>
</header>
<div
v-show="!fullContent && fixedHeaderAndTab"
:class="[style['layout-header-placement'], 'flex-shrink-0 overflow-hidden']"
></div>
</template>
<!-- Tab -->
<template v-if="showTab">
<div
:class="[
style['layout-tab'],
'flex-shrink-0',
commonClass,
tabClass,
{ 'top-0!': fullContent || !showHeader },
leftGapClass,
{ 'absolute left-0 w-full': fixedHeaderAndTab }
]"
>
<slot name="tab"></slot>
</div>
<div
v-show="fullContent || fixedHeaderAndTab"
:class="[style['layout-tab-placement'], 'flex-shrink-0 overflow-hidden']"
></div>
</template>
<!-- Sider -->
<template v-if="showSider">
<aside
v-show="!fullContent"
:class="[
'absolute left-0 top-0 h-full',
commonClass,
siderClass,
siderPaddingClass,
siderCollapse ? style['layout-sider_collapsed'] : style['layout-sider']
]"
>
<slot name="sider"></slot>
</aside>
</template>
<!-- Mobile Sider -->
<template v-if="showMobileSider">
<aside
:class="[
'absolute left-0 top-0 w-0 h-full bg-white',
commonClass,
mobileSiderClass,
style['layout-mobile-sider'],
siderCollapse ? 'overflow-hidden' : style['layout-sider']
]"
>
<slot name="sider"></slot>
</aside>
<div
v-show="!siderCollapse"
:class="['absolute left-0 top-0 w-full h-full bg-[rgba(0,0,0,0.2)]', style['layout-mobile-sider-mask']]"
@click="handleClickMask"
></div>
</template>
<!-- Main Content -->
<main
:id="isContentScroll ? scrollElId : undefined"
:class="[
'flex flex-col flex-grow',
commonClass,
contentClass,
leftGapClass,
{ 'overflow-y-auto': isContentScroll }
]"
>
<slot></slot>
</main>
<!-- Footer -->
<template v-if="showFooter">
<footer
v-show="!fullContent"
:class="[
style['layout-footer'],
'flex-shrink-0',
commonClass,
footerClass,
footerLeftGapClass,
{ 'absolute left-0 bottom-0 w-full': fixedFooter }
]"
>
<slot name="footer"></slot>
</footer>
<div
v-show="!fullContent && fixedFooter"
:class="[style['layout-footer-placement'], 'flex-shrink-0 overflow-hidden']"
></div>
</template>
</div>
</div>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'; import { computed } from 'vue';
import type { AdminLayoutProps } from '../../types'; import type { AdminLayoutProps } from '../../types';
import { LAYOUT_SCROLL_EL_ID, LAYOUT_MAX_Z_INDEX, createLayoutCssVars } from './shared'; import { LAYOUT_MAX_Z_INDEX, LAYOUT_SCROLL_EL_ID, createLayoutCssVars } from './shared';
import style from './index.module.css'; import style from './index.module.css';
defineOptions({ defineOptions({
@ -150,31 +28,29 @@ const props = withDefaults(defineProps<AdminLayoutProps>(), {
rightFooter: false rightFooter: false
}); });
const emit = defineEmits<Emits>();
const slots = defineSlots<Slots>();
interface Emits { interface Emits {
/** /** Update siderCollapse */
* update siderCollapse
*/
(e: 'update:siderCollapse', collapse: boolean): void; (e: 'update:siderCollapse', collapse: boolean): void;
} }
const emit = defineEmits<Emits>();
type SlotFn = (props?: Record<string, unknown>) => any; type SlotFn = (props?: Record<string, unknown>) => any;
type Slots = { type Slots = {
/** main */ /** Main */
default?: SlotFn; default?: SlotFn;
/** header */ /** Header */
header?: SlotFn; header?: SlotFn;
/** tab */ /** Tab */
tab?: SlotFn; tab?: SlotFn;
/** sider */ /** Sider */
sider?: SlotFn; sider?: SlotFn;
/** footer */ /** Footer */
footer?: SlotFn; footer?: SlotFn;
}; };
const slots = defineSlots<Slots>();
const cssVars = computed(() => createLayoutCssVars(props)); const cssVars = computed(() => createLayoutCssVars(props));
// config visible // config visible
@ -235,4 +111,126 @@ function handleClickMask() {
} }
</script> </script>
<template>
<div class="relative h-full" :class="[commonClass]" :style="cssVars">
<div
:id="isWrapperScroll ? scrollElId : undefined"
class="flex flex-col h-full"
:class="[commonClass, scrollWrapperClass, { 'overflow-y-auto': isWrapperScroll }]"
>
<!-- Header -->
<template v-if="showHeader">
<header
v-show="!fullContent"
class="flex-shrink-0"
:class="[
style['layout-header'],
commonClass,
headerClass,
headerLeftGapClass,
{ 'absolute top-0 left-0 w-full': fixedHeaderAndTab }
]"
>
<slot name="header"></slot>
</header>
<div
v-show="!fullContent && fixedHeaderAndTab"
class="flex-shrink-0 overflow-hidden"
:class="[style['layout-header-placement']]"
></div>
</template>
<!-- Tab -->
<template v-if="showTab">
<div
class="flex-shrink-0"
:class="[
style['layout-tab'],
commonClass,
tabClass,
{ 'top-0!': fullContent || !showHeader },
leftGapClass,
{ 'absolute left-0 w-full': fixedHeaderAndTab }
]"
>
<slot name="tab"></slot>
</div>
<div
v-show="fullContent || fixedHeaderAndTab"
class="flex-shrink-0 overflow-hidden"
:class="[style['layout-tab-placement']]"
></div>
</template>
<!-- Sider -->
<template v-if="showSider">
<aside
v-show="!fullContent"
class="absolute left-0 top-0 h-full"
:class="[
commonClass,
siderClass,
siderPaddingClass,
siderCollapse ? style['layout-sider_collapsed'] : style['layout-sider']
]"
>
<slot name="sider"></slot>
</aside>
</template>
<!-- Mobile Sider -->
<template v-if="showMobileSider">
<aside
class="absolute left-0 top-0 w-0 h-full bg-white"
:class="[
commonClass,
mobileSiderClass,
style['layout-mobile-sider'],
siderCollapse ? 'overflow-hidden' : style['layout-sider']
]"
>
<slot name="sider"></slot>
</aside>
<div
v-show="!siderCollapse"
class="absolute left-0 top-0 w-full h-full bg-[rgba(0,0,0,0.2)]"
:class="[style['layout-mobile-sider-mask']]"
@click="handleClickMask"
></div>
</template>
<!-- Main Content -->
<main
:id="isContentScroll ? scrollElId : undefined"
class="flex flex-col flex-grow"
:class="[commonClass, contentClass, leftGapClass, { 'overflow-y-auto': isContentScroll }]"
>
<slot></slot>
</main>
<!-- Footer -->
<template v-if="showFooter">
<footer
v-show="!fullContent"
class="flex-shrink-0"
:class="[
style['layout-footer'],
commonClass,
footerClass,
footerLeftGapClass,
{ 'absolute left-0 bottom-0 w-full': fixedFooter }
]"
>
<slot name="footer"></slot>
</footer>
<div
v-show="!fullContent && fixedFooter"
class="flex-shrink-0 overflow-hidden"
:class="[style['layout-footer-placement']]"
></div>
</template>
</div>
</div>
</template>
<style scoped></style> <style scoped></style>

View File

@ -1,18 +1,15 @@
import type { AdminLayoutProps, LayoutCssVarsProps, LayoutCssVars } from '../../types'; import type { AdminLayoutProps, LayoutCssVars, LayoutCssVarsProps } from '../../types';
/** /** The id of the scroll element of the layout */
* the id of the scroll element of the layout
*/
export const LAYOUT_SCROLL_EL_ID = '__SCROLL_EL_ID__'; export const LAYOUT_SCROLL_EL_ID = '__SCROLL_EL_ID__';
/** /** The max z-index of the layout */
* the max z-index of the layout
*/
export const LAYOUT_MAX_Z_INDEX = 100; export const LAYOUT_MAX_Z_INDEX = 100;
/** /**
* create layout css vars by css vars props * Create layout css vars by css vars props
* @param props css vars props *
* @param props Css vars props
*/ */
function createLayoutCssVarsByCssVarsProps(props: LayoutCssVarsProps) { function createLayoutCssVarsByCssVarsProps(props: LayoutCssVarsProps) {
const cssVars: LayoutCssVars = { const cssVars: LayoutCssVars = {
@ -32,7 +29,8 @@ function createLayoutCssVarsByCssVarsProps(props: LayoutCssVarsProps) {
} }
/** /**
* create layout css vars * Create layout css vars
*
* @param props * @param props
*/ */
export function createLayoutCssVars(props: AdminLayoutProps) { export function createLayoutCssVars(props: AdminLayoutProps) {

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch, onMounted } from 'vue'; import { onMounted, ref, watch } from 'vue';
import ColorPicker from '@simonwep/pickr'; import ColorPicker from '@simonwep/pickr';
import '@simonwep/pickr/dist/themes/nano.min.css'; import '@simonwep/pickr/dist/themes/nano.min.css';
@ -7,12 +7,6 @@ defineOptions({
name: 'ColorPicker' name: 'ColorPicker'
}); });
interface Props {
color: string;
palettes?: string[];
disabled?: boolean;
}
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
palettes: () => [ palettes: () => [
'#3b82f6', '#3b82f6',
@ -35,12 +29,18 @@ const props = withDefaults(defineProps<Props>(), {
] ]
}); });
const emit = defineEmits<Emits>();
interface Props {
color: string;
palettes?: string[];
disabled?: boolean;
}
interface Emits { interface Emits {
(e: 'update:color', value: string): void; (e: 'update:color', value: string): void;
} }
const emit = defineEmits<Emits>();
const domRef = ref<HTMLElement | null>(null); const domRef = ref<HTMLElement | null>(null);
const instance = ref<ColorPicker | null>(null); const instance = ref<ColorPicker | null>(null);

View File

@ -1,7 +1,43 @@
<script setup lang="ts">
import type { PageTabProps } from '../../types';
import style from './index.module.css';
defineOptions({
name: 'ButtonTab'
});
defineProps<PageTabProps>();
defineSlots<Slots>();
type SlotFn = (props?: Record<string, unknown>) => any;
type Slots = {
/**
* Slot
*
* The center content of the tab
*/
default?: SlotFn;
/**
* Slot
*
* The left content of the tab
*/
prefix?: SlotFn;
/**
* Slot
*
* The right content of the tab
*/
suffix?: SlotFn;
};
</script>
<template> <template>
<div <div
class=":soy: relative inline-flex justify-center items-center gap-12px px-12px py-4px border-1px border-solid rounded-4px cursor-pointer whitespace-nowrap"
:class="[ :class="[
':soy: relative inline-flex justify-center items-center gap-12px px-12px py-4px border-1px border-solid rounded-4px cursor-pointer whitespace-nowrap',
style['button-tab'], style['button-tab'],
{ [style['button-tab_dark']]: darkMode }, { [style['button-tab_dark']]: darkMode },
{ [style['button-tab_active']]: active }, { [style['button-tab_active']]: active },
@ -14,36 +50,4 @@
</div> </div>
</template> </template>
<script setup lang="ts">
import style from './index.module.css';
import type { PageTabProps } from '../../types';
defineOptions({
name: 'ButtonTab'
});
defineProps<PageTabProps>();
type SlotFn = (props?: Record<string, unknown>) => any;
type Slots = {
/**
* slot
* @description the center content of the tab
*/
default?: SlotFn;
/**
* slot
* @description the left content of the tab
*/
prefix?: SlotFn;
/**
* slot
* @description the right content of the tab
*/
suffix?: SlotFn;
};
defineSlots<Slots>();
</script>
<style scoped></style> <style scoped></style>

View File

@ -1,5 +1,11 @@
<script setup lang="ts">
defineOptions({
name: 'ChromeTabBg'
});
</script>
<template> <template>
<svg style="width: 100%; height: 100%"> <svg class="wh-full">
<defs> <defs>
<symbol id="geometry-left" viewBox="0 0 214 36"> <symbol id="geometry-left" viewBox="0 0 214 36">
<path d="M17 0h197v36H0v-2c4.5 0 9-3.5 9-8V8c0-4.5 3.5-8 8-8z"></path> <path d="M17 0h197v36H0v-2c4.5 0 9-3.5 9-8V8c0-4.5 3.5-8 8-8z"></path>
@ -22,10 +28,4 @@
</svg> </svg>
</template> </template>
<script setup lang="ts">
defineOptions({
name: 'ChromeTabBg'
});
</script>
<style scoped></style> <style scoped></style>

View File

@ -1,27 +1,7 @@
<template>
<div
:class="[
':soy: relative inline-flex justify-center items-center gap-16px -mr-18px px-24px py-6px cursor-pointer whitespace-nowrap',
style['chrome-tab'],
{ [style['chrome-tab_dark']]: darkMode },
{ [style['chrome-tab_active']]: active },
{ [style['chrome-tab_active_dark']]: active && darkMode }
]"
>
<div :class="[':soy: absolute left-0 top-0 -z-1 w-full h-full pointer-events-none', style['chrome-tab__bg']]">
<ChromeTabBg />
</div>
<slot name="prefix"></slot>
<slot></slot>
<slot name="suffix"></slot>
<div :class="[':soy: absolute right-7px w-1px h-16px bg-#1f2225', style['chrome-tab-divider']]"></div>
</div>
</template>
<script setup lang="ts"> <script setup lang="ts">
import type { PageTabProps } from '../../types';
import ChromeTabBg from './chrome-tab-bg.vue'; import ChromeTabBg from './chrome-tab-bg.vue';
import style from './index.module.css'; import style from './index.module.css';
import type { PageTabProps } from '../../types';
defineOptions({ defineOptions({
name: 'ChromeTab' name: 'ChromeTab'
@ -29,27 +9,50 @@ defineOptions({
defineProps<PageTabProps>(); defineProps<PageTabProps>();
defineSlots<Slots>();
type SlotFn = (props?: Record<string, unknown>) => any; type SlotFn = (props?: Record<string, unknown>) => any;
type Slots = { type Slots = {
/** /**
* slot * Slot
* @description the center content of the tab *
* The center content of the tab
*/ */
default?: SlotFn; default?: SlotFn;
/** /**
* slot * Slot
* @description the left content of the tab *
* The left content of the tab
*/ */
prefix?: SlotFn; prefix?: SlotFn;
/** /**
* slot * Slot
* @description the right content of the tab *
* The right content of the tab
*/ */
suffix?: SlotFn; suffix?: SlotFn;
}; };
defineSlots<Slots>();
</script> </script>
<template>
<div
class=":soy: relative inline-flex justify-center items-center gap-16px -mr-18px px-24px py-6px cursor-pointer whitespace-nowrap"
:class="[
style['chrome-tab'],
{ [style['chrome-tab_dark']]: darkMode },
{ [style['chrome-tab_active']]: active },
{ [style['chrome-tab_active_dark']]: active && darkMode }
]"
>
<div class=":soy: absolute left-0 top-0 -z-1 w-full h-full pointer-events-none" :class="[style['chrome-tab__bg']]">
<ChromeTabBg />
</div>
<slot name="prefix"></slot>
<slot></slot>
<slot name="suffix"></slot>
<div class=":soy: absolute right-7px w-1px h-16px bg-#1f2225" :class="[style['chrome-tab-divider']]"></div>
</div>
</template>
<style scoped></style> <style scoped></style>

View File

@ -1,3 +1,19 @@
<script setup lang="ts">
defineOptions({
name: 'IconClose'
});
const emit = defineEmits<Emits>();
interface Emits {
(e: 'click'): void;
}
function handleClick() {
emit('click');
}
</script>
<template> <template>
<div <div
class=":soy: relative inline-flex justify-center items-center w-16px h-16px text-14px rd-50%" class=":soy: relative inline-flex justify-center items-center w-16px h-16px text-14px rd-50%"
@ -12,20 +28,4 @@
</div> </div>
</template> </template>
<script setup lang="ts">
defineOptions({
name: 'IconClose'
});
interface Emits {
(e: 'click'): void;
}
const emit = defineEmits<Emits>();
function handleClick() {
emit('click');
}
</script>
<style scoped></style> <style scoped></style>

View File

@ -11,4 +11,5 @@ declare const styles: {
readonly 'chrome-tab_dark': string; readonly 'chrome-tab_dark': string;
readonly 'chrome-tab-divider': string; readonly 'chrome-tab-divider': string;
}; };
export = styles;
export default styles;

View File

@ -1,26 +1,12 @@
<template>
<component :is="activeTabComponent.component" :class="activeTabComponent.class" :style="cssVars" v-bind="bindProps">
<template #prefix>
<slot name="prefix"></slot>
</template>
<slot></slot>
<template #suffix>
<slot name="suffix">
<SvgIconClose v-if="closable" :class="[style['icon_close']]" @click="handleClose" />
</slot>
</template>
</component>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'; import { computed } from 'vue';
import type { Component } from 'vue'; import type { Component } from 'vue';
import { createTabCssVars, ACTIVE_COLOR } from './shared'; import type { PageTabMode, PageTabProps } from '../../types';
import { ACTIVE_COLOR, createTabCssVars } from './shared';
import ChromeTab from './chrome-tab.vue'; import ChromeTab from './chrome-tab.vue';
import ButtonTab from './button-tab.vue'; import ButtonTab from './button-tab.vue';
import SvgIconClose from './icon-close.vue'; import SvgIconClose from './icon-close.vue';
import style from './index.module.css'; import style from './index.module.css';
import type { PageTabProps, PageTabMode } from '../../types';
defineOptions({ defineOptions({
name: 'PageTab' name: 'PageTab'
@ -33,34 +19,37 @@ const props = withDefaults(defineProps<PageTabProps>(), {
closable: true closable: true
}); });
const emit = defineEmits<Emits>();
defineSlots<Slots>();
interface Emits { interface Emits {
(e: 'close'): void; (e: 'close'): void;
} }
const emit = defineEmits<Emits>();
type SlotFn = (props?: Record<string, unknown>) => any; type SlotFn = (props?: Record<string, unknown>) => any;
type Slots = { type Slots = {
/** /**
* slot * Slot
* @description the center content of the tab *
* The center content of the tab
*/ */
default?: SlotFn; default?: SlotFn;
/** /**
* slot * Slot
* @description the left content of the tab *
* The left content of the tab
*/ */
prefix?: SlotFn; prefix?: SlotFn;
/** /**
* slot * Slot
* @description the right content of the tab *
* The right content of the tab
*/ */
suffix?: SlotFn; suffix?: SlotFn;
}; };
defineSlots<Slots>();
const activeTabComponent = computed(() => { const activeTabComponent = computed(() => {
const { mode, chromeClass, buttonClass } = props; const { mode, chromeClass, buttonClass } = props;
@ -91,4 +80,18 @@ function handleClose() {
} }
</script> </script>
<template>
<component :is="activeTabComponent.component" :class="activeTabComponent.class" :style="cssVars" v-bind="bindProps">
<template #prefix>
<slot name="prefix"></slot>
</template>
<slot></slot>
<template #suffix>
<slot name="suffix">
<SvgIconClose v-if="closable" :class="[style['icon_close']]" @click="handleClose" />
</slot>
</template>
</component>
</template>
<style scoped></style> <style scoped></style>

View File

@ -1,9 +1,7 @@
import { addColorAlpha, transformColorWithOpacity } from '@sa/utils'; import { addColorAlpha, transformColorWithOpacity } from '@sa/utils';
import type { PageTabCssVarsProps, PageTabCssVars } from '../../types'; import type { PageTabCssVars, PageTabCssVarsProps } from '../../types';
/** /** The active color of the tab */
* the active color of the tab
*/
export const ACTIVE_COLOR = '#1890ff'; export const ACTIVE_COLOR = '#1890ff';
function createCssVars(props: PageTabCssVarsProps) { function createCssVars(props: PageTabCssVarsProps) {

View File

@ -1,146 +1,156 @@
/** /** Header config */
* header config
*/
interface AdminLayoutHeaderConfig { interface AdminLayoutHeaderConfig {
/** /**
* whether header is visible * Whether header is visible
*
* @default true * @default true
*/ */
headerVisible?: boolean; headerVisible?: boolean;
/** /**
* header class * Header class
*
* @default '' * @default ''
*/ */
headerClass?: string; headerClass?: string;
/** /**
* header height * Header height
*
* @default 56px * @default 56px
*/ */
headerHeight?: number; headerHeight?: number;
} }
/** /** Tab config */
* tab config
*/
interface AdminLayoutTabConfig { interface AdminLayoutTabConfig {
/** /**
* whether tab is visible * Whether tab is visible
*
* @default true * @default true
*/ */
tabVisible?: boolean; tabVisible?: boolean;
/** /**
* tab class * Tab class
*
* @default '' * @default ''
*/ */
tabClass?: string; tabClass?: string;
/** /**
* tab height * Tab height
*
* @default 48px * @default 48px
*/ */
tabHeight?: number; tabHeight?: number;
} }
/** /** Sider config */
* sider config
*/
interface AdminLayoutSiderConfig { interface AdminLayoutSiderConfig {
/** /**
* whether sider is visible * Whether sider is visible
*
* @default true * @default true
*/ */
siderVisible?: boolean; siderVisible?: boolean;
/** /**
* sider class * Sider class
*
* @default '' * @default ''
*/ */
siderClass?: string; siderClass?: string;
/** /**
* mobile sider class * Mobile sider class
*
* @default '' * @default ''
*/ */
mobileSiderClass?: string; mobileSiderClass?: string;
/** /**
* sider collapse status * Sider collapse status
*
* @default false * @default false
*/ */
siderCollapse?: boolean; siderCollapse?: boolean;
/** /**
* sider width when collapse is false * Sider width when collapse is false
*
* @default '220px' * @default '220px'
*/ */
siderWidth?: number; siderWidth?: number;
/** /**
* sider width when collapse is true * Sider width when collapse is true
*
* @default '64px' * @default '64px'
*/ */
siderCollapsedWidth?: number; siderCollapsedWidth?: number;
} }
/** /** Content config */
* content config
*/
export interface AdminLayoutContentConfig { export interface AdminLayoutContentConfig {
/** /**
* content class * Content class
*
* @default '' * @default ''
*/ */
contentClass?: string; contentClass?: string;
/** /**
* whether content is full the page * Whether content is full the page
* @description if true, other elements will be hidden by `display: none` *
* If true, other elements will be hidden by `display: none`
*/ */
fullContent?: boolean; fullContent?: boolean;
} }
/** /** Footer config */
* footer config
*/
export interface AdminLayoutFooterConfig { export interface AdminLayoutFooterConfig {
/** /**
* whether footer is visible * Whether footer is visible
*
* @default true * @default true
*/ */
footerVisible?: boolean; footerVisible?: boolean;
/** /**
* whether footer is fixed * Whether footer is fixed
*
* @default true * @default true
*/ */
fixedFooter?: boolean; fixedFooter?: boolean;
/** /**
* footer class * Footer class
*
* @default '' * @default ''
*/ */
footerClass?: string; footerClass?: string;
/** /**
* footer height * Footer height
*
* @default 48px * @default 48px
*/ */
footerHeight?: number; footerHeight?: number;
/** /**
* whether footer is on the right side * Whether footer is on the right side
* @description when the layout is vertical, the footer is on the right side *
* When the layout is vertical, the footer is on the right side
*/ */
rightFooter?: boolean; rightFooter?: boolean;
} }
/** /**
* layout mode * Layout mode
* - horizontal *
* - vertical * - Horizontal
* - Vertical
*/ */
export type LayoutMode = 'horizontal' | 'vertical'; export type LayoutMode = 'horizontal' | 'vertical';
/** /**
* the scroll mode when content overflow * The scroll mode when content overflow
* - wrapper: the layout component's wrapper element has a scrollbar *
* - content: the layout component's content element has a scrollbar * - Wrapper: the layout component's wrapper element has a scrollbar
* - Content: the layout component's content element has a scrollbar
*
* @default 'wrapper' * @default 'wrapper'
*/ */
export type LayoutScrollMode = 'wrapper' | 'content'; export type LayoutScrollMode = 'wrapper' | 'content';
/** /** Admin layout props */
* admin layout props
*/
export interface AdminLayoutProps export interface AdminLayoutProps
extends AdminLayoutHeaderConfig, extends AdminLayoutHeaderConfig,
AdminLayoutTabConfig, AdminLayoutTabConfig,
@ -148,52 +158,58 @@ export interface AdminLayoutProps
AdminLayoutContentConfig, AdminLayoutContentConfig,
AdminLayoutFooterConfig { AdminLayoutFooterConfig {
/** /**
* layout mode * Layout mode
*
* - {@link LayoutMode} * - {@link LayoutMode}
*/ */
mode?: LayoutMode; mode?: LayoutMode;
/** is mobile layout */ /** Is mobile layout */
isMobile?: boolean; isMobile?: boolean;
/** /**
* scroll mode * Scroll mode
*
* - {@link ScrollMode} * - {@link ScrollMode}
*/ */
scrollMode?: LayoutScrollMode; scrollMode?: LayoutScrollMode;
/** /**
* the id of the scroll element of the layout * The id of the scroll element of the layout
* @description it can be used to get the corresponding Dom and scroll it *
* It can be used to get the corresponding Dom and scroll it
*
* @example
* use the default id by import
* ```ts
* import { adminLayoutScrollElId } from '@sa/vue-materials';
* ```
*
* @default * @default
* ```ts * ```ts
* const adminLayoutScrollElId = '__ADMIN_LAYOUT_SCROLL_EL_ID__' * const adminLayoutScrollElId = '__ADMIN_LAYOUT_SCROLL_EL_ID__'
* ``` * ```
* @example use the default id by import
* ```ts
* import { adminLayoutScrollElId } from '@sa/vue-materials';
* ```
*/ */
scrollElId?: string; scrollElId?: string;
/** /** The class of the scroll element */
* the class of the scroll element
*/
scrollElClass?: string; scrollElClass?: string;
/** /** The class of the scroll wrapper element */
* the class of the scroll wrapper element
*/
scrollWrapperClass?: string; scrollWrapperClass?: string;
/** /**
* the common class of the layout * The common class of the layout
* @description is can be used to configure the transition animation *
* Is can be used to configure the transition animation
*
* @default 'transition-all-300' * @default 'transition-all-300'
*/ */
commonClass?: string; commonClass?: string;
/** /**
* whether fix the header and tab * Whether fix the header and tab
*
* @default true * @default true
*/ */
fixedTop?: boolean; fixedTop?: boolean;
/** /**
* the max z-index of the layout * The max z-index of the layout
* @description the z-index of Header,Tab,Sider and Footer will not exceed this value *
* The z-index of Header,Tab,Sider and Footer will not exceed this value
*/ */
maxZIndex?: number; maxZIndex?: number;
} }
@ -222,48 +238,44 @@ export type LayoutCssVars = {
}; };
/** /**
* the mode of the tab * The mode of the tab
* - button: button style *
* - chrome: chrome style * - Button: button style
* - Chrome: chrome style
*
* @default chrome * @default chrome
*/ */
export type PageTabMode = 'button' | 'chrome'; export type PageTabMode = 'button' | 'chrome';
export interface PageTabProps { export interface PageTabProps {
/** /** Whether is dark mode */
* whether is dark mode
*/
darkMode?: boolean; darkMode?: boolean;
/** /**
* the mode of the tab * The mode of the tab
*
* - {@link TabMode} * - {@link TabMode}
*/ */
mode?: PageTabMode; mode?: PageTabMode;
/** /**
* the common class of the layout * The common class of the layout
* @description is can be used to configure the transition animation *
* Is can be used to configure the transition animation
*
* @default 'transition-all-300' * @default 'transition-all-300'
*/ */
commonClass?: string; commonClass?: string;
/** /** The class of the button tab */
* the class of the button tab
*/
buttonClass?: string; buttonClass?: string;
/** /** The class of the chrome tab */
* the class of the chrome tab
*/
chromeClass?: string; chromeClass?: string;
/** /** Whether the tab is active */
* whether the tab is active
*/
active?: boolean; active?: boolean;
/** /** The color of the active tab */
* the color of the active tab
*/
activeColor?: string; activeColor?: string;
/** /**
* whether the tab is closable * Whether the tab is closable
* @description show the close icon when true *
* Show the close icon when true
*/ */
closable?: boolean; closable?: boolean;
} }

View File

@ -1,19 +1,19 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".", "baseUrl": ".",
"module": "ESNext", "module": "ESNext",
"target": "ESNext",
"lib": ["DOM", "ESNext"],
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "preserve",
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"noUnusedLocals": true, "types": ["node"],
"strict": true,
"strictNullChecks": true, "strictNullChecks": true,
"forceConsistentCasingInFileNames": true, "noUnusedLocals": true,
"types": ["node"] "allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
}, },
"include": ["src/**/*"], "include": ["src/**/*"],
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]

View File

@ -6,9 +6,7 @@
}, },
"typesVersions": { "typesVersions": {
"*": { "*": {
"*": [ "*": ["./src/*"]
"./src/*"
]
} }
}, },
"dependencies": { "dependencies": {

View File

@ -1,19 +1,19 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".", "baseUrl": ".",
"module": "ESNext", "module": "ESNext",
"target": "ESNext",
"lib": ["DOM", "ESNext"],
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "preserve",
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"noUnusedLocals": true, "types": ["node"],
"strict": true,
"strictNullChecks": true, "strictNullChecks": true,
"forceConsistentCasingInFileNames": true, "noUnusedLocals": true,
"types": ["node"] "allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
}, },
"include": ["src/**/*"], "include": ["src/**/*"],
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]

View File

@ -1,6 +1,6 @@
#!/usr/bin/env node #!/usr/bin/env node
const path = require('path'); const path = require('node:path');
const jiti = require('jiti')(__filename); const jiti = require('jiti')(__filename);
jiti(path.resolve(__dirname, './src/index.ts')); jiti(path.resolve(__dirname, './src/index.ts'));

View File

@ -1,19 +1,17 @@
{ {
"name": "@sa/scripts", "name": "@sa/scripts",
"version": "1.0.0", "version": "1.0.0",
"bin": {
"sa": "./bin.cjs"
},
"exports": { "exports": {
".": "./src/index.ts" ".": "./src/index.ts"
}, },
"typesVersions": { "typesVersions": {
"*": { "*": {
"*": [ "*": ["./src/*"]
"./src/*"
]
} }
}, },
"bin": {
"sa": "./bin.cjs"
},
"devDependencies": { "devDependencies": {
"c12": "1.5.1", "c12": "1.5.1",
"cac": "6.7.14", "cac": "6.7.14",
@ -21,8 +19,8 @@
"enquirer": "2.4.1", "enquirer": "2.4.1",
"execa": "8.0.1", "execa": "8.0.1",
"jiti": "1.21.0", "jiti": "1.21.0",
"lint-staged": "15.1.0", "lint-staged": "15.2.0",
"npm-check-updates": "16.14.6", "npm-check-updates": "16.14.12",
"rimraf": "5.0.5" "rimraf": "5.0.5"
} }
} }

View File

@ -1,7 +1,7 @@
import path from 'node:path'; import path from 'node:path';
import { readFileSync } from 'node:fs'; import { readFileSync } from 'node:fs';
import enquirer from 'enquirer'; import enquirer from 'enquirer';
import { bgRed, red, green } from 'kolorist'; import { bgRed, green, red } from 'kolorist';
import { execCommand } from '../shared'; import { execCommand } from '../shared';
import type { CliOption } from '../types'; import type { CliOption } from '../types';

View File

@ -1,3 +1,4 @@
import process from 'node:process';
import { loadConfig } from 'c12'; import { loadConfig } from 'c12';
import type { CliOption } from '../types'; import type { CliOption } from '../types';

View File

@ -1,7 +1,7 @@
import cac from 'cac'; import cac from 'cac';
import { blue, lightGreen } from 'kolorist'; import { blue, lightGreen } from 'kolorist';
import { version } from '../package.json'; import { version } from '../package.json';
import { cleanup, updatePkg, gitCommit, gitCommitVerify, prettierWrite, execLintStaged } from './commands'; import { cleanup, execLintStaged, gitCommit, gitCommitVerify, prettierWrite, updatePkg } from './commands';
import { loadCliOptions } from './config'; import { loadCliOptions } from './config';
type Command = 'cleanup' | 'update-pkg' | 'git-commit' | 'git-commit-verify' | 'prettier-write' | 'lint-staged'; type Command = 'cleanup' | 'update-pkg' | 'git-commit' | 'git-commit-verify' | 'prettier-write' | 'lint-staged';

View File

@ -1,37 +1,33 @@
export interface CliOption { export interface CliOption {
/** /** The project root directory */
* the project root directory
*/
cwd: string; cwd: string;
/** /**
* cleanup dirs * Cleanup dirs
*
* Glob pattern syntax {@link https://github.com/isaacs/minimatch}
*
* @default * @default
* ```json * ```json
* ["** /dist", "** /pnpm-lock.yaml", "** /node_modules", "!node_modules/**"] * ["** /dist", "** /pnpm-lock.yaml", "** /node_modules", "!node_modules/**"]
* ``` * ```
* @description glob pattern syntax {@link https://github.com/isaacs/minimatch}
*/ */
cleanupDirs: string[]; cleanupDirs: string[];
/** /** Git commit types */
* git commit types
*/
gitCommitTypes: [string, string][]; gitCommitTypes: [string, string][];
/** /** Git commit scopes */
* git commit scopes
*/
gitCommitScopes: [string, string][]; gitCommitScopes: [string, string][];
/** /**
* npm-check-updates command args * Npm-check-updates command args
* @default ["--deep","-u"] *
* @default ['--deep', '-u']
*/ */
ncuCommandArgs: string[]; ncuCommandArgs: string[];
/** /**
* prettier write glob * Prettier write glob
* @description glob pattern syntax {@link https://github.com/micromatch/micromatch} *
* Glob pattern syntax {@link https://github.com/micromatch/micromatch}
*/ */
prettierWriteGlob: string[]; prettierWriteGlob: string[];
/** /** Lint-staged config */
* lint-staged config
*/
lintStagedConfig: Record<string, string | string[]>; lintStagedConfig: Record<string, string | string[]>;
} }

View File

@ -1,19 +1,19 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".", "baseUrl": ".",
"module": "ESNext", "module": "ESNext",
"target": "ESNext",
"lib": ["DOM", "ESNext"],
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "preserve",
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"noUnusedLocals": true, "types": ["node"],
"strict": true,
"strictNullChecks": true, "strictNullChecks": true,
"forceConsistentCasingInFileNames": true, "noUnusedLocals": true,
"types": ["node"] "allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
}, },
"include": ["src/**/*", "typings/**/*"], "include": ["src/**/*", "typings/**/*"],
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]

View File

@ -6,9 +6,7 @@
}, },
"typesVersions": { "typesVersions": {
"*": { "*": {
"*": [ "*": ["./src/*"]
"./src/*"
]
} }
} }
} }

View File

@ -1,19 +1,19 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".", "baseUrl": ".",
"module": "ESNext", "module": "ESNext",
"target": "ESNext",
"lib": ["DOM", "ESNext"],
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "preserve",
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"noUnusedLocals": true, "types": ["node"],
"strict": true,
"strictNullChecks": true, "strictNullChecks": true,
"forceConsistentCasingInFileNames": true, "noUnusedLocals": true,
"types": ["node"] "allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
}, },
"include": ["src/**/*"], "include": ["src/**/*"],
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]

View File

@ -6,9 +6,7 @@
}, },
"typesVersions": { "typesVersions": {
"*": { "*": {
"*": [ "*": ["./src/*"]
"./src/*"
]
} }
}, },
"dependencies": { "dependencies": {

View File

@ -6,29 +6,32 @@ import type { AnyColor, HsvColor, RgbColor } from 'colord';
extend([namesPlugin, mixPlugin]); extend([namesPlugin, mixPlugin]);
/** /**
* add color alpha * Add color alpha
* @param color - color *
* @param alpha - alpha (0 - 1) * @param color - Color
* @param alpha - Alpha (0 - 1)
*/ */
export function addColorAlpha(color: string, alpha: number) { export function addColorAlpha(color: string, alpha: number) {
return colord(color).alpha(alpha).toHex(); return colord(color).alpha(alpha).toHex();
} }
/** /**
* mix color * Mix color
* @param firstColor - first color *
* @param secondColor - second color * @param firstColor - First color
* @param ratio - the ratio of the second color (0 - 1) * @param secondColor - Second color
* @param ratio - The ratio of the second color (0 - 1)
*/ */
export function mixColor(firstColor: string, secondColor: string, ratio: number) { export function mixColor(firstColor: string, secondColor: string, ratio: number) {
return colord(firstColor).mix(secondColor, ratio).toHex(); return colord(firstColor).mix(secondColor, ratio).toHex();
} }
/** /**
* transform color with opacity to similar color without opacity * Transform color with opacity to similar color without opacity
* @param color - color *
* @param alpha - alpha (0 - 1) * @param color - Color
* @param bgColor background color (usually white or black) * @param alpha - Alpha (0 - 1)
* @param bgColor Background color (usually white or black)
*/ */
export function transformColorWithOpacity(color: string, alpha: number, bgColor = '#ffffff') { export function transformColorWithOpacity(color: string, alpha: number, bgColor = '#ffffff') {
const originColor = addColorAlpha(color, alpha); const originColor = addColorAlpha(color, alpha);
@ -50,67 +53,57 @@ export function transformColorWithOpacity(color: string, alpha: number, bgColor
} }
/** /**
* is white color * Is white color
* @param color - color *
* @param color - Color
*/ */
export function isWhiteColor(color: string) { export function isWhiteColor(color: string) {
return colord(color).isEqual('#ffffff'); return colord(color).isEqual('#ffffff');
} }
/** /**
* get rgb of color * Get rgb of color
* @param color color *
* @param color Color
*/ */
export function getRgbOfColor(color: string) { export function getRgbOfColor(color: string) {
return colord(color).toRgb(); return colord(color).toRgb();
} }
/** /** Hue step */
* hue step
*/
const hueStep = 2; const hueStep = 2;
/** /** Saturation step, light color part */
* saturation step, light color part
*/
const saturationStep = 16; const saturationStep = 16;
/** /** Saturation step, dark color part */
* saturation step, dark color part
*/
const saturationStep2 = 5; const saturationStep2 = 5;
/** /** Brightness step, light color part */
* brightness step, light color part
*/
const brightnessStep1 = 5; const brightnessStep1 = 5;
/** /** Brightness step, dark color part */
* brightness step, dark color part
*/
const brightnessStep2 = 15; const brightnessStep2 = 15;
/** /** Light color count, main color up */
* light color count, main color up
*/
const lightColorCount = 5; const lightColorCount = 5;
/** /** Dark color count, main color down */
* dark color count, main color down
*/
const darkColorCount = 4; const darkColorCount = 4;
/** /**
* the color index of color palette * The color index of color palette
* @description from left to right, the color is from light to dark, 6 is main color *
* From left to right, the color is from light to dark, 6 is main color
*/ */
type ColorIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10; type ColorIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
/** /**
* get color palette (from left to right, the color is from light to dark, 6 is main color) * Get color palette (from left to right, the color is from light to dark, 6 is main color)
* @param color - color *
* @param index - the color index of color palette (the main color index is 6) * @param color - Color
* @returns hex color * @param index - The color index of color palette (the main color index is 6)
* @returns Hex color
*/ */
export function getColorPalette(color: AnyColor, index: ColorIndex): string { export function getColorPalette(color: AnyColor, index: ColorIndex): string {
const transformColor = colord(color); const transformColor = colord(color);
if (!transformColor.isValid()) { if (!transformColor.isValid()) {
throw Error('invalid input color value'); throw new Error('invalid input color value');
} }
if (index === 6) { if (index === 6) {
@ -130,9 +123,7 @@ export function getColorPalette(color: AnyColor, index: ColorIndex): string {
return colord(newHsv).toHex(); return colord(newHsv).toHex();
} }
/** /** Map of dark color index and opacity */
* map of dark color index and opacity
*/
const darkColorMap = [ const darkColorMap = [
{ index: 7, opacity: 0.15 }, { index: 7, opacity: 0.15 },
{ index: 6, opacity: 0.25 }, { index: 6, opacity: 0.25 },
@ -147,10 +138,11 @@ const darkColorMap = [
]; ];
/** /**
* get color palettes * Get color palettes
* @param color - color *
* @param darkTheme - dark theme * @param color - Color
* @param darkThemeMixColor - dark theme mix color (default: #141414) * @param darkTheme - Dark theme
* @param darkThemeMixColor - Dark theme mix color (default: #141414)
*/ */
export function getColorPalettes(color: AnyColor, darkTheme = false, darkThemeMixColor = '#141414'): string[] { export function getColorPalettes(color: AnyColor, darkTheme = false, darkThemeMixColor = '#141414'): string[] {
const indexes: ColorIndex[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const indexes: ColorIndex[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
@ -171,10 +163,11 @@ export function getColorPalettes(color: AnyColor, darkTheme = false, darkThemeMi
} }
/** /**
* get hue * Get hue
* @param hsv - hsv format color *
* @param i - the relative distance from 6 * @param hsv - Hsv format color
* @param isLight - is light color * @param i - The relative distance from 6
* @param isLight - Is light color
*/ */
function getHue(hsv: HsvColor, i: number, isLight: boolean) { function getHue(hsv: HsvColor, i: number, isLight: boolean) {
let hue: number; let hue: number;
@ -199,10 +192,11 @@ function getHue(hsv: HsvColor, i: number, isLight: boolean) {
} }
/** /**
* get saturation * Get saturation
* @param hsv - hsv format color *
* @param i - the relative distance from 6 * @param hsv - Hsv format color
* @param isLight - is light color * @param i - The relative distance from 6
* @param isLight - Is light color
*/ */
function getSaturation(hsv: HsvColor, i: number, isLight: boolean) { function getSaturation(hsv: HsvColor, i: number, isLight: boolean) {
if (hsv.h === 0 && hsv.s === 0) { if (hsv.h === 0 && hsv.s === 0) {
@ -235,10 +229,11 @@ function getSaturation(hsv: HsvColor, i: number, isLight: boolean) {
} }
/** /**
* get value of hsv * Get value of hsv
* @param hsv - hsv format color *
* @param i - the relative distance from 6 * @param hsv - Hsv format color
* @param isLight - is light color * @param i - The relative distance from 6
* @param isLight - Is light color
*/ */
function getValue(hsv: HsvColor, i: number, isLight: boolean) { function getValue(hsv: HsvColor, i: number, isLight: boolean) {
let value: number; let value: number;

View File

@ -1,9 +1,7 @@
import CryptoJS from 'crypto-js'; import CryptoJS from 'crypto-js';
export class Crypto<T extends object> { export class Crypto<T extends object> {
/** /** Secret */
* secret
*/
secret: string; secret: string;
constructor(secret: string) { constructor(secret: string) {

View File

@ -1,8 +1,6 @@
import localforage from 'localforage'; import localforage from 'localforage';
/** /** The storage type */
* the storage type
*/
export type StorageType = 'local' | 'session'; export type StorageType = 'local' | 'session';
export function createStorage<T extends object>(type: StorageType) { export function createStorage<T extends object>(type: StorageType) {
@ -10,9 +8,10 @@ export function createStorage<T extends object>(type: StorageType) {
const storage = { const storage = {
/** /**
* set session * Set session
* @param key session key *
* @param value session value * @param key Session key
* @param value Session value
*/ */
set<K extends keyof T>(key: K, value: T[K]) { set<K extends keyof T>(key: K, value: T[K]) {
const json = JSON.stringify(value); const json = JSON.stringify(value);
@ -20,8 +19,9 @@ export function createStorage<T extends object>(type: StorageType) {
stg.setItem(key as string, json); stg.setItem(key as string, json);
}, },
/** /**
* get session * Get session
* @param key session key *
* @param key Session key
*/ */
get<K extends keyof T>(key: K): T[K] | null { get<K extends keyof T>(key: K): T[K] | null {
const json = stg.getItem(key as string); const json = stg.getItem(key as string);

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@ import { computed } from 'vue';
import { NConfigProvider, darkTheme } from 'naive-ui'; import { NConfigProvider, darkTheme } from 'naive-ui';
import { useAppStore } from './store/modules/app'; import { useAppStore } from './store/modules/app';
import { useThemeStore } from './store/modules/theme'; import { useThemeStore } from './store/modules/theme';
import { naiveLocales, naiveDateLocales } from './locales/naive'; import { naiveDateLocales, naiveLocales } from './locales/naive';
defineOptions({ defineOptions({
name: 'App' name: 'App'

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { defineComponent, createTextVNode } from 'vue'; import { createTextVNode, defineComponent } from 'vue';
import { useDialog, useLoadingBar, useMessage, useNotification } from 'naive-ui'; import { useDialog, useLoadingBar, useMessage, useNotification } from 'naive-ui';
defineOptions({ defineOptions({
@ -17,9 +17,8 @@ const ContextHolder = defineComponent({
} }
register(); register();
},
render() { return () => createTextVNode();
return createTextVNode();
} }
}); });
</script> </script>

View File

@ -1,11 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
defineOptions({ name: 'DarkModeContainer' }); defineOptions({ name: 'DarkModeContainer' });
defineProps<Props>();
interface Props { interface Props {
inverted?: boolean; inverted?: boolean;
} }
defineProps<Props>();
</script> </script>
<template> <template>

View File

@ -4,11 +4,14 @@ import { $t } from '@/locales';
defineOptions({ name: 'ExceptionBase' }); defineOptions({ name: 'ExceptionBase' });
const props = defineProps<Props>();
type ExceptionType = '403' | '404' | '500'; type ExceptionType = '403' | '404' | '500';
interface Props { interface Props {
/** /**
* exception type * Exception type
*
* - 403: no permission * - 403: no permission
* - 404: not found * - 404: not found
* - 500: service error * - 500: service error
@ -16,8 +19,6 @@ interface Props {
type: ExceptionType; type: ExceptionType;
} }
const props = defineProps<Props>();
const iconMap: Record<ExceptionType, string> = { const iconMap: Record<ExceptionType, string> = {
'403': 'no-permission', '403': 'no-permission',
'404': 'not-found', '404': 'not-found',

View File

@ -5,17 +5,17 @@ defineOptions({
name: 'FullScreen' name: 'FullScreen'
}); });
defineProps<Props>();
interface Props { interface Props {
full?: boolean; full?: boolean;
} }
defineProps<Props>();
</script> </script>
<template> <template>
<ButtonIcon :key="String(full)" :tooltip-content="full ? $t('icon.fullscreenExit') : $t('icon.fullscreen')"> <ButtonIcon :key="String(full)" :tooltip-content="full ? $t('icon.fullscreenExit') : $t('icon.fullscreen')">
<icon-gridicons-fullscreen-exit v-if="full" /> <IconGridiconsFullscreenExit v-if="full" />
<icon-gridicons-fullscreen v-else /> <IconGridiconsFullscreen v-else />
</ButtonIcon> </ButtonIcon>
</template> </template>

View File

@ -6,31 +6,25 @@ defineOptions({
name: 'LangSwitch' name: 'LangSwitch'
}); });
interface Props {
/**
* current language
*/
lang: App.I18n.LangType;
/**
* language options
*/
langOptions: App.I18n.LangOption[];
/**
* show tooltip
*/
showTooltip?: boolean;
}
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
showTooltip: true showTooltip: true
}); });
const emit = defineEmits<Emits>();
interface Props {
/** Current language */
lang: App.I18n.LangType;
/** Language options */
langOptions: App.I18n.LangOption[];
/** Show tooltip */
showTooltip?: boolean;
}
type Emits = { type Emits = {
(e: 'changeLang', lang: App.I18n.LangType): void; (e: 'changeLang', lang: App.I18n.LangType): void;
}; };
const emits = defineEmits<Emits>();
const tooltipContent = computed(() => { const tooltipContent = computed(() => {
if (!props.showTooltip) return ''; if (!props.showTooltip) return '';
@ -38,7 +32,7 @@ const tooltipContent = computed(() => {
}); });
function changeLang(lang: App.I18n.LangType) { function changeLang(lang: App.I18n.LangType) {
emits('changeLang', lang); emit('changeLang', lang);
} }
</script> </script>

View File

@ -1,22 +1,18 @@
<script lang="ts" setup> <script lang="ts" setup>
import { $t } from '@/locales';
import { computed } from 'vue'; import { computed } from 'vue';
import { $t } from '@/locales';
defineOptions({ name: 'MenuToggler' }); defineOptions({ name: 'MenuToggler' });
const props = defineProps<Props>();
interface Props { interface Props {
/** /** Show collapsed icon */
* show collapsed icon
*/
collapsed?: boolean; collapsed?: boolean;
/** /** Arrow style icon */
* arrow style icon
*/
arrowIcon?: boolean; arrowIcon?: boolean;
} }
const props = defineProps<Props>();
type NumberBool = 0 | 1; type NumberBool = 0 | 1;
const icon = computed(() => { const icon = computed(() => {

View File

@ -1,15 +1,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import { $t } from '@/locales';
import { computed } from 'vue'; import { computed } from 'vue';
import { $t } from '@/locales';
defineOptions({ name: 'PinToggler' }); defineOptions({ name: 'PinToggler' });
const props = defineProps<Props>();
interface Props { interface Props {
pin?: boolean; pin?: boolean;
} }
const props = defineProps<Props>();
const icon = computed(() => (props.pin ? 'mdi-pin-off' : 'mdi-pin')); const icon = computed(() => (props.pin ? 'mdi-pin-off' : 'mdi-pin'));
</script> </script>

View File

@ -5,16 +5,16 @@ defineOptions({
name: 'ReloadButton' name: 'ReloadButton'
}); });
defineProps<Props>();
interface Props { interface Props {
loading?: boolean; loading?: boolean;
} }
defineProps<Props>();
</script> </script>
<template> <template>
<ButtonIcon :tooltip-content="$t('icon.reload')"> <ButtonIcon :tooltip-content="$t('icon.reload')">
<icon-ant-design-reload-outlined :class="{ 'animate-spin animate-duration-750': loading }" /> <IconAntDesignReloadOutlined :class="{ 'animate-spin animate-duration-750': loading }" />
</ButtonIcon> </ButtonIcon>
</template> </template>

View File

@ -3,7 +3,7 @@ defineOptions({ name: 'SystemLogo' });
</script> </script>
<template> <template>
<icon-local-logo /> <IconLocalLogo />
</template> </template>
<style scoped></style> <style scoped></style>

View File

@ -5,32 +5,26 @@ import { $t } from '@/locales';
defineOptions({ name: 'ThemeSchemaSwitch' }); defineOptions({ name: 'ThemeSchemaSwitch' });
interface Props {
/**
* theme schema
*/
themeSchema: UnionKey.ThemeScheme;
/**
* show tooltip
*/
showTooltip?: boolean;
/**
* tooltip placement
*/
tooltipPlacement?: PopoverPlacement;
}
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
showTooltip: true, showTooltip: true,
tooltipPlacement: 'bottom' tooltipPlacement: 'bottom'
}); });
const emit = defineEmits<Emits>();
interface Props {
/** Theme schema */
themeSchema: UnionKey.ThemeScheme;
/** Show tooltip */
showTooltip?: boolean;
/** Tooltip placement */
tooltipPlacement?: PopoverPlacement;
}
interface Emits { interface Emits {
(e: 'switch'): void; (e: 'switch'): void;
} }
const emit = defineEmits<Emits>();
function handleSwitch() { function handleSwitch() {
emit('switch'); emit('switch');
} }

View File

@ -6,16 +6,17 @@ import type { Options } from '@better-scroll/core';
defineOptions({ name: 'BetterScroll' }); defineOptions({ name: 'BetterScroll' });
const props = defineProps<Props>();
interface Props { interface Props {
/** /**
* BetterScroll options * BetterScroll options
*
* @link https://better-scroll.github.io/docs/zh-CN/guide/base-scroll-options.html * @link https://better-scroll.github.io/docs/zh-CN/guide/base-scroll-options.html
*/ */
options: Options; options: Options;
} }
const props = defineProps<Props>();
const bsWrap = ref<HTMLElement>(); const bsWrap = ref<HTMLElement>();
const bsContent = ref<HTMLElement>(); const bsContent = ref<HTMLElement>();
const { width: wrapWidth } = useElementSize(bsWrap); const { width: wrapWidth } = useElementSize(bsWrap);

View File

@ -8,25 +8,6 @@ defineOptions({
inheritAttrs: false inheritAttrs: false
}); });
interface Props {
/**
* button class
*/
class?: string;
/**
* iconify icon name
*/
icon?: string;
/**
* tooltip content
*/
tooltipContent?: string;
/**
* tooltip placement
*/
tooltipPlacement?: PopoverPlacement;
}
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
class: 'h-36px text-icon', class: 'h-36px text-icon',
icon: '', icon: '',
@ -34,6 +15,17 @@ const props = withDefaults(defineProps<Props>(), {
tooltipPlacement: 'bottom' tooltipPlacement: 'bottom'
}); });
interface Props {
/** Button class */
class?: string;
/** Iconify icon name */
icon?: string;
/** Tooltip content */
tooltipContent?: string;
/** Tooltip placement */
tooltipPlacement?: PopoverPlacement;
}
interface ButtonProps { interface ButtonProps {
className: string; className: string;
} }

View File

@ -4,24 +4,21 @@ import { Icon } from '@iconify/vue';
defineOptions({ name: 'SvgIcon' }); defineOptions({ name: 'SvgIcon' });
const props = defineProps<Props>();
/** /**
* props * Props
* - support iconify and local svg icon *
* - if icon and localIcon are passed at the same time, localIcon will be rendered first * - Support iconify and local svg icon
* - If icon and localIcon are passed at the same time, localIcon will be rendered first
*/ */
interface Props { interface Props {
/** /** Iconify icon name */
* iconify icon name
*/
icon?: string; icon?: string;
/** /** Local svg icon name */
* local svg icon name
*/
localIcon?: string; localIcon?: string;
} }
const props = defineProps<Props>();
const attrs = useAttrs(); const attrs = useAttrs();
const bindAttrs = computed<{ class: string; style: string }>(() => ({ const bindAttrs = computed<{ class: string; style: string }>(() => ({
@ -39,9 +36,7 @@ const symbolId = computed(() => {
return `#${prefix}-${icon}`; return `#${prefix}-${icon}`;
}); });
/** /** If localIcon is passed, render localIcon first */
* if localIcon is passed, render localIcon first
*/
const renderLocalIcon = computed(() => props.localIcon || !props.icon); const renderLocalIcon = computed(() => props.localIcon || !props.icon);
</script> </script>

View File

@ -3,9 +3,7 @@ import { computed } from 'vue';
import { getColorPalette } from '@sa/utils'; import { getColorPalette } from '@sa/utils';
interface Props { interface Props {
/** /** Theme color */
* theme color
*/
themeColor: string; themeColor: string;
} }

View File

@ -1,34 +1,25 @@
export const REG_USER_NAME = /^[\u4e00-\u9fa5a-zA-Z0-9_-]{4,16}$/; export const REG_USER_NAME = /^[\u4E00-\u9FA5a-zA-Z0-9_-]{4,16}$/;
/** /** Phone reg */
* phone reg
*/
export const REG_PHONE = export const REG_PHONE =
/^[1](([3][0-9])|([4][01456789])|([5][012356789])|([6][2567])|([7][0-8])|([8][0-9])|([9][012356789]))[0-9]{8}$/; /^[1](([3][0-9])|([4][01456789])|([5][012356789])|([6][2567])|([7][0-8])|([8][0-9])|([9][012356789]))[0-9]{8}$/;
/** /**
* password reg * Password reg
* @description 6-18 characters, including letters, numbers, and underscores *
* 6-18 characters, including letters, numbers, and underscores
*/ */
export const REG_PWD = /^\w{6,18}$/; export const REG_PWD = /^\w{6,18}$/;
/** /** Email reg */
* email reg
*/
export const REG_EMAIL = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/; export const REG_EMAIL = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
/** /** Six digit code reg */
* six digit code reg
*/
export const REG_CODE_SIX = /^\d{6}$/; export const REG_CODE_SIX = /^\d{6}$/;
/** /** Four digit code reg */
* four digit code reg
*/
export const REG_CODE_FOUR = /^\d{4}$/; export const REG_CODE_FOUR = /^\d{4}$/;
/** /** Url reg */
* url reg
*/
export const REG_URL = export const REG_URL =
/(((^https?:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)$/; /(((^https?:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)$/;

View File

@ -1,6 +1,6 @@
import { ref } from 'vue'; import { ref } from 'vue';
import type { FormInst } from 'naive-ui'; import type { FormInst } from 'naive-ui';
import { REG_USER_NAME, REG_PHONE, REG_PWD, REG_CODE_SIX, REG_EMAIL } from '@/constants/reg'; import { REG_CODE_SIX, REG_EMAIL, REG_PHONE, REG_PWD, REG_USER_NAME } from '@/constants/reg';
import { $t } from '@/locales'; import { $t } from '@/locales';
export function useFormRules() { export function useFormRules() {

View File

@ -4,9 +4,11 @@ import type { RouteKey } from '@elegant-router/types';
import { router as globalRouter } from '@/router'; import { router as globalRouter } from '@/router';
/** /**
* router push * Router push
* @description jump to the specified route, it can replace function router.push *
* @param inSetup whether is in vue script setup * Jump to the specified route, it can replace function router.push
*
* @param inSetup Whether is in vue script setup
*/ */
export function useRouterPush(inSetup = true) { export function useRouterPush(inSetup = true) {
const router = inSetup ? useRouter() : globalRouter; const router = inSetup ? useRouter() : globalRouter;
@ -44,9 +46,10 @@ export function useRouterPush(inSetup = true) {
} }
/** /**
* navigate to login page * Navigate to login page
* @param loginModule the login module *
* @param redirectUrl the redirect url, if not specified, it will be the current route fullPath * @param loginModule The login module
* @param redirectUrl The redirect url, if not specified, it will be the current route fullPath
*/ */
async function toLogin(loginModule?: UnionKey.LoginModule, redirectUrl?: string) { async function toLogin(loginModule?: UnionKey.LoginModule, redirectUrl?: string) {
const module = loginModule || 'pwd-login'; const module = loginModule || 'pwd-login';
@ -67,7 +70,8 @@ export function useRouterPush(inSetup = true) {
} }
/** /**
* toggle login module * Toggle login module
*
* @param module * @param module
*/ */
async function toggleLoginModule(module: UnionKey.LoginModule) { async function toggleLoginModule(module: UnionKey.LoginModule) {
@ -76,9 +80,7 @@ export function useRouterPush(inSetup = true) {
return routerPushByKey('login', { query, params: { module } }); return routerPushByKey('login', { query, params: { module } });
} }
/** /** Redirect from login */
* redirect from login
*/
async function redirectFromLogin() { async function redirectFromLogin() {
const redirect = route.value.query?.redirect as string; const redirect = route.value.query?.redirect as string;

View File

@ -1,4 +1,4 @@
import { ref, computed, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useContext } from '@sa/hooks'; import { useContext } from '@sa/hooks';
import { useRouteStore } from '@/store/modules/route'; import { useRouteStore } from '@/store/modules/route';

View File

@ -7,17 +7,15 @@ defineOptions({
name: 'GlobalContent' name: 'GlobalContent'
}); });
interface Props {
/**
* show padding for content
*/
showPadding?: boolean;
}
withDefaults(defineProps<Props>(), { withDefaults(defineProps<Props>(), {
showPadding: true showPadding: true
}); });
interface Props {
/** Show padding for content */
showPadding?: boolean;
}
const appStore = useAppStore(); const appStore = useAppStore();
const themeStore = useThemeStore(); const themeStore = useThemeStore();
const routeStore = useRouteStore(); const routeStore = useRouteStore();

View File

@ -7,37 +7,29 @@ import { useRouteStore } from '@/store/modules/route';
import HorizontalMenu from '../global-menu/base-menu.vue'; import HorizontalMenu from '../global-menu/base-menu.vue';
import GlobalLogo from '../global-logo/index.vue'; import GlobalLogo from '../global-logo/index.vue';
import GlobalBreadcrumb from '../global-breadcrumb/index.vue'; import GlobalBreadcrumb from '../global-breadcrumb/index.vue';
import { useMixMenuContext } from '../../hooks/use-mix-menu';
import ThemeButton from './components/theme-button.vue'; import ThemeButton from './components/theme-button.vue';
import UserAvatar from './components/user-avatar.vue'; import UserAvatar from './components/user-avatar.vue';
import { useMixMenuContext } from '../../hooks/use-mix-menu';
defineOptions({
name: 'GlobalHeader'
});
defineProps<Props>();
const appStore = useAppStore(); const appStore = useAppStore();
const themeStore = useThemeStore(); const themeStore = useThemeStore();
const routeStore = useRouteStore(); const routeStore = useRouteStore();
const { isFullscreen, toggle } = useFullscreen(); const { isFullscreen, toggle } = useFullscreen();
const { menus } = useMixMenuContext(); const { menus } = useMixMenuContext();
defineOptions({
name: 'GlobalHeader'
});
interface Props { interface Props {
/** /** Whether to show the logo */
* whether to show the logo
*/
showLogo?: App.Global.HeaderProps['showLogo']; showLogo?: App.Global.HeaderProps['showLogo'];
/** /** Whether to show the menu toggler */
* whether to show the menu toggler
*/
showMenuToggler?: App.Global.HeaderProps['showMenuToggler']; showMenuToggler?: App.Global.HeaderProps['showMenuToggler'];
/** /** Whether to show the menu */
* whether to show the menu
*/
showMenu?: App.Global.HeaderProps['showMenu']; showMenu?: App.Global.HeaderProps['showMenu'];
} }
defineProps<Props>();
const headerMenus = computed(() => { const headerMenus = computed(() => {
if (themeStore.layout.mode === 'horizontal') { if (themeStore.layout.mode === 'horizontal') {
return routeStore.menus; return routeStore.menus;

View File

@ -5,16 +5,14 @@ defineOptions({
name: 'GlobalLogo' name: 'GlobalLogo'
}); });
interface Props {
/**
* whether to show the title
*/
showTitle?: boolean;
}
withDefaults(defineProps<Props>(), { withDefaults(defineProps<Props>(), {
showTitle: true showTitle: true
}); });
interface Props {
/** Whether to show the title */
showTitle?: boolean;
}
</script> </script>
<template> <template>

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import type { MenuProps, MentionOption } from 'naive-ui'; import type { MentionOption, MenuProps } from 'naive-ui';
import { SimpleScrollbar } from '@sa/materials'; import { SimpleScrollbar } from '@sa/materials';
import type { RouteKey } from '@elegant-router/types'; import type { RouteKey } from '@elegant-router/types';
import { useAppStore } from '@/store/modules/app'; import { useAppStore } from '@/store/modules/app';
@ -13,16 +13,16 @@ defineOptions({
name: 'BaseMenu' name: 'BaseMenu'
}); });
const props = withDefaults(defineProps<Props>(), {
mode: 'vertical'
});
interface Props { interface Props {
darkTheme?: boolean; darkTheme?: boolean;
mode?: MenuProps['mode']; mode?: MenuProps['mode'];
menus: App.Global.Menu[]; menus: App.Global.Menu[];
} }
const props = withDefaults(defineProps<Props>(), {
mode: 'vertical'
});
const route = useRoute(); const route = useRoute();
const appStore = useAppStore(); const appStore = useAppStore();
const themeStore = useThemeStore(); const themeStore = useThemeStore();

View File

@ -11,39 +11,31 @@ defineOptions({
name: 'FirstLevelMenu' name: 'FirstLevelMenu'
}); });
defineProps<Props>();
const emit = defineEmits<Emits>();
interface Props { interface Props {
activeMenuKey?: string; activeMenuKey?: string;
inverted?: boolean; inverted?: boolean;
} }
defineProps<Props>();
interface Emits { interface Emits {
(e: 'select', menu: App.Global.Menu): boolean; (e: 'select', menu: App.Global.Menu): boolean;
} }
const emit = defineEmits<Emits>();
const appStore = useAppStore(); const appStore = useAppStore();
const themeStore = useThemeStore(); const themeStore = useThemeStore();
const routeStore = useRouteStore(); const routeStore = useRouteStore();
interface MixMenuItemProps { interface MixMenuItemProps {
/** /** Menu item label */
* menu item label
*/
label: App.Global.Menu['label']; label: App.Global.Menu['label'];
/** /** Menu item icon */
* menu item icon
*/
icon: App.Global.Menu['icon']; icon: App.Global.Menu['icon'];
/** /** Active menu item */
* active menu item
*/
active: boolean; active: boolean;
/** /** Mini size */
* mini size
*/
isMini: boolean; isMini: boolean;
} }
const [DefineMixMenuItem, MixMenuItem] = createReusableTemplate<MixMenuItemProps>(); const [DefineMixMenuItem, MixMenuItem] = createReusableTemplate<MixMenuItemProps>();

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import FirstLevelMenu from './first-level-menu.vue';
import { useMixMenuContext } from '../../hooks/use-mix-menu';
import { useRouterPush } from '@/hooks/common/router'; import { useRouterPush } from '@/hooks/common/router';
import { useMixMenuContext } from '../../hooks/use-mix-menu';
import FirstLevelMenu from './first-level-menu.vue';
defineOptions({ defineOptions({
name: 'HorizontalMixMenu' name: 'HorizontalMixMenu'

View File

@ -5,9 +5,9 @@ import { useAppStore } from '@/store/modules/app';
import { useRouteStore } from '@/store/modules/route'; import { useRouteStore } from '@/store/modules/route';
import { useThemeStore } from '@/store/modules/theme'; import { useThemeStore } from '@/store/modules/theme';
import { useRouterPush } from '@/hooks/common/router'; import { useRouterPush } from '@/hooks/common/router';
import { useMixMenu } from '../../hooks/use-mix-menu';
import FirstLevelMenu from './first-level-menu.vue'; import FirstLevelMenu from './first-level-menu.vue';
import BaseMenu from './base-menu.vue'; import BaseMenu from './base-menu.vue';
import { useMixMenu } from '../../hooks/use-mix-menu';
defineOptions({ defineOptions({
name: 'VerticalMixMenu' name: 'VerticalMixMenu'

View File

@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'; import { computed } from 'vue';
import type { VNode } from 'vue'; import type { VNode } from 'vue';
import { $t } from '@/locales';
import { useSvgIconRender } from '@sa/hooks'; import { useSvgIconRender } from '@sa/hooks';
import { $t } from '@/locales';
import { useTabStore } from '@/store/modules/tab'; import { useTabStore } from '@/store/modules/tab';
import SvgIcon from '@/components/custom/svg-icon.vue'; import SvgIcon from '@/components/custom/svg-icon.vue';
@ -10,25 +10,21 @@ defineOptions({
name: 'ContextMenu' name: 'ContextMenu'
}); });
const props = withDefaults(defineProps<Props>(), {
excludeKeys: () => [],
disabledKeys: () => []
});
interface Props { interface Props {
/** /** ClientX */
* clientX
*/
x: number; x: number;
/** /** ClientY */
* clientY
*/
y: number; y: number;
tabId: string; tabId: string;
excludeKeys?: App.Global.DropdownKey[]; excludeKeys?: App.Global.DropdownKey[];
disabledKeys?: App.Global.DropdownKey[]; disabledKeys?: App.Global.DropdownKey[];
} }
const props = withDefaults(defineProps<Props>(), {
excludeKeys: () => [],
disabledKeys: () => []
});
const visible = defineModel<boolean>('visible'); const visible = defineModel<boolean>('visible');
const { removeTab, clearTabs, clearLeftTabs, clearRightTabs } = useTabStore(); const { removeTab, clearTabs, clearLeftTabs, clearRightTabs } = useTabStore();

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, watch, nextTick } from 'vue'; import { nextTick, reactive, ref, watch } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useElementBounding } from '@vueuse/core'; import { useElementBounding } from '@vueuse/core';
import { PageTab } from '@sa/materials'; import { PageTab } from '@sa/materials';

View File

@ -7,28 +7,22 @@ defineOptions({
name: 'LayoutModeCard' name: 'LayoutModeCard'
}); });
const props = defineProps<Props>();
const emit = defineEmits<Emits>();
interface Props { interface Props {
/** /** Layout mode */
* layout mode
*/
mode: UnionKey.ThemeLayoutMode; mode: UnionKey.ThemeLayoutMode;
/** /** Disabled */
* disabled
*/
disabled?: boolean; disabled?: boolean;
} }
const props = defineProps<Props>();
interface Emits { interface Emits {
/** /** Layout mode change */
* layout mode change
*/
(e: 'update:mode', mode: UnionKey.ThemeLayoutMode): void; (e: 'update:mode', mode: UnionKey.ThemeLayoutMode): void;
} }
const emit = defineEmits<Emits>();
type LayoutConfig = Record< type LayoutConfig = Record<
UnionKey.ThemeLayoutMode, UnionKey.ThemeLayoutMode,
{ {

View File

@ -3,14 +3,12 @@ defineOptions({
name: 'SettingItem' name: 'SettingItem'
}); });
defineProps<Props>();
interface Props { interface Props {
/** /** Label */
* label
*/
label: string; label: string;
} }
defineProps<Props>();
</script> </script>
<template> <template>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import Clipboard from 'clipboard'; import Clipboard from 'clipboard';
import { $t } from '@/locales'; import { $t } from '@/locales';
import { useThemeStore } from '@/store/modules/theme'; import { useThemeStore } from '@/store/modules/theme';

View File

@ -2,7 +2,7 @@
import { computed } from 'vue'; import { computed } from 'vue';
import { $t } from '@/locales'; import { $t } from '@/locales';
import { useThemeStore } from '@/store/modules/theme'; import { useThemeStore } from '@/store/modules/theme';
import { themeScrollModeOptions, themePageAnimationModeOptions, themeTabModeOptions } from '@/constants/app'; import { themePageAnimationModeOptions, themeScrollModeOptions, themeTabModeOptions } from '@/constants/app';
import SettingItem from '../components/setting-item.vue'; import SettingItem from '../components/setting-item.vue';
defineOptions({ defineOptions({

View File

@ -4,7 +4,8 @@ import 'dayjs/locale/en';
import { localStg } from '@/utils/storage'; import { localStg } from '@/utils/storage';
/** /**
* set dayjs locale * Set dayjs locale
*
* @param lang * @param lang
*/ */
export function setDayjsLocale(lang: App.I18n.LangType = 'zh-CN') { export function setDayjsLocale(lang: App.I18n.LangType = 'zh-CN') {

View File

@ -11,7 +11,8 @@ const i18n = createI18n({
}); });
/** /**
* setup plugin i18n * Setup plugin i18n
*
* @param app * @param app
*/ */
export function setupI18n(app: App) { export function setupI18n(app: App) {

View File

@ -1,5 +1,5 @@
import { zhCN, enUS, dateZhCN, dateEnUS } from 'naive-ui'; import { dateEnUS, dateZhCN, enUS, zhCN } from 'naive-ui';
import type { NLocale, NDateLocale } from 'naive-ui'; import type { NDateLocale, NLocale } from 'naive-ui';
export const naiveLocales: Record<App.I18n.LangType, NLocale> = { export const naiveLocales: Record<App.I18n.LangType, NLocale> = {
'zh-CN': zhCN, 'zh-CN': zhCN,

View File

@ -1,6 +1,6 @@
import { createApp } from 'vue'; import { createApp } from 'vue';
import './plugins/assets'; import './plugins/assets';
import { setupNProgress, setupIconifyOffline, setupDayjs } from './plugins'; import { setupDayjs, setupIconifyOffline, setupNProgress } from './plugins';
import { setupStore } from './store'; import { setupStore } from './store';
import { setupRouter } from './router'; import { setupRouter } from './router';
import { setupI18n } from './locales'; import { setupI18n } from './locales';

View File

@ -1,8 +1,6 @@
import { addAPIProvider, disableCache } from '@iconify/vue'; import { addAPIProvider, disableCache } from '@iconify/vue';
/** /** Setup the iconify offline */
* setup the iconify offline
*/
export function setupIconifyOffline() { export function setupIconifyOffline() {
const { VITE_ICONIFY_URL } = import.meta.env; const { VITE_ICONIFY_URL } = import.meta.env;

View File

@ -1,8 +1,6 @@
import NProgress from 'nprogress'; import NProgress from 'nprogress';
/** /** Setup plugin NProgress */
* setup plugin NProgress
*/
export function setupNProgress() { export function setupNProgress() {
NProgress.configure({ easing: 'ease', speed: 500 }); NProgress.configure({ easing: 'ease', speed: 500 });

View File

@ -4,8 +4,9 @@ import { createDocumentTitleGuard } from './title';
import { createPermissionGuard } from './permission'; import { createPermissionGuard } from './permission';
/** /**
* router guard * Router guard
* @param router - router instance *
* @param router - Router instance
*/ */
export function createRouterGuard(router: Router) { export function createRouterGuard(router: Router) {
createProgressGuard(router); createProgressGuard(router);

View File

@ -1,4 +1,4 @@
import type { Router, NavigationGuardNext, RouteLocationNormalized } from 'vue-router'; import type { NavigationGuardNext, RouteLocationNormalized, Router } from 'vue-router';
import type { RouteKey, RoutePath } from '@elegant-router/types'; import type { RouteKey, RoutePath } from '@elegant-router/types';
import { useAuthStore } from '@/store/modules/auth'; import { useAuthStore } from '@/store/modules/auth';
import { useRouteStore } from '@/store/modules/route'; import { useRouteStore } from '@/store/modules/route';

View File

@ -1,6 +1,6 @@
import { $t } from '@/locales';
import { useTitle } from '@vueuse/core'; import { useTitle } from '@vueuse/core';
import type { Router } from 'vue-router'; import type { Router } from 'vue-router';
import { $t } from '@/locales';
export function createDocumentTitleGuard(router: Router) { export function createDocumentTitleGuard(router: Router) {
router.afterEach(to => { router.afterEach(to => {

View File

@ -1,10 +1,10 @@
import type { App } from 'vue'; import type { App } from 'vue';
import { import {
createRouter, type RouterHistory,
createWebHistory,
createWebHashHistory,
createMemoryHistory, createMemoryHistory,
type RouterHistory createRouter,
createWebHashHistory,
createWebHistory
} from 'vue-router'; } from 'vue-router';
import { createRoutes } from './routes'; import { createRoutes } from './routes';
import { createRouterGuard } from './guard'; import { createRouterGuard } from './guard';
@ -24,9 +24,7 @@ export const router = createRouter({
routes: constantVueRoutes routes: constantVueRoutes
}); });
/** /** Setup Vue Router */
* setup Vue Router
*/
export async function setupRouter(app: App) { export async function setupRouter(app: App) {
app.use(router); app.use(router);
createRouterGuard(router); createRouterGuard(router);

View File

@ -1,4 +1,4 @@
import type { ElegantConstRoute, ElegantRoute, CustomRoute } from '@elegant-router/types'; import type { CustomRoute, ElegantConstRoute, ElegantRoute } from '@elegant-router/types';
import { generatedRoutes } from '../elegant/routes'; import { generatedRoutes } from '../elegant/routes';
import { layouts, views } from '../elegant/imports'; import { layouts, views } from '../elegant/imports';
import { transformElegantRoutesToVueRoutes } from '../elegant/transform'; import { transformElegantRoutesToVueRoutes } from '../elegant/transform';
@ -26,9 +26,7 @@ const customRoutes: CustomRoute[] = [
} }
]; ];
/** /** Create routes */
* create routes
*/
export function createRoutes() { export function createRoutes() {
const constantRoutes: ElegantRoute[] = []; const constantRoutes: ElegantRoute[] = [];
@ -51,8 +49,9 @@ export function createRoutes() {
} }
/** /**
* get auth vue routes * Get auth vue routes
* @param routes elegant routes *
* @param routes Elegant routes
*/ */
export function getAuthVueRoutes(routes: ElegantConstRoute[]) { export function getAuthVueRoutes(routes: ElegantConstRoute[]) {
return transformElegantRoutesToVueRoutes(routes, layouts, views); return transformElegantRoutesToVueRoutes(routes, layouts, views);

View File

@ -1,9 +1,10 @@
import { request } from '../request'; import { request } from '../request';
/** /**
* login * Login
* @param userName user name *
* @param password password * @param userName User name
* @param password Password
*/ */
export function fetchLogin(userName: string, password: string) { export function fetchLogin(userName: string, password: string) {
return request<App.Service.Response<Api.Auth.LoginToken>>('/auth/login', { return request<App.Service.Response<Api.Auth.LoginToken>>('/auth/login', {
@ -15,16 +16,15 @@ export function fetchLogin(userName: string, password: string) {
}); });
} }
/** /** Get user info */
* get user info
*/
export function fetchGetUserInfo() { export function fetchGetUserInfo() {
return request<App.Service.Response<Api.Auth.UserInfo>>('/auth/getUserInfo'); return request<App.Service.Response<Api.Auth.UserInfo>>('/auth/getUserInfo');
} }
/** /**
* refresh token * Refresh token
* @param refreshToken refresh token *
* @param refreshToken Refresh token
*/ */
export function fetchRefreshToken(refreshToken: string) { export function fetchRefreshToken(refreshToken: string) {
return request<App.Service.Response<Api.Auth.LoginToken>>('/auth/refreshToken', { return request<App.Service.Response<Api.Auth.LoginToken>>('/auth/refreshToken', {

View File

@ -1,17 +1,19 @@
import { request } from '../request'; import { request } from '../request';
/** /**
* get user routes * Get user routes
* @param example whether to use example data, default: 0 *
* @param example Whether to use example data, default: 0
*/ */
export function fetchGetUserRoutes(example: '0' | '1' = '0') { export function fetchGetUserRoutes(example: '0' | '1' = '0') {
return request<App.Service.Response<Api.Route.UserRoute>>('/route/getUserRoutes', { params: { example } }); return request<App.Service.Response<Api.Route.UserRoute>>('/route/getUserRoutes', { params: { example } });
} }
/** /**
* whether the route is exist * Whether the route is exist
* @param routeName route name *
* @param example whether to use example data, default: 0 * @param routeName Route name
* @param example Whether to use example data, default: 0
*/ */
export function fetchIsRouteExist(routeName: string, example: '0' | '1' = '0') { export function fetchIsRouteExist(routeName: string, example: '0' | '1' = '0') {
return request<App.Service.Response<boolean>>('/route/isRouteExist', { params: { routeName, example } }); return request<App.Service.Response<boolean>>('/route/isRouteExist', { params: { routeName, example } });

View File

@ -1,6 +1,6 @@
import { localStg } from '@/utils/storage';
import { createOfetch as createRequest } from '@sa/request'; import { createOfetch as createRequest } from '@sa/request';
import { createServiceConfig, createProxyPattern } from '~/env.config'; import { localStg } from '@/utils/storage';
import { createProxyPattern, createServiceConfig } from '~/env.config';
const { baseURL, otherBaseURL } = createServiceConfig(import.meta.env); const { baseURL, otherBaseURL } = createServiceConfig(import.meta.env);

View File

@ -2,9 +2,7 @@ import type { App } from 'vue';
import { createPinia } from 'pinia'; import { createPinia } from 'pinia';
import { resetSetupStore } from './plugins'; import { resetSetupStore } from './plugins';
/** /** Setup Vue store plugin pinia */
* setup Vue store plugin pinia
*/
export function setupStore(app: App) { export function setupStore(app: App) {
const store = createPinia(); const store = createPinia();

View File

@ -1,4 +1,4 @@
import { ref, watch, effectScope, onScopeDispose } from 'vue'; import { effectScope, onScopeDispose, ref, watch } from 'vue';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { breakpointsTailwind, useBreakpoints, useTitle } from '@vueuse/core'; import { breakpointsTailwind, useBreakpoints, useTitle } from '@vueuse/core';
import { useBoolean } from '@sa/hooks'; import { useBoolean } from '@sa/hooks';
@ -24,14 +24,13 @@ export const useAppStore = defineStore(SetupStoreId.App, () => {
const { bool: siderCollapse, setBool: setSiderCollapse, toggle: toggleSiderCollapse } = useBoolean(); const { bool: siderCollapse, setBool: setSiderCollapse, toggle: toggleSiderCollapse } = useBoolean();
const { bool: mixSiderFixed, setBool: setMixSiderFixed, toggle: toggleMixSiderFixed } = useBoolean(); const { bool: mixSiderFixed, setBool: setMixSiderFixed, toggle: toggleMixSiderFixed } = useBoolean();
/** /** Is mobile layout */
* is mobile layout
*/
const isMobile = breakpoints.smaller('sm'); const isMobile = breakpoints.smaller('sm');
/** /**
* reload page * Reload page
* @param duration duration time *
* @param duration Duration time
*/ */
async function reloadPage(duration = 0) { async function reloadPage(duration = 0) {
setReloadFlag(false); setReloadFlag(false);
@ -64,9 +63,7 @@ export const useAppStore = defineStore(SetupStoreId.App, () => {
localStg.set('lang', lang); localStg.set('lang', lang);
} }
/** /** Update document title by locale */
* update document title by locale
*/
function updateDocumentTitleByLocale() { function updateDocumentTitleByLocale() {
const { i18nKey, title } = router.currentRoute.value.meta; const { i18nKey, title } = router.currentRoute.value.meta;
@ -110,9 +107,7 @@ export const useAppStore = defineStore(SetupStoreId.App, () => {
}); });
}); });
/** /** On scope dispose */
* on scope dispose
*/
onScopeDispose(() => { onScopeDispose(() => {
scope.stop(); scope.stop();
}); });

View File

@ -1,13 +1,13 @@
import { ref, reactive, computed } from 'vue'; import { computed, reactive, ref } from 'vue';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { useLoading } from '@sa/hooks'; import { useLoading } from '@sa/hooks';
import { SetupStoreId } from '@/enum'; import { SetupStoreId } from '@/enum';
import { useRouterPush } from '@/hooks/common/router'; import { useRouterPush } from '@/hooks/common/router';
import { fetchLogin, fetchGetUserInfo } from '@/service/api'; import { fetchGetUserInfo, fetchLogin } from '@/service/api';
import { localStg } from '@/utils/storage'; import { localStg } from '@/utils/storage';
import { useRouteStore } from '../route';
import { getToken, getUserInfo, clearAuthStorage } from './shared';
import { $t } from '@/locales'; import { $t } from '@/locales';
import { useRouteStore } from '../route';
import { clearAuthStorage, getToken, getUserInfo } from './shared';
export const useAuthStore = defineStore(SetupStoreId.Auth, () => { export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
const routeStore = useRouteStore(); const routeStore = useRouteStore();
@ -18,14 +18,10 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
const userInfo: Api.Auth.UserInfo = reactive(getUserInfo()); const userInfo: Api.Auth.UserInfo = reactive(getUserInfo());
/** /** Is login */
* is login
*/
const isLogin = computed(() => Boolean(token.value)); const isLogin = computed(() => Boolean(token.value));
/** /** Reset auth store */
* reset auth store
*/
async function resetStore() { async function resetStore() {
const authStore = useAuthStore(); const authStore = useAuthStore();
@ -41,9 +37,10 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
} }
/** /**
* login * Login
* @param userName user name *
* @param password password * @param userName User name
* @param password Password
*/ */
async function login(userName: string, password: string) { async function login(userName: string, password: string) {
startLoading(); startLoading();

Some files were not shown because too many files have changed in this diff Show More