diff --git a/.stylelintrc.cjs b/.stylelintrc.cjs
index bc1219c9..e6b1be15 100644
--- a/.stylelintrc.cjs
+++ b/.stylelintrc.cjs
@@ -44,7 +44,7 @@ module.exports = {
"at-rule-no-unknown": [
true,
{
- ignoreAtRules: ["apply", "use"],
+ ignoreAtRules: ["apply", "use", "forward"],
},
],
},
diff --git a/README.md b/README.md
index 57c992aa..502747d2 100644
--- a/README.md
+++ b/README.md
@@ -3,9 +3,9 @@
vue3-element-admin
-
-
-
+
+
+
diff --git a/package.json b/package.json
index d209bf11..4f82fd25 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "vue3-element-admin",
- "version": "2.14.0",
+ "version": "2.15.0",
"private": true,
"type": "module",
"scripts": {
@@ -52,8 +52,7 @@
"animate.css": "^4.1.1",
"axios": "^1.7.7",
"codemirror": "^5.65.17",
- "codemirror-editor-vue3": "^2.7.0",
- "color": "^4.2.3",
+ "codemirror-editor-vue3": "^2.7.1",
"echarts": "^5.5.1",
"element-plus": "^2.8.1",
"exceljs": "^4.4.0",
@@ -73,7 +72,6 @@
"@commitlint/config-conventional": "^18.6.3",
"@iconify-json/ep": "^1.2.0",
"@types/codemirror": "^5.60.15",
- "@types/color": "^3.0.6",
"@types/lodash": "^4.17.7",
"@types/node": "^20.16.5",
"@types/nprogress": "^0.2.3",
@@ -119,8 +117,7 @@
"vue-tsc": "^2.1.6"
},
"engines": {
- "node": "^18.0.0 || ^20.7.0",
- "pnpm": ">=9"
+ "node": ">=18.0.0"
},
"repository": "https://gitee.com/youlaiorg/vue3-element-admin.git",
"author": "有来开源组织",
diff --git a/src/api/generator.ts b/src/api/codegen.ts
similarity index 98%
rename from src/api/generator.ts
rename to src/api/codegen.ts
index a6888c7c..166aa222 100644
--- a/src/api/generator.ts
+++ b/src/api/codegen.ts
@@ -1,6 +1,6 @@
import request from "@/utils/request";
-const GENERATOR_BASE_URL = "/api/v1/generator";
+const GENERATOR_BASE_URL = "/api/v1/codegen";
class GeneratorAPI {
/** 获取数据表分页列表 */
diff --git a/src/assets/icons/gitee.svg b/src/assets/icons/gitee.svg
new file mode 100644
index 00000000..c799c2f3
--- /dev/null
+++ b/src/assets/icons/gitee.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/icons/qq.svg b/src/assets/icons/qq.svg
new file mode 100644
index 00000000..a59086b4
--- /dev/null
+++ b/src/assets/icons/qq.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/icons/wechat.svg b/src/assets/icons/wechat.svg
new file mode 100644
index 00000000..2fc58038
--- /dev/null
+++ b/src/assets/icons/wechat.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/images/login-image.svg b/src/assets/images/login-image.svg
new file mode 100644
index 00000000..f42e56b6
--- /dev/null
+++ b/src/assets/images/login-image.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/components/IconSelect/index.vue b/src/components/IconSelect/index.vue
index 7e383086..e74aa44c 100644
--- a/src/components/IconSelect/index.vue
+++ b/src/components/IconSelect/index.vue
@@ -201,7 +201,7 @@ onClickOutside(iconSelectRef, () => (popoverVisible.value = false), {
}
.icon-item:hover {
- border-color: #409eff;
+ border-color: #4080ff;
scale: 1.2;
}
}
diff --git a/src/lang/package/en.ts b/src/lang/package/en.ts
index 7eac43b3..77e9e4f9 100644
--- a/src/lang/package/en.ts
+++ b/src/lang/package/en.ts
@@ -11,6 +11,8 @@ export default {
login: "Login",
captchaCode: "Verify Code",
capsLock: "Caps Lock is On",
+ rememberMe: "Remember Me",
+ forgetPassword: "Forget Password",
message: {
username: {
required: "Please enter Username",
@@ -23,6 +25,7 @@ export default {
required: "Please enter Verify Code",
},
},
+ otherLoginMethods: "Other login methods",
},
// 导航栏国际化
navbar: {
diff --git a/src/lang/package/zh-cn.ts b/src/lang/package/zh-cn.ts
index 36764dc9..e8093cf1 100644
--- a/src/lang/package/zh-cn.ts
+++ b/src/lang/package/zh-cn.ts
@@ -11,6 +11,8 @@ export default {
login: "登 录",
captchaCode: "验证码",
capsLock: "大写锁定已打开",
+ rememberMe: "记住我",
+ forgetPassword: "忘记密码",
message: {
username: {
required: "请输入用户名",
@@ -23,6 +25,7 @@ export default {
required: "请输入验证码",
},
},
+ otherLoginMethods: "其他登录方式",
},
// 导航栏国际化
navbar: {
diff --git a/src/layout/components/Settings/components/ThemeColorPicker.vue b/src/layout/components/Settings/components/ThemeColorPicker.vue
index 5a0d59cd..587c1ac5 100644
--- a/src/layout/components/Settings/components/ThemeColorPicker.vue
+++ b/src/layout/components/Settings/components/ThemeColorPicker.vue
@@ -15,7 +15,7 @@ const emit = defineEmits(["update:modelValue"]);
// 定义颜色预设
const colorPresets = [
- "#409EFF",
+ "#4080FF",
"#ff4500",
"#ff8c00",
"#90ee90",
diff --git a/src/main.ts b/src/main.ts
index ff813587..4ea367dc 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -10,10 +10,10 @@ import "element-plus/theme-chalk/dark/css-vars.css";
import "@/styles/index.scss";
import "uno.css";
import "animate.css";
-import { InstallCodemirro } from "codemirror-editor-vue3";
+import { InstallCodeMirror } from "codemirror-editor-vue3";
const app = createApp(App);
// 注册插件
app.use(setupPlugins);
-app.use(InstallCodemirro);
+app.use(InstallCodeMirror);
app.mount("#app");
diff --git a/src/settings.ts b/src/settings.ts
index e25fef2c..c4393497 100644
--- a/src/settings.ts
+++ b/src/settings.ts
@@ -18,7 +18,7 @@ const defaultSettings: AppSettings = {
theme: mediaQueryList.matches ? ThemeEnum.DARK : ThemeEnum.LIGHT,
size: SizeEnum.DEFAULT,
language: LanguageEnum.ZH_CN,
- themeColor: "#409EFF",
+ themeColor: "#4080FF",
watermarkEnabled: false,
watermarkContent: pkg.name,
};
diff --git a/src/store/modules/settings.ts b/src/store/modules/settings.ts
index a581e90f..9a2eee6a 100644
--- a/src/store/modules/settings.ts
+++ b/src/store/modules/settings.ts
@@ -1,68 +1,50 @@
import defaultSettings from "@/settings";
import { ThemeEnum } from "@/enums/ThemeEnum";
-import Color from "color";
+import { generateThemeColors, applyTheme, toggleDarkMode } from "@/utils/theme";
type SettingsValue = boolean | string;
export const useSettingsStore = defineStore("setting", () => {
- // 是否显示设置
+ // 基本设置
const settingsVisible = ref(false);
- // 是否显示标签视图
+ // 标签
const tagsView = useStorage("tagsView", defaultSettings.tagsView);
- // 是否显示侧边栏logo
+ // 侧边栏 Logo
const sidebarLogo = useStorage(
"sidebarLogo",
defaultSettings.sidebarLogo
);
- // 是否固定头部
+ // 固定头部
const fixedHeader = useStorage(
"fixedHeader",
defaultSettings.fixedHeader
);
- // 布局模式:left-左侧模式(默认) top-顶部模式 mix-混合模式
+ // 布局
const layout = useStorage("layout", defaultSettings.layout);
- // 主题颜色
- const themeColor = useStorage(
- "themeColor",
- defaultSettings.themeColor
- );
- // 主题:light-亮色(默认) dark-暗色
- const theme = useStorage("theme", defaultSettings.theme);
- // 是否开启水印
+ // 水印
const watermarkEnabled = useStorage(
"watermarkEnabled",
defaultSettings.watermarkEnabled
);
+ // 主题
+ const themeColor = useStorage(
+ "themeColor",
+ defaultSettings.themeColor
+ );
+ const theme = useStorage("theme", defaultSettings.theme);
+
+ // 监听主题变化
watch(
[theme, themeColor],
- ([newTheme, newThemeColor], [oldTheme, oldThemeColor]) => {
- if (newTheme !== oldTheme) {
- if (newTheme === ThemeEnum.DARK) {
- document.documentElement.classList.add("dark");
- } else {
- document.documentElement.classList.remove("dark");
- }
- }
-
- if (newThemeColor !== oldThemeColor) {
- const rootStyle = document.documentElement.style;
- rootStyle.setProperty(`--el-color-primary`, newThemeColor);
- rootStyle.setProperty(`--el-color-primary-dark-2`, newThemeColor);
-
- for (let i = 1; i < 10; i++) {
- rootStyle.setProperty(
- `--el-color-primary-light-${i}`,
- `${Color(newThemeColor).alpha(1 - i * 0.1)}`
- );
- }
- }
+ ([newTheme, newThemeColor]) => {
+ toggleDarkMode(newTheme === ThemeEnum.DARK);
+ const colors = generateThemeColors(newThemeColor);
+ applyTheme(colors);
},
- {
- immediate: true, // 立即执行,确保在侦听器创建时执行一次
- }
+ { immediate: true }
);
-
+ // 设置更改函数
const settingsMap: Record> = {
fixedHeader,
tagsView,
@@ -79,31 +61,17 @@ export const useSettingsStore = defineStore("setting", () => {
value: SettingsValue;
}) {
const setting = settingsMap[key];
- if (setting) {
- setting.value = value;
- }
+ if (setting) setting.value = value;
}
- /**
- * 切换主题
- */
function changeTheme(val: string) {
theme.value = val;
}
- /**
- * 切换主题颜色
- *
- * @param color 主题颜色
- *
- */
function changeThemeColor(color: string) {
themeColor.value = color;
}
- /**
- * 切换布局
- */
function changeLayout(val: string) {
layout.value = val;
}
diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts
index 4eea0408..40bf52f2 100644
--- a/src/store/modules/user.ts
+++ b/src/store/modules/user.ts
@@ -69,7 +69,6 @@ export const useUserStore = defineStore("user", () => {
// remove token
function resetToken() {
- console.log("resetToken");
return new Promise((resolve) => {
localStorage.setItem(TOKEN_KEY, "");
resetRouter();
diff --git a/src/styles/login.scss b/src/styles/login.scss
index ede34896..3f4f382c 100644
--- a/src/styles/login.scss
+++ b/src/styles/login.scss
@@ -8,25 +8,47 @@
background: url("@/assets/images/login-background-light.jpg") no-repeat center
right;
- .top-bar {
- position: absolute;
- top: 0;
- left: 0;
+ .login-content {
display: flex;
- align-items: center;
- justify-content: flex-end;
width: 100%;
- padding: 10px;
- }
+ min-width: 400px;
+ max-width: 850px;
+ overflow: hidden;
+ background-color: #fff;
+ border-radius: 5px;
+ box-shadow: var(--el-box-shadow-light);
- .login-card {
- width: 400px;
- background: transparent;
- border: none;
- border-radius: 4%;
+ @media (width <= 768px) {
+ flex-direction: column;
+ max-width: 100%;
+ height: 100vh;
+ border-radius: 0;
+ box-shadow: none;
+ }
- @media (width <= 640px) {
- width: 340px;
+ .login-image {
+ display: flex;
+ flex: 3;
+ align-items: center;
+ justify-content: center;
+ background: linear-gradient(60deg, #165dff, #6aa1ff);
+
+ @media (width <= 768px) {
+ display: none;
+ }
+ }
+
+ .login-box {
+ display: flex;
+ flex: 2;
+ flex-direction: column;
+ justify-content: center;
+ min-width: 400px;
+ padding: 30px;
+
+ @media (width <= 768px) {
+ width: 100%;
+ }
}
.input-wrapper {
@@ -43,13 +65,6 @@
}
}
- .icp-info {
- position: absolute;
- bottom: 4px;
- font-size: 12px;
- text-align: center;
- }
-
.el-form-item {
background: var(--el-input-bg-color);
border: 1px solid var(--el-border-color);
@@ -75,7 +90,14 @@
}
}
-html.dark .login-container {
- background: url("@/assets/images/login-background-dark.jpg") no-repeat center
- right;
+html.dark {
+ .login-container {
+ background: url("@/assets/images/login-background-dark.jpg") no-repeat
+ center right;
+
+ .login-content {
+ background: transparent;
+ box-shadow: var(--el-box-shadow);
+ }
+ }
}
diff --git a/src/styles/variables.scss b/src/styles/variables.scss
index 42f97182..7948a7c6 100644
--- a/src/styles/variables.scss
+++ b/src/styles/variables.scss
@@ -1,3 +1,27 @@
+@forward "element-plus/theme-chalk/src/common/var.scss" with (
+ $colors: (
+ "primary": (
+ "base": #4080ff,
+ ),
+ "success": (
+ "base": #23c343,
+ ),
+ "warning": (
+ "base": #ff9a2e,
+ ),
+ "danger": (
+ "base": #f76560,
+ ),
+ "info": (
+ "base": #a9aeb8,
+ ),
+ ),
+
+ $bg-color: (
+ "page": #f5f8fd,
+ )
+);
+
/** 全局SCSS变量 */
:root {
diff --git a/src/utils/theme.ts b/src/utils/theme.ts
new file mode 100644
index 00000000..a62d01d1
--- /dev/null
+++ b/src/utils/theme.ts
@@ -0,0 +1,52 @@
+// 辅助函数:将十六进制颜色转换为 RGB
+function hexToRgb(hex: string): [number, number, number] {
+ const bigint = parseInt(hex.slice(1), 16);
+ return [(bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255];
+}
+
+// 辅助函数:将 RGB 转换为十六进制颜色
+function rgbToHex(r: number, g: number, b: number): string {
+ return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
+}
+
+// 辅助函数:调整颜色亮度
+function adjustBrightness(hex: string, factor: number): string {
+ const rgb = hexToRgb(hex);
+ const newRgb = rgb.map((val) =>
+ Math.max(0, Math.min(255, Math.round(val + (255 - val) * factor)))
+ ) as [number, number, number];
+ return rgbToHex(...newRgb);
+}
+
+export function generateThemeColors(primary: string) {
+ const colors: Record = {
+ primary,
+ };
+
+ // 生成浅色变体
+ for (let i = 1; i <= 9; i++) {
+ const factor = i * 0.1;
+ colors[`primary-light-${i}`] = adjustBrightness(primary, factor);
+ }
+
+ // 生成深色变体
+ colors["primary-dark-2"] = adjustBrightness(primary, -0.2);
+
+ return colors;
+}
+
+export function applyTheme(colors: Record) {
+ const el = document.documentElement;
+
+ Object.entries(colors).forEach(([key, value]) => {
+ el.style.setProperty(`--el-color-${key}`, value);
+ });
+}
+
+export function toggleDarkMode(isDark: boolean) {
+ if (isDark) {
+ document.documentElement.classList.add("dark");
+ } else {
+ document.documentElement.classList.remove("dark");
+ }
+}
diff --git a/src/views/generator/index.vue b/src/views/codegen/index.vue
similarity index 99%
rename from src/views/generator/index.vue
rename to src/views/codegen/index.vue
index b1a6b1d2..98f48bd1 100644
--- a/src/views/generator/index.vue
+++ b/src/views/codegen/index.vue
@@ -430,7 +430,7 @@