feat: 完善 Prompt 管理与生成流程配置
This commit is contained in:
13
RuoYi-Vue/tmp-logs/logback-local.xml
Normal file
13
RuoYi-Vue/tmp-logs/logback-local.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
</configuration>
|
||||
1
RuoYi-Vue/tmp-logs/metric-empty-current-backend.pid
Normal file
1
RuoYi-Vue/tmp-logs/metric-empty-current-backend.pid
Normal file
@@ -0,0 +1 @@
|
||||
20464
|
||||
1
RuoYi-Vue/tmp-logs/metric-empty-fixed-backend.pid
Normal file
1
RuoYi-Vue/tmp-logs/metric-empty-fixed-backend.pid
Normal file
@@ -0,0 +1 @@
|
||||
33372
|
||||
5
RuoYi-Vue/tmp-logs/qing-ui-preview/babel.config.js
Normal file
5
RuoYi-Vue/tmp-logs/qing-ui-preview/babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
"@vue/cli-plugin-babel/preset"
|
||||
]
|
||||
}
|
||||
24
RuoYi-Vue/tmp-logs/qing-ui-preview/package.json
Normal file
24
RuoYi-Vue/tmp-logs/qing-ui-preview/package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "product-web",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vue-cli-service serve --host 0.0.0.0",
|
||||
"serve": "vue-cli-service serve --host 0.0.0.0",
|
||||
"build": "vue-cli-service build"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.27.2",
|
||||
"core-js": "^3.8.3",
|
||||
"echarts": "5.4.0",
|
||||
"element-ui": "^2.15.14",
|
||||
"quill": "^1.3.7",
|
||||
"vue": "^2.6.14",
|
||||
"vue-quill-editor": "^3.0.6",
|
||||
"vue-router": "^3.6.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-service": "^4.5.19",
|
||||
"vue-template-compiler": "^2.6.14"
|
||||
}
|
||||
}
|
||||
13
RuoYi-Vue/tmp-logs/qing-ui-preview/public/index.html
Normal file
13
RuoYi-Vue/tmp-logs/qing-ui-preview/public/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>演示商品</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>请启用 JavaScript 后访问。</noscript>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
543
RuoYi-Vue/tmp-logs/qing-ui-preview/src/App.vue
Normal file
543
RuoYi-Vue/tmp-logs/qing-ui-preview/src/App.vue
Normal file
@@ -0,0 +1,543 @@
|
||||
<template>
|
||||
<div class="portal-shell">
|
||||
<template v-if="!isLoginRoute">
|
||||
<header class="top-nav">
|
||||
<router-link class="brand" to="/demo/product">
|
||||
<span class="brand-logo" aria-hidden="true"><i></i></span>
|
||||
<span>演示商品</span>
|
||||
</router-link>
|
||||
|
||||
<nav class="portal-nav" aria-label="主导航">
|
||||
<div class="portal-nav-group">
|
||||
<router-link class="portal-nav-parent" to="/demo/product" exact>首页</router-link>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="user-area">
|
||||
<template v-if="currentUser">
|
||||
<button class="user-chip" type="button">
|
||||
<span class="avatar">{{ userInitial }}</span>
|
||||
<span>{{ currentUserName }}</span>
|
||||
</button>
|
||||
<button class="logout-link" type="button" @click="logout">退出</button>
|
||||
</template>
|
||||
<button v-else class="login-link" type="button" @click="goLogin">登录</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section v-if="isHomeRoute" class="hero-banner" aria-label="项目横幅">
|
||||
<div class="hero-pattern" aria-hidden="true"></div>
|
||||
<div class="hero-content">
|
||||
<span class="hero-eyebrow">WELCOME</span>
|
||||
<h1>演示商品</h1>
|
||||
<p>发现内容,连接服务,轻松完成每一次访问。</p>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<main :class="['portal-main', { 'portal-main-login': isLoginRoute }]">
|
||||
<router-view />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "App",
|
||||
data() {
|
||||
return {
|
||||
currentUser: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isLoginRoute() {
|
||||
return this.$route.path === "/login"
|
||||
},
|
||||
isHomeRoute() {
|
||||
return this.$route.path === "/demo/product"
|
||||
},
|
||||
currentUserName() {
|
||||
return this.currentUser && (this.currentUser.displayName || this.currentUser.account || this.currentUser.nickname || this.currentUser.username || "User")
|
||||
},
|
||||
userInitial() {
|
||||
const name = this.currentUserName || "U"
|
||||
return name.slice(0, 1).toUpperCase()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadUser()
|
||||
window.addEventListener("portal-user-change", this.loadUser)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener("portal-user-change", this.loadUser)
|
||||
},
|
||||
methods: {
|
||||
canShowMenu(visibleRoles) {
|
||||
if (!Array.isArray(visibleRoles) || visibleRoles.length === 0) {
|
||||
return true
|
||||
}
|
||||
const roleCode = this.currentUser && this.currentUser.roleCode
|
||||
return Boolean(roleCode) && visibleRoles.indexOf(roleCode) !== -1
|
||||
},
|
||||
isNavGroupActive(paths) {
|
||||
return Array.isArray(paths) && paths.indexOf(this.$route.path) !== -1
|
||||
},
|
||||
loadUser() {
|
||||
const raw = localStorage.getItem("portal-user")
|
||||
if (!raw) {
|
||||
this.currentUser = null
|
||||
return
|
||||
}
|
||||
try {
|
||||
this.currentUser = JSON.parse(raw)
|
||||
} catch (error) {
|
||||
localStorage.removeItem("portal-user")
|
||||
this.currentUser = null
|
||||
}
|
||||
},
|
||||
goLogin() {
|
||||
this.$router.push({
|
||||
path: "/login",
|
||||
query: { redirect: this.$route.fullPath }
|
||||
})
|
||||
},
|
||||
logout() {
|
||||
this.request.post("/auth/logout").catch(() => {})
|
||||
localStorage.removeItem("portal-token")
|
||||
localStorage.removeItem("portal-user")
|
||||
window.dispatchEvent(new Event("portal-user-change"))
|
||||
this.$router.push("/login")
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--portal-primary: #167d83;
|
||||
--portal-primary-strong: #11656a;
|
||||
--portal-primary-soft: #eaf7f7;
|
||||
--portal-accent: #2f72d6;
|
||||
--portal-text: #172033;
|
||||
--portal-text-secondary: #5f6b7a;
|
||||
--portal-border: #e3e9f1;
|
||||
--portal-surface: #ffffff;
|
||||
--portal-background: #f5f7fb;
|
||||
--portal-radius-sm: 8px;
|
||||
--portal-radius-md: 12px;
|
||||
--portal-radius-lg: 18px;
|
||||
--portal-shadow-sm: 0 8px 24px rgba(15, 23, 42, 0.06);
|
||||
--portal-shadow-md: 0 18px 44px rgba(15, 23, 42, 0.12);
|
||||
--portal-content-width: 1200px;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--portal-background);
|
||||
color: var(--portal-text);
|
||||
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
button {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
button:focus-visible,
|
||||
a:focus-visible {
|
||||
outline: 3px solid rgba(47, 114, 214, 0.28);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.portal-shell {
|
||||
min-height: 100vh;
|
||||
background: var(--portal-background);
|
||||
}
|
||||
|
||||
.top-nav {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 20;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(180px, auto) minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
min-height: 72px;
|
||||
padding: 0 32px;
|
||||
color: var(--portal-text);
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
border-bottom: 1px solid var(--portal-border);
|
||||
box-shadow: 0 4px 16px rgba(15, 23, 42, 0.04);
|
||||
backdrop-filter: blur(14px);
|
||||
}
|
||||
|
||||
.brand,
|
||||
.portal-nav,
|
||||
.user-area,
|
||||
.user-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.brand {
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.brand span:last-child {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.brand-logo {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
flex: 0 0 38px;
|
||||
place-items: center;
|
||||
overflow: hidden;
|
||||
border-radius: 11px;
|
||||
color: #ffffff;
|
||||
background: linear-gradient(135deg, var(--portal-primary), var(--portal-accent));
|
||||
box-shadow: 0 7px 18px rgba(22, 125, 131, 0.24);
|
||||
}
|
||||
|
||||
.brand-logo::before,
|
||||
.brand-logo::after,
|
||||
.brand-logo i {
|
||||
position: absolute;
|
||||
display: block;
|
||||
content: "";
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
transform: rotate(-35deg);
|
||||
}
|
||||
|
||||
.brand-logo::before {
|
||||
width: 20px;
|
||||
height: 7px;
|
||||
top: 9px;
|
||||
left: 7px;
|
||||
}
|
||||
|
||||
.brand-logo i {
|
||||
width: 15px;
|
||||
height: 6px;
|
||||
top: 17px;
|
||||
left: 11px;
|
||||
}
|
||||
|
||||
.brand-logo::after {
|
||||
width: 10px;
|
||||
height: 5px;
|
||||
top: 25px;
|
||||
left: 15px;
|
||||
}
|
||||
|
||||
.portal-nav {
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.portal-nav-group {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
min-height: 72px;
|
||||
}
|
||||
|
||||
.portal-nav-parent,
|
||||
.portal-nav-toggle,
|
||||
.portal-nav-child {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.portal-nav-parent {
|
||||
min-height: 36px;
|
||||
padding: 0 14px;
|
||||
border-radius: var(--portal-radius-sm);
|
||||
color: var(--portal-text);
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.portal-nav-parent:hover,
|
||||
.portal-nav-parent.router-link-active,
|
||||
.portal-nav-group.active .portal-nav-parent {
|
||||
color: var(--portal-primary-strong);
|
||||
background: var(--portal-primary-soft);
|
||||
}
|
||||
|
||||
.portal-nav-directory {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.portal-nav-toggle {
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin-left: -8px;
|
||||
border-radius: 6px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.portal-nav-group:hover .portal-nav-toggle,
|
||||
.portal-nav-group:focus-within .portal-nav-toggle,
|
||||
.portal-nav-group.active .portal-nav-toggle {
|
||||
color: var(--portal-primary-strong);
|
||||
background: var(--portal-primary-soft);
|
||||
}
|
||||
|
||||
.portal-nav-dropdown {
|
||||
position: absolute;
|
||||
top: calc(100% - 8px);
|
||||
left: 0;
|
||||
z-index: 30;
|
||||
display: none;
|
||||
min-width: 168px;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--portal-border);
|
||||
border-radius: var(--portal-radius-md);
|
||||
background: var(--portal-surface);
|
||||
box-shadow: var(--portal-shadow-md);
|
||||
}
|
||||
|
||||
.portal-nav-group:hover .portal-nav-dropdown,
|
||||
.portal-nav-group:focus-within .portal-nav-dropdown {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.portal-nav-child {
|
||||
min-height: 34px;
|
||||
padding: 0 10px;
|
||||
border-radius: 6px;
|
||||
color: #334155;
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.portal-nav-child:hover,
|
||||
.portal-nav-child.router-link-active {
|
||||
color: var(--portal-primary-strong);
|
||||
background: var(--portal-primary-soft);
|
||||
}
|
||||
|
||||
.user-area {
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
min-width: 130px;
|
||||
}
|
||||
|
||||
.user-chip,
|
||||
.login-link,
|
||||
.logout-link {
|
||||
border: 1px solid var(--portal-border);
|
||||
color: var(--portal-text-secondary);
|
||||
background: var(--portal-surface);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.user-chip {
|
||||
gap: 8px;
|
||||
min-height: 38px;
|
||||
padding: 0 10px 0 0;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
display: inline-grid;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
place-items: center;
|
||||
border: 1px solid #bfe4e5;
|
||||
border-radius: 50%;
|
||||
color: var(--portal-primary-strong);
|
||||
background: var(--portal-primary-soft);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.logout-link,
|
||||
.login-link {
|
||||
min-height: 32px;
|
||||
padding: 0 10px;
|
||||
border-radius: var(--portal-radius-sm);
|
||||
}
|
||||
|
||||
.logout-link:hover,
|
||||
.login-link:hover {
|
||||
color: var(--portal-primary-strong);
|
||||
background: var(--portal-primary-soft);
|
||||
}
|
||||
|
||||
.hero-banner {
|
||||
position: relative;
|
||||
display: grid;
|
||||
min-height: 228px;
|
||||
place-items: center;
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(132deg, rgba(20, 124, 131, 0.97), rgba(47, 114, 214, 0.91)),
|
||||
var(--portal-primary);
|
||||
}
|
||||
|
||||
.hero-pattern {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0.58;
|
||||
background:
|
||||
linear-gradient(42deg, transparent 0 18%, rgba(255, 255, 255, 0.18) 18% 27%, transparent 27% 100%),
|
||||
linear-gradient(132deg, transparent 0 48%, rgba(246, 186, 51, 0.32) 48% 55%, transparent 55% 100%);
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: min(var(--portal-content-width), calc(100% - 48px));
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.hero-eyebrow {
|
||||
display: inline-block;
|
||||
margin-bottom: 12px;
|
||||
color: rgba(255, 255, 255, 0.78);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.18em;
|
||||
}
|
||||
|
||||
.hero-banner h1 {
|
||||
max-width: 780px;
|
||||
margin: 0;
|
||||
color: #ffffff;
|
||||
font-size: clamp(34px, 4vw, 48px);
|
||||
font-weight: 700;
|
||||
letter-spacing: 0;
|
||||
line-height: 1.2;
|
||||
text-shadow: 0 12px 28px rgba(15, 23, 42, 0.22);
|
||||
}
|
||||
|
||||
.hero-banner p {
|
||||
max-width: 620px;
|
||||
margin: 14px 0 0;
|
||||
color: rgba(255, 255, 255, 0.86);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.portal-main {
|
||||
width: min(var(--portal-content-width), calc(100% - 48px));
|
||||
margin: 0 auto;
|
||||
padding: 32px 0 52px;
|
||||
}
|
||||
|
||||
.portal-main-login {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.top-nav {
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 10px;
|
||||
height: auto;
|
||||
min-height: 56px;
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
.portal-nav {
|
||||
grid-column: 1 / -1;
|
||||
justify-content: flex-start;
|
||||
order: 3;
|
||||
gap: 6px;
|
||||
overflow-x: auto;
|
||||
overflow-y: visible;
|
||||
}
|
||||
|
||||
.portal-nav-group {
|
||||
min-height: 38px;
|
||||
}
|
||||
|
||||
.portal-nav-parent {
|
||||
min-height: 34px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.portal-nav-dropdown {
|
||||
position: fixed;
|
||||
top: 104px;
|
||||
left: 16px;
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
.hero-banner {
|
||||
min-height: 176px;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
width: min(100% - 32px, var(--portal-content-width));
|
||||
}
|
||||
|
||||
.hero-banner p {
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.portal-main {
|
||||
width: min(calc(100% - 28px), var(--portal-content-width));
|
||||
padding: 22px 0 36px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.brand {
|
||||
max-width: calc(100vw - 150px);
|
||||
}
|
||||
|
||||
.brand span:last-child {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.user-area {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.user-chip > span:last-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero-banner {
|
||||
min-height: 160px;
|
||||
}
|
||||
|
||||
.hero-eyebrow {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
21
RuoYi-Vue/tmp-logs/qing-ui-preview/src/main.js
Normal file
21
RuoYi-Vue/tmp-logs/qing-ui-preview/src/main.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import Vue from "vue"
|
||||
import ElementUI from "element-ui"
|
||||
import VueQuillEditor from "vue-quill-editor"
|
||||
import "element-ui/lib/theme-chalk/index.css"
|
||||
import "quill/dist/quill.core.css"
|
||||
import "quill/dist/quill.snow.css"
|
||||
import App from "./App.vue"
|
||||
import router from "./router"
|
||||
import request from "./utils/request"
|
||||
import dict from "./utils/dict"
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.use(ElementUI)
|
||||
Vue.use(VueQuillEditor)
|
||||
Vue.prototype.request = request
|
||||
Vue.prototype._dict = dict
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
render: h => h(App)
|
||||
}).$mount("#app")
|
||||
50
RuoYi-Vue/tmp-logs/qing-ui-preview/src/router/index.js
Normal file
50
RuoYi-Vue/tmp-logs/qing-ui-preview/src/router/index.js
Normal file
@@ -0,0 +1,50 @@
|
||||
import Vue from "vue"
|
||||
import Router from "vue-router"
|
||||
import Login from "@/views/login/index.vue"
|
||||
import Product from "@/views/demo/product/index.vue"
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
const router = new Router({
|
||||
mode: "hash",
|
||||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
redirect: "/demo/product"
|
||||
},
|
||||
{
|
||||
path: "/login",
|
||||
name: "Login",
|
||||
component: Login
|
||||
},
|
||||
{
|
||||
path: "/demo/product",
|
||||
name: "Product",
|
||||
component: Product,
|
||||
meta: { requiresAuth: false }
|
||||
},
|
||||
{
|
||||
path: "/secondary",
|
||||
name: "Secondary",
|
||||
component: Product,
|
||||
meta: { requiresAuth: false }
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const requiresAuth = to.matched.some(record => record.meta && record.meta.requiresAuth)
|
||||
const hasToken = Boolean(localStorage.getItem("portal-token"))
|
||||
|
||||
if (requiresAuth && !hasToken) {
|
||||
next({
|
||||
path: "/login",
|
||||
query: { redirect: to.fullPath }
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
export default router
|
||||
1
RuoYi-Vue/tmp-logs/qing-ui-preview/src/style.css
Normal file
1
RuoYi-Vue/tmp-logs/qing-ui-preview/src/style.css
Normal file
@@ -0,0 +1 @@
|
||||
body { margin: 0; }
|
||||
32
RuoYi-Vue/tmp-logs/qing-ui-preview/src/utils/dict.js
Normal file
32
RuoYi-Vue/tmp-logs/qing-ui-preview/src/utils/dict.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const dictData = {}
|
||||
|
||||
const emptyDict = []
|
||||
|
||||
function normalizeOptions(options) {
|
||||
if (!Array.isArray(options)) {
|
||||
return emptyDict
|
||||
}
|
||||
return options.map((option) => {
|
||||
const label = option.label || option.name || option.value
|
||||
const value = option.value || label
|
||||
return {
|
||||
...option,
|
||||
label,
|
||||
name: option.name || label,
|
||||
value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
getDictDataByType(type) {
|
||||
return normalizeOptions(dictData[type])
|
||||
},
|
||||
getDictLabel(type, value) {
|
||||
if (value === undefined || value === null || value === "") {
|
||||
return "-"
|
||||
}
|
||||
const matched = normalizeOptions(dictData[type]).find((option) => String(option.value) === String(value))
|
||||
return matched ? matched.label : value
|
||||
}
|
||||
}
|
||||
6
RuoYi-Vue/tmp-logs/qing-ui-preview/src/utils/request.js
Normal file
6
RuoYi-Vue/tmp-logs/qing-ui-preview/src/utils/request.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const page = { data: { records: [], total: 0 } }
|
||||
export default {
|
||||
get() { return Promise.resolve(page) },
|
||||
post(path) { return Promise.resolve(path === '/auth/login' ? { code: '200', data: { token: 'preview-token', user: { displayName: 'Preview User' } } } : { code: '200' }) },
|
||||
delete() { return Promise.resolve({ code: '200' }) }
|
||||
}
|
||||
1122
RuoYi-Vue/tmp-logs/qing-ui-preview/src/views/demo/product/index.vue
Normal file
1122
RuoYi-Vue/tmp-logs/qing-ui-preview/src/views/demo/product/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
291
RuoYi-Vue/tmp-logs/qing-ui-preview/src/views/login/index.vue
Normal file
291
RuoYi-Vue/tmp-logs/qing-ui-preview/src/views/login/index.vue
Normal file
@@ -0,0 +1,291 @@
|
||||
<template>
|
||||
<section class="portal-login">
|
||||
<div class="login-visual" aria-hidden="true">
|
||||
<div class="login-brand">
|
||||
<span class="login-logo"></span>
|
||||
<strong>演示商品</strong>
|
||||
</div>
|
||||
<h1>欢迎使用</h1>
|
||||
<p>登录后继续访问项目功能与个人服务。</p>
|
||||
</div>
|
||||
|
||||
<div class="login-panel">
|
||||
<div class="login-heading">
|
||||
<span>欢迎回来</span>
|
||||
<h2>账号登录</h2>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
v-if="roleOptions.length === 0"
|
||||
title="当前没有可登录的前台角色,请联系管理员检查系统角色配置。"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
class="login-alert"
|
||||
/>
|
||||
|
||||
<el-form :model="form" label-position="top" class="login-form" @submit.native.prevent>
|
||||
<el-form-item label="账号">
|
||||
<el-input v-model="form.account" placeholder="请输入账号" prefix-icon="el-icon-user" />
|
||||
</el-form-item>
|
||||
<el-form-item label="密码">
|
||||
<el-input v-model="form.password" type="password" placeholder="请输入密码" prefix-icon="el-icon-lock" show-password />
|
||||
</el-form-item>
|
||||
<el-button type="primary" class="login-button" :loading="loading" :disabled="roleOptions.length === 0" @click="handleLogin">登录</el-button>
|
||||
</el-form>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const roleOptions = [
|
||||
]
|
||||
|
||||
const defaultRole = roleOptions.length > 0 ? roleOptions[0] : null
|
||||
|
||||
export default {
|
||||
name: "PortalLogin",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
roleOptions,
|
||||
form: {
|
||||
roleCode: defaultRole ? defaultRole.value : "",
|
||||
account: defaultRole ? defaultRole.sampleAccount : "",
|
||||
password: "123456"
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"form.roleCode"(roleCode) {
|
||||
const role = this.roleOptions.find(item => item.value === roleCode)
|
||||
if (role && (!this.form.account || this.roleOptions.some(item => item.sampleAccount === this.form.account))) {
|
||||
this.form.account = role.sampleAccount
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleLogin() {
|
||||
if (!this.form.roleCode) {
|
||||
this.$message.warning("请选择登录角色")
|
||||
return
|
||||
}
|
||||
if (!this.form.account || !this.form.password) {
|
||||
this.$message.warning("请输入账号和密码")
|
||||
return
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
this.request.post("/auth/login", this.form)
|
||||
.then(res => {
|
||||
if (!res || res.code !== "200" || !res.data || !res.data.token) {
|
||||
throw new Error((res && res.msg) || "登录失败")
|
||||
}
|
||||
localStorage.setItem("portal-token", res.data.token)
|
||||
localStorage.setItem("portal-user", JSON.stringify(res.data.user || {
|
||||
account: this.form.account,
|
||||
displayName: this.form.account,
|
||||
roleCode: this.form.roleCode
|
||||
}))
|
||||
window.dispatchEvent(new Event("portal-user-change"))
|
||||
this.$message.success("登录成功")
|
||||
const redirect = this.$route.query.redirect || "/demo/product"
|
||||
this.$router.push(redirect)
|
||||
})
|
||||
.catch(error => {
|
||||
this.$message.error(error.message || "登录失败")
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.portal-login {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(430px, 36vw);
|
||||
min-height: 100vh;
|
||||
background:
|
||||
linear-gradient(132deg, rgba(20, 124, 131, 0.97), rgba(47, 114, 214, 0.91)),
|
||||
var(--portal-primary, #167d83);
|
||||
}
|
||||
|
||||
.login-visual {
|
||||
position: relative;
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
padding: 72px clamp(48px, 8vw, 128px);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.login-visual::before {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: block;
|
||||
content: "";
|
||||
opacity: 0.58;
|
||||
background:
|
||||
linear-gradient(42deg, transparent 0 18%, rgba(255, 255, 255, 0.18) 18% 27%, transparent 27% 100%),
|
||||
linear-gradient(132deg, transparent 0 48%, rgba(246, 186, 51, 0.32) 48% 55%, transparent 55% 100%);
|
||||
}
|
||||
|
||||
.login-brand,
|
||||
.login-visual h1,
|
||||
.login-visual p {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.login-brand {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 40px;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.login-logo {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 9px 19px 9px 19px;
|
||||
background: linear-gradient(135deg, #6de5e8 0 38%, #f6c85b 38% 66%, #ffffff 66%);
|
||||
transform: rotate(45deg);
|
||||
box-shadow: 0 8px 22px rgba(15, 23, 42, 0.18);
|
||||
}
|
||||
|
||||
.login-visual h1,
|
||||
.login-visual p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.login-visual h1 {
|
||||
max-width: 680px;
|
||||
font-size: clamp(42px, 5vw, 64px);
|
||||
font-weight: 700;
|
||||
letter-spacing: 0;
|
||||
line-height: 1.18;
|
||||
}
|
||||
|
||||
.login-visual p {
|
||||
max-width: 520px;
|
||||
margin-top: 20px;
|
||||
color: rgba(255, 255, 255, 0.88);
|
||||
font-size: 17px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.login-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: clamp(40px, 5vw, 64px);
|
||||
background: #ffffff;
|
||||
box-shadow: -18px 0 48px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
.login-heading span {
|
||||
color: var(--portal-primary, #167d83);
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.login-heading h2 {
|
||||
margin: 8px 0 28px;
|
||||
color: #111827;
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.login-alert {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.role-select,
|
||||
.login-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-form >>> .el-input__inner,
|
||||
.login-form >>> .el-select .el-input__inner {
|
||||
height: 44px;
|
||||
border-color: #dfe5ec;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.login-form >>> .el-input__inner:focus {
|
||||
border-color: var(--portal-primary, #167d83);
|
||||
box-shadow: 0 0 0 3px rgba(22, 125, 131, 0.12);
|
||||
}
|
||||
|
||||
.login-button {
|
||||
height: 44px;
|
||||
margin-top: 10px;
|
||||
border-color: var(--portal-primary, #167d83);
|
||||
border-radius: 8px;
|
||||
background: var(--portal-primary, #167d83);
|
||||
font-weight: 600;
|
||||
box-shadow: 0 10px 22px rgba(22, 125, 131, 0.18);
|
||||
}
|
||||
|
||||
.login-button:hover,
|
||||
.login-button:focus {
|
||||
border-color: var(--portal-primary-strong, #11656a);
|
||||
background: var(--portal-primary-strong, #11656a);
|
||||
}
|
||||
|
||||
@media (max-width: 880px) {
|
||||
.portal-login {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.login-visual {
|
||||
min-height: 220px;
|
||||
padding: 34px 26px;
|
||||
}
|
||||
|
||||
.login-visual h1 {
|
||||
font-size: 34px;
|
||||
}
|
||||
|
||||
.login-brand {
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.login-panel {
|
||||
padding: 34px 24px 44px;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.login-visual {
|
||||
min-height: 184px;
|
||||
padding: 26px 22px;
|
||||
}
|
||||
|
||||
.login-brand {
|
||||
margin-bottom: 20px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.login-visual h1 {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.login-visual p {
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
18
RuoYi-Vue/tmp-logs/qing-ui-preview/vue.config.js
Normal file
18
RuoYi-Vue/tmp-logs/qing-ui-preview/vue.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const port = process.env.PORT || 8081
|
||||
const apiBaseUrl = process.env.VUE_APP_API_BASE_URL || "http://localhost:8080"
|
||||
|
||||
module.exports = {
|
||||
transpileDependencies: [],
|
||||
devServer: {
|
||||
port,
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: apiBaseUrl,
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
"^/api": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user