init
29
.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
13
index.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vue 3 PC Web</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1569
package-lock.json
generated
Normal file
20
package.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "vue3-pc-web",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"vue-router": "^4.6.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
|
"less": "^4.2.0",
|
||||||
|
"vite": "^6.0.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
391
src/App.vue
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app">
|
||||||
|
<div class="viewport-wrapper" :style="viewportStyle">
|
||||||
|
<header class="header">
|
||||||
|
<div class="left">
|
||||||
|
<div class="logo-wrap"></div>
|
||||||
|
<div class="route-list">
|
||||||
|
<div class="route-item">IP版权库</div>
|
||||||
|
<div class="route-item">推广任务</div>
|
||||||
|
<div class="route-item">AI创作工具</div>
|
||||||
|
<div class="route-item">资产库</div>
|
||||||
|
<div class="route-item">我的作品</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<div class="login-list">
|
||||||
|
<div class="login-item">订阅</div>
|
||||||
|
<div class="login-item">登录</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</header>
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<aside style="display: none;" class="sidebar">
|
||||||
|
<div class="choose-image-or-video-wrap">
|
||||||
|
<div class="section-btn section-image">
|
||||||
|
<span>图</span>
|
||||||
|
</div>
|
||||||
|
<div class="section-btn section-video choosed">
|
||||||
|
<span>视</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<router-link to="/dashboard" active-class="active-sidebar-item">
|
||||||
|
<div class="sidebar-item">
|
||||||
|
<div class="sidebar-item__icon">
|
||||||
|
<span>ICON</span>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-item__text">
|
||||||
|
<span>图生视频</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</router-link>
|
||||||
|
<router-link to="/product" active-class="active-sidebar-item">
|
||||||
|
<div class="sidebar-item">
|
||||||
|
<div class="sidebar-item__icon">
|
||||||
|
<span>ICON</span>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-item__text">
|
||||||
|
<span>参考生视频</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<!-- <nav class="sidebar-nav">
|
||||||
|
<ul>
|
||||||
|
<li class="nav-item">
|
||||||
|
<router-link to="/dashboard" active-class="active">
|
||||||
|
<span class="nav-icon">📊</span>
|
||||||
|
<span class="nav-text">数据统计</span>
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<router-link to="/product" active-class="active">
|
||||||
|
<span class="nav-icon">📦</span>
|
||||||
|
<span class="nav-text">产品管理</span>
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<router-link to="/user" active-class="active">
|
||||||
|
<span class="nav-icon">👥</span>
|
||||||
|
<span class="nav-text">用户管理</span>
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<router-link to="/notification" active-class="active">
|
||||||
|
<span class="nav-icon">🔔</span>
|
||||||
|
<span class="nav-text">通知中心</span>
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<router-link to="/setting" active-class="active">
|
||||||
|
<span class="nav-icon">⚙️</span>
|
||||||
|
<span class="nav-text">系统设置</span>
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<router-link to="/document" active-class="active">
|
||||||
|
<span class="nav-icon">📚</span>
|
||||||
|
<span class="nav-text">文档中心</span>
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<router-link to="/message" active-class="active">
|
||||||
|
<span class="nav-icon">💬</span>
|
||||||
|
<span class="nav-text">消息管理</span>
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<router-link to="/analytics" active-class="active">
|
||||||
|
<span class="nav-icon">📈</span>
|
||||||
|
<span class="nav-text">数据分析</span>
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav> -->
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<main class="main">
|
||||||
|
<router-view />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
viewportStyle: this.resize()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
resize() {
|
||||||
|
console.log('wr-test-window.innerWidth', window.innerWidth);
|
||||||
|
const designWidth = 1920;
|
||||||
|
const designHeight = 1080;
|
||||||
|
if (window.innerWidth >= 1920) {
|
||||||
|
const result = {
|
||||||
|
transform: `scale(1)`,
|
||||||
|
width: `100%`,
|
||||||
|
height: `100%`,
|
||||||
|
transformOrigin: 'left top'
|
||||||
|
};
|
||||||
|
this.viewportStyle = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const scale = Math.min(window.innerWidth / designWidth, 1);
|
||||||
|
const result = {
|
||||||
|
transform: `scale(${scale})`,
|
||||||
|
width: `${designWidth}px`,
|
||||||
|
height: `${designHeight}px`,
|
||||||
|
transformOrigin: 'left top'
|
||||||
|
};
|
||||||
|
this.viewportStyle = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.resize();
|
||||||
|
window.addEventListener('resize', this.resize);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
window.removeEventListener('resize', this.resize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.app {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: @body-bg-color;
|
||||||
|
background-image: url('./assets/imgs/background/index-bg.png');
|
||||||
|
.img-bg();
|
||||||
|
overflow: auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewport-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
width: 100%;
|
||||||
|
height: @header-height;
|
||||||
|
backdrop-filter: blur(30px);
|
||||||
|
background: rgba(162, 162, 162, 0.2);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 70px;
|
||||||
|
.left {
|
||||||
|
.display-row-left()
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
.display-row-right()
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-wrap {
|
||||||
|
width: 150px;
|
||||||
|
height: 35px;
|
||||||
|
background-image: url('./assets/imgs/logo/logo.png');
|
||||||
|
.img-bg();
|
||||||
|
margin-right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-list {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.route-item {
|
||||||
|
width: 154px;
|
||||||
|
height: 54px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 20px;
|
||||||
|
color: @text-color;
|
||||||
|
.display-row-center();
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-list {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-content: center;
|
||||||
|
.login-item {
|
||||||
|
width: 100px;
|
||||||
|
height: 54px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
.display-row-center();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
min-height: @main-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
width: 120px;
|
||||||
|
background-color: @block-bg;
|
||||||
|
height: calc(100vh - 73px - 4px); /* 减去header和margin */
|
||||||
|
overflow-y: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 4px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 0;
|
||||||
|
|
||||||
|
.choose-image-or-video-wrap {
|
||||||
|
width: 80px;
|
||||||
|
height: 31px;
|
||||||
|
background: @body-bg-color;
|
||||||
|
border-radius: 2px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 5px 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
.section-btn {
|
||||||
|
width: 34px;
|
||||||
|
height: 21px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: @not-checked-section-bg;
|
||||||
|
color: @not-checked-section-color;
|
||||||
|
}
|
||||||
|
.choosed {
|
||||||
|
background-color: @checked-section-bg;
|
||||||
|
span {
|
||||||
|
.highlight-text()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 15px 0;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
background-color: @not-checked-section-bg;
|
||||||
|
|
||||||
|
.sidebar-item__icon {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
span {
|
||||||
|
color: @text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item__text {
|
||||||
|
font-size: 12px;
|
||||||
|
span {
|
||||||
|
color: @text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.active-sidebar-item {
|
||||||
|
.sidebar-item {
|
||||||
|
background-color: @body-bg-color;
|
||||||
|
.sidebar-item__icon {
|
||||||
|
span {
|
||||||
|
.highlight-text()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sidebar-item__text {
|
||||||
|
span {
|
||||||
|
.highlight-text()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-nav {
|
||||||
|
padding: 16px 0;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
a {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
color: #409eff;
|
||||||
|
border-right: 3px solid #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 24px;
|
||||||
|
color: #303133;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.3s;
|
||||||
|
border-right: 3px solid transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-right: 12px;
|
||||||
|
width: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
flex: 1;
|
||||||
|
background-color: transparent;
|
||||||
|
overflow-y: auto;
|
||||||
|
width: 100%;
|
||||||
|
// height: 100%;
|
||||||
|
min-height: @main-height;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
BIN
src/assets/imgs/background/index-bg.png
Normal file
|
After Width: | Height: | Size: 2.8 MiB |
BIN
src/assets/imgs/index/episode-checked-icon.png
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
src/assets/imgs/index/episode-checked.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
src/assets/imgs/index/episode.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
src/assets/imgs/index/generate-image-choosed.png
Normal file
|
After Width: | Height: | Size: 965 B |
BIN
src/assets/imgs/index/generate-image.png
Normal file
|
After Width: | Height: | Size: 1019 B |
BIN
src/assets/imgs/index/generate-video-choosed.png
Normal file
|
After Width: | Height: | Size: 883 B |
BIN
src/assets/imgs/index/generate-video.png
Normal file
|
After Width: | Height: | Size: 952 B |
BIN
src/assets/imgs/index/image-choosed.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
src/assets/imgs/index/options-image.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/imgs/index/raw-checked-icon.png
Normal file
|
After Width: | Height: | Size: 180 KiB |
BIN
src/assets/imgs/index/raw-checked.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
src/assets/imgs/index/raw.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
src/assets/imgs/index/video-choosed.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
src/assets/imgs/logo/logo.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
180
src/components/form/RadioGroup.vue
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
<template>
|
||||||
|
<!-- RadioGroup 组件容器 -->
|
||||||
|
<div class="custom-radio-group" :class="{ 'is-disabled': disabled }">
|
||||||
|
<!-- 可选的标题 -->
|
||||||
|
<div v-if="label" class="radio-group-label">{{ label }}</div>
|
||||||
|
|
||||||
|
<!-- Radio 选项列表 -->
|
||||||
|
<div class="radio-options">
|
||||||
|
<div
|
||||||
|
v-for="option in options"
|
||||||
|
:key="option.value"
|
||||||
|
class="radio-option"
|
||||||
|
:class="{ 'is-checked': option.value === modelValue, 'is-disabled': disabled }"
|
||||||
|
@click="selectOption(option)"
|
||||||
|
>
|
||||||
|
<!-- Radio 按钮 -->
|
||||||
|
<div class="radio-button">
|
||||||
|
<div class="radio-inner" :class="{ 'is-checked': option.value === modelValue }"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Radio 标签 -->
|
||||||
|
<div class="radio-label">{{ option.label }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'CustomRadioGroup',
|
||||||
|
props: {
|
||||||
|
// 绑定值,支持 v-model
|
||||||
|
modelValue: {
|
||||||
|
type: [String, Number, Boolean],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 选项列表
|
||||||
|
options: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
// 是否禁用
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 组标签
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['update:modelValue', 'change'],
|
||||||
|
methods: {
|
||||||
|
// 选择选项
|
||||||
|
selectOption(option) {
|
||||||
|
if (this.disabled) return;
|
||||||
|
|
||||||
|
// 更新 v-model 绑定值
|
||||||
|
this.$emit('update:modelValue', option.value);
|
||||||
|
// 触发 change 事件
|
||||||
|
this.$emit('change', option.value, option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
/* RadioGroup 组件容器 */
|
||||||
|
.custom-radio-group {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
/* 禁用状态 */
|
||||||
|
&.is-disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 组标签 */
|
||||||
|
.radio-group-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radio 选项列表 - 一行内布局 */
|
||||||
|
.radio-options {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radio 选项 - flex布局且宽度等分 */
|
||||||
|
.radio-option {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 12px 0;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
/* 选中状态 */
|
||||||
|
&.is-checked {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
border-color: #409eff;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 悬停状态 */
|
||||||
|
&:not(.is-disabled):hover {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
border-color: #c6e2ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 禁用状态 */
|
||||||
|
&.is-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radio 按钮 */
|
||||||
|
.radio-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 8px;
|
||||||
|
|
||||||
|
/* Radio 内部圆点 */
|
||||||
|
.radio-inner {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border: 2px solid #dcdfe6;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #fff;
|
||||||
|
transition: all 0.3s;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
/* 选中状态的内部圆点 */
|
||||||
|
&.is-checked {
|
||||||
|
border-color: #409eff;
|
||||||
|
background-color: #409eff;
|
||||||
|
|
||||||
|
/* 选中后的小圆点 */
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radio 标签 */
|
||||||
|
.radio-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
/* 选中状态的标签颜色 */
|
||||||
|
.radio-option.is-checked & {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
330
src/components/form/Select.vue
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
<template>
|
||||||
|
<!-- Select 组件容器 -->
|
||||||
|
<div class="custom-select" :class="{ 'is-open': isOpen, 'is-disabled': disabled }">
|
||||||
|
<!-- 选择框主体 -->
|
||||||
|
<div
|
||||||
|
class="select-trigger"
|
||||||
|
@click.stop="toggleDropdown"
|
||||||
|
>
|
||||||
|
<!-- 选中的值 -->
|
||||||
|
<span class="select-value">{{ selectedLabel || placeholder }}</span>
|
||||||
|
<!-- 下拉箭头 -->
|
||||||
|
<span class="select-arrow">▼</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 下拉选项列表 -->
|
||||||
|
<div v-if="isOpen" class="select-dropdown">
|
||||||
|
<!-- 搜索框(可选) -->
|
||||||
|
<div v-if="filterable" class="select-search">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="search-input"
|
||||||
|
placeholder="搜索选项..."
|
||||||
|
v-model="searchValue"
|
||||||
|
@input="handleSearch"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 选项列表 -->
|
||||||
|
<div class="select-options">
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<div v-if="loading" class="select-loading">
|
||||||
|
<div class="loading-spinner"></div>
|
||||||
|
<span>加载中...</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 无选项状态 -->
|
||||||
|
<div v-else-if="filteredOptions.length === 0" class="select-empty">
|
||||||
|
无匹配选项
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 选项列表 -->
|
||||||
|
<div
|
||||||
|
v-for="option in filteredOptions"
|
||||||
|
:key="option.value"
|
||||||
|
class="select-option"
|
||||||
|
:class="{ 'is-selected': option.value === modelValue }"
|
||||||
|
@click="selectOption(option)"
|
||||||
|
>
|
||||||
|
{{ option.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'CustomSelect',
|
||||||
|
props: {
|
||||||
|
// 绑定值,支持 v-model
|
||||||
|
modelValue: {
|
||||||
|
type: [String, Number, Boolean],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 选项列表
|
||||||
|
options: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
// 占位符
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: '请选择'
|
||||||
|
},
|
||||||
|
// 是否可搜索
|
||||||
|
filterable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 是否禁用
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 是否加载中
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['update:modelValue', 'change', 'search'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isOpen: true, // 下拉框是否打开
|
||||||
|
searchValue: '', // 搜索值
|
||||||
|
selectedLabel: '' // 选中的标签
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 过滤后的选项
|
||||||
|
filteredOptions() {
|
||||||
|
if (!this.searchValue) {
|
||||||
|
return this.options
|
||||||
|
}
|
||||||
|
return this.options.filter(option =>
|
||||||
|
option.label.toLowerCase().includes(this.searchValue.toLowerCase())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// 监听绑定值变化,更新选中标签
|
||||||
|
modelValue: {
|
||||||
|
handler(newValue) {
|
||||||
|
this.updateSelectedLabel(newValue)
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
// 监听选项列表变化,更新选中标签
|
||||||
|
options: {
|
||||||
|
handler() {
|
||||||
|
this.updateSelectedLabel(this.modelValue)
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// 点击外部关闭下拉框
|
||||||
|
document.addEventListener('click', this.closeDropdown)
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
// 移除事件监听
|
||||||
|
document.removeEventListener('click', this.closeDropdown)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 更新选中标签
|
||||||
|
updateSelectedLabel(value) {
|
||||||
|
const option = this.options.find(opt => opt.value === value)
|
||||||
|
this.selectedLabel = option ? option.label : ''
|
||||||
|
},
|
||||||
|
|
||||||
|
// 切换下拉框显示/隐藏
|
||||||
|
toggleDropdown() {
|
||||||
|
if (this.disabled) return
|
||||||
|
this.isOpen = !this.isOpen;
|
||||||
|
console.log('wr-test-isOpen', this.isOpen);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 关闭下拉框
|
||||||
|
closeDropdown(e) {
|
||||||
|
if (!this.$el.contains(e.target)) {
|
||||||
|
this.isOpen = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 选择选项
|
||||||
|
selectOption(option) {
|
||||||
|
// 更新 v-model 绑定值
|
||||||
|
this.$emit('update:modelValue', option.value)
|
||||||
|
// 触发 change 事件
|
||||||
|
this.$emit('change', option.value, option)
|
||||||
|
// 关闭下拉框
|
||||||
|
this.isOpen = false
|
||||||
|
// 清空搜索值
|
||||||
|
this.searchValue = ''
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理搜索
|
||||||
|
handleSearch() {
|
||||||
|
this.$emit('search', this.searchValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
/* Select 组件容器 */
|
||||||
|
.custom-select {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
/* 打开状态 */
|
||||||
|
&.is-open {
|
||||||
|
.select-arrow {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 禁用状态 */
|
||||||
|
&.is-disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选择框主体 */
|
||||||
|
.select-trigger {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px 14px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #c6e2ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #409eff;
|
||||||
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选中的值 */
|
||||||
|
.select-value {
|
||||||
|
flex: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
/* 占位符样式 */
|
||||||
|
.custom-select:not(.is-open) & {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 下拉箭头 */
|
||||||
|
.select-arrow {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 下拉选项列表 */
|
||||||
|
.select-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin-top: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 搜索框 */
|
||||||
|
.select-search {
|
||||||
|
padding: 10px;
|
||||||
|
border-bottom: 1px solid #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选项列表 */
|
||||||
|
.select-options {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选项项 */
|
||||||
|
.select-option {
|
||||||
|
padding: 10px 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选中状态 */
|
||||||
|
&.is-selected {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 加载状态 */
|
||||||
|
.select-loading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
|
color: #909399;
|
||||||
|
|
||||||
|
.loading-spinner {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border: 2px solid #f3f3f3;
|
||||||
|
border-top: 2px solid #409eff;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 无选项状态 */
|
||||||
|
.select-empty {
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 加载动画 */
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
108
src/components/form/Switch.vue
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<template>
|
||||||
|
<!-- Switch 组件容器 -->
|
||||||
|
<div
|
||||||
|
class="custom-switch"
|
||||||
|
:class="{ 'is-checked': modelValue, 'is-disabled': disabled }"
|
||||||
|
@click="toggle"
|
||||||
|
>
|
||||||
|
<!-- 开关轨道 -->
|
||||||
|
<div class="switch-track">
|
||||||
|
<!-- 开关滑块 -->
|
||||||
|
<div class="switch-thumb"></div>
|
||||||
|
</div>
|
||||||
|
<!-- 可选的标签文字 -->
|
||||||
|
<span v-if="label" class="switch-label">{{ label }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'CustomSwitch',
|
||||||
|
props: {
|
||||||
|
// 绑定值,支持 v-model
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 是否禁用
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 开关标签
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['update:modelValue', 'change'],
|
||||||
|
methods: {
|
||||||
|
// 切换开关状态
|
||||||
|
toggle() {
|
||||||
|
if (this.disabled) return;
|
||||||
|
|
||||||
|
const newValue = !this.modelValue;
|
||||||
|
// 更新 v-model 绑定值
|
||||||
|
this.$emit('update:modelValue', newValue);
|
||||||
|
// 触发 change 事件
|
||||||
|
this.$emit('change', newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
/* 开关容器 */
|
||||||
|
.custom-switch {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
/* 禁用状态 */
|
||||||
|
&.is-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选中状态的轨道颜色 */
|
||||||
|
&.is-checked .switch-track {
|
||||||
|
background-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选中状态的滑块位置 */
|
||||||
|
&.is-checked .switch-thumb {
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 开关轨道 */
|
||||||
|
.switch-track {
|
||||||
|
position: relative;
|
||||||
|
width: 44px;
|
||||||
|
height: 24px;
|
||||||
|
background-color: #dcdfe6;
|
||||||
|
border-radius: 12px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 开关滑块 */
|
||||||
|
.switch-thumb {
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 开关标签 */
|
||||||
|
.switch-label {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
220
src/components/form/Textarea.vue
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
<template>
|
||||||
|
<!-- Textarea 组件容器 -->
|
||||||
|
<div class="custom-textarea-wrapper">
|
||||||
|
<!-- Textarea 主体 -->
|
||||||
|
<div class="textarea-container">
|
||||||
|
<textarea
|
||||||
|
ref="textareaRef"
|
||||||
|
class="custom-textarea"
|
||||||
|
:value="modelValue"
|
||||||
|
@input="handleInput"
|
||||||
|
@change="handleChange"
|
||||||
|
@blur="handleBlur"
|
||||||
|
@focus="handleFocus"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:disabled="disabled"
|
||||||
|
:rows="rows"
|
||||||
|
:cols="cols"
|
||||||
|
:maxlength="maxlength"
|
||||||
|
></textarea>
|
||||||
|
|
||||||
|
<!-- 左下角 slot -->
|
||||||
|
<div class="textarea-slot textarea-slot-left">
|
||||||
|
<slot name="left"></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右下角 slot -->
|
||||||
|
<div class="textarea-slot textarea-slot-right">
|
||||||
|
<slot name="right"></slot>
|
||||||
|
<!-- 可选的字数统计 -->
|
||||||
|
<span v-if="showWordLimit" class="word-limit">
|
||||||
|
{{ currentLength }}/{{ maxlength }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'CustomTextarea',
|
||||||
|
props: {
|
||||||
|
// 绑定值,支持 v-model
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 占位符
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: '请输入内容...'
|
||||||
|
},
|
||||||
|
// 行数
|
||||||
|
rows: {
|
||||||
|
type: Number,
|
||||||
|
default: 4
|
||||||
|
},
|
||||||
|
// 列数
|
||||||
|
cols: {
|
||||||
|
type: Number,
|
||||||
|
default: 50
|
||||||
|
},
|
||||||
|
// 是否禁用
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 最大长度
|
||||||
|
maxlength: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
// 是否显示字数统计
|
||||||
|
showWordLimit: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['update:modelValue', 'input', 'change', 'blur', 'focus'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentLength: 0 // 当前输入长度
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// 监听绑定值变化,更新当前长度
|
||||||
|
modelValue: {
|
||||||
|
handler(newValue) {
|
||||||
|
this.currentLength = newValue.length
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 处理输入事件
|
||||||
|
handleInput(e) {
|
||||||
|
const value = e.target.value
|
||||||
|
// 更新当前长度
|
||||||
|
this.currentLength = value.length
|
||||||
|
// 更新 v-model 绑定值
|
||||||
|
this.$emit('update:modelValue', value)
|
||||||
|
// 触发 input 事件
|
||||||
|
this.$emit('input', e)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理 change 事件
|
||||||
|
handleChange(e) {
|
||||||
|
this.$emit('change', e)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理 blur 事件
|
||||||
|
handleBlur(e) {
|
||||||
|
this.$emit('blur', e)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理 focus 事件
|
||||||
|
handleFocus(e) {
|
||||||
|
this.$emit('focus', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
/* Textarea 组件容器 */
|
||||||
|
.custom-textarea-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Textarea 容器(用于定位 slot) */
|
||||||
|
.textarea-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Textarea 主体样式 */
|
||||||
|
.custom-textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100px;
|
||||||
|
padding: 12px;
|
||||||
|
background-color: #f5f7fa; /* 自定义背景色 */
|
||||||
|
color: #303133; /* 自定义文字色 */
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
outline: none;
|
||||||
|
transition: all 0.3s;
|
||||||
|
resize: vertical; /* 允许垂直调整大小 */
|
||||||
|
|
||||||
|
/* 占位符样式 */
|
||||||
|
&::placeholder {
|
||||||
|
color: #909399; /* 自定义 placeholder 颜色 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 聚焦状态 */
|
||||||
|
&:focus {
|
||||||
|
border-color: #409eff;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 禁用状态 */
|
||||||
|
&:disabled {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
color: #c0c4cc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 自定义滚动条样式 */
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: #f1f1f1;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: #c1c1c1;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #a8a8a8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slot 容器 */
|
||||||
|
.textarea-slot {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
padding: 4px 8px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左下角 slot */
|
||||||
|
.textarea-slot-left {
|
||||||
|
bottom: 4px;
|
||||||
|
left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右下角 slot */
|
||||||
|
.textarea-slot-right {
|
||||||
|
bottom: 4px;
|
||||||
|
right: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 字数统计 */
|
||||||
|
.word-limit {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
8
src/main.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import './styles/theme.less'
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
app.use(router)
|
||||||
|
app.mount('#app')
|
||||||
108
src/router/index.js
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
|
// 导入页面组件
|
||||||
|
const Dashboard = () => import('../views/Dashboard.vue')
|
||||||
|
|
||||||
|
const Index = () => import('../views/Index.vue')
|
||||||
|
|
||||||
|
const Product = () => import('../views/Product.vue')
|
||||||
|
const User = () => import('../views/User.vue')
|
||||||
|
const Notification = () => import('../views/Notification.vue')
|
||||||
|
const Setting = () => import('../views/Setting.vue')
|
||||||
|
const Document = () => import('../views/Document.vue')
|
||||||
|
const Message = () => import('../views/Message.vue')
|
||||||
|
const Analytics = () => import('../views/Analytics.vue')
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
redirect: '/index'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/index',
|
||||||
|
name: 'Index',
|
||||||
|
component: Index,
|
||||||
|
meta: {
|
||||||
|
title: '首页',
|
||||||
|
icon: '📊'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/product',
|
||||||
|
name: 'Product',
|
||||||
|
component: Product,
|
||||||
|
meta: {
|
||||||
|
title: '产品管理',
|
||||||
|
icon: '📦'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/user',
|
||||||
|
name: 'User',
|
||||||
|
component: User,
|
||||||
|
meta: {
|
||||||
|
title: '用户管理',
|
||||||
|
icon: '👥'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/notification',
|
||||||
|
name: 'Notification',
|
||||||
|
component: Notification,
|
||||||
|
meta: {
|
||||||
|
title: '通知中心',
|
||||||
|
icon: '🔔'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/setting',
|
||||||
|
name: 'Setting',
|
||||||
|
component: Setting,
|
||||||
|
meta: {
|
||||||
|
title: '系统设置',
|
||||||
|
icon: '⚙️'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/document',
|
||||||
|
name: 'Document',
|
||||||
|
component: Document,
|
||||||
|
meta: {
|
||||||
|
title: '文档中心',
|
||||||
|
icon: '📚'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/message',
|
||||||
|
name: 'Message',
|
||||||
|
component: Message,
|
||||||
|
meta: {
|
||||||
|
title: '消息管理',
|
||||||
|
icon: '💬'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/analytics',
|
||||||
|
name: 'Analytics',
|
||||||
|
component: Analytics,
|
||||||
|
meta: {
|
||||||
|
title: '数据分析',
|
||||||
|
icon: '📈'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(), // 使用history模式,而非hash模式
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
// 路由守卫,设置页面标题
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
if (to.meta.title) {
|
||||||
|
document.title = `${to.meta.title} - Vue 3 PC Web`
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
115
src/styles/theme.less
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
// ---------全局变量------------ 开始
|
||||||
|
@body-bg-color: #000000;
|
||||||
|
@text-color: #ffffff;
|
||||||
|
@button-bg: linear-gradient( 230deg, #FBC7FF 0%, #A8FFFF 34.48%, #FFFBDB 68.47%, #E9E0FF 100%);
|
||||||
|
@block-bg: #24222D;
|
||||||
|
@checked-section-bg: #313131 !important;
|
||||||
|
@checked-section-color: @button-bg !important;
|
||||||
|
@not-checked-section-bg: transparent;
|
||||||
|
@not-checked-section-color: #717171;
|
||||||
|
@header-height: 93px;
|
||||||
|
@main-height: calc(1080px - @header-height);
|
||||||
|
|
||||||
|
// ---------全局变量------------ 结束
|
||||||
|
|
||||||
|
|
||||||
|
// ---------基础样式------------ 开始
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: @body-bg-color;
|
||||||
|
color: @text-color;
|
||||||
|
font-family: Source Han Sans CN, Source Han Sans CN;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
height: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: #505050;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: fade(@text-color, 40%);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: transparent;
|
||||||
|
color: #ffffff;
|
||||||
|
font-family: Source Han Sans CN, Source Han Sans CN;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 28px; // 新增行高设置
|
||||||
|
border: none;
|
||||||
|
resize: none;
|
||||||
|
&:hover {
|
||||||
|
border-color: none;
|
||||||
|
}
|
||||||
|
&:focus {
|
||||||
|
border-color: none;
|
||||||
|
outline: none; /* 移除浏览器默认的 outline */
|
||||||
|
}
|
||||||
|
&::placeholder { /* Standard syntax */
|
||||||
|
font-family: Source Han Sans CN, Source Han Sans CN;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #767676;
|
||||||
|
line-height: 28px; // 新增行高设置
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ---------基础样式------------ 结束
|
||||||
|
|
||||||
|
// ---------常用类定义------------ 开始
|
||||||
|
|
||||||
|
.highlight-text {
|
||||||
|
background-image: @button-bg;
|
||||||
|
color: transparent;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-row-center {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
.display-row-center();
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-row-left {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-row-right {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-bg {
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100%;
|
||||||
|
background-position: center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------常用类定义------------ 结束
|
||||||
438
src/views/Analytics.vue
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-container">
|
||||||
|
<h2 class="page-title">📈 数据分析</h2>
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="time-range-selector">
|
||||||
|
<button class="range-btn active">今日</button>
|
||||||
|
<button class="range-btn">昨日</button>
|
||||||
|
<button class="range-btn">本周</button>
|
||||||
|
<button class="range-btn">本月</button>
|
||||||
|
<button class="range-btn">自定义</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="analytics-grid">
|
||||||
|
<div class="card">
|
||||||
|
<h3>流量分析</h3>
|
||||||
|
<div class="chart-placeholder line-chart">
|
||||||
|
<div class="line-pattern"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>转化率分析</h3>
|
||||||
|
<div class="conversion-steps">
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-number">1</div>
|
||||||
|
<div class="step-info">
|
||||||
|
<div class="step-title">访问</div>
|
||||||
|
<div class="step-value">10,000</div>
|
||||||
|
</div>
|
||||||
|
<div class="step-arrow">→</div>
|
||||||
|
</div>
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-number">2</div>
|
||||||
|
<div class="step-info">
|
||||||
|
<div class="step-title">浏览</div>
|
||||||
|
<div class="step-value">7,500</div>
|
||||||
|
<div class="step-rate">75%</div>
|
||||||
|
</div>
|
||||||
|
<div class="step-arrow">→</div>
|
||||||
|
</div>
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-number">3</div>
|
||||||
|
<div class="step-info">
|
||||||
|
<div class="step-title">注册</div>
|
||||||
|
<div class="step-value">1,500</div>
|
||||||
|
<div class="step-rate">20%</div>
|
||||||
|
</div>
|
||||||
|
<div class="step-arrow">→</div>
|
||||||
|
</div>
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-number">4</div>
|
||||||
|
<div class="step-info">
|
||||||
|
<div class="step-title">购买</div>
|
||||||
|
<div class="step-value">300</div>
|
||||||
|
<div class="step-rate">20%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>用户来源</h3>
|
||||||
|
<div class="source-list">
|
||||||
|
<div class="source-item">
|
||||||
|
<div class="source-name">直接访问</div>
|
||||||
|
<div class="source-bar">
|
||||||
|
<div class="source-progress" style="width: 45%;"></div>
|
||||||
|
</div>
|
||||||
|
<div class="source-percent">45%</div>
|
||||||
|
</div>
|
||||||
|
<div class="source-item">
|
||||||
|
<div class="source-name">搜索引擎</div>
|
||||||
|
<div class="source-bar">
|
||||||
|
<div class="source-progress" style="width: 30%;"></div>
|
||||||
|
</div>
|
||||||
|
<div class="source-percent">30%</div>
|
||||||
|
</div>
|
||||||
|
<div class="source-item">
|
||||||
|
<div class="source-name">社交媒体</div>
|
||||||
|
<div class="source-bar">
|
||||||
|
<div class="source-progress" style="width: 15%;"></div>
|
||||||
|
</div>
|
||||||
|
<div class="source-percent">15%</div>
|
||||||
|
</div>
|
||||||
|
<div class="source-item">
|
||||||
|
<div class="source-name">其他</div>
|
||||||
|
<div class="source-bar">
|
||||||
|
<div class="source-progress" style="width: 10%;"></div>
|
||||||
|
</div>
|
||||||
|
<div class="source-percent">10%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>热门页面</h3>
|
||||||
|
<div class="top-pages">
|
||||||
|
<div class="page-item">
|
||||||
|
<div class="page-rank">1</div>
|
||||||
|
<div class="page-info">
|
||||||
|
<div class="page-title">首页</div>
|
||||||
|
<div class="page-path">/</div>
|
||||||
|
</div>
|
||||||
|
<div class="page-views">5,678</div>
|
||||||
|
</div>
|
||||||
|
<div class="page-item">
|
||||||
|
<div class="page-rank">2</div>
|
||||||
|
<div class="page-info">
|
||||||
|
<div class="page-title">产品列表</div>
|
||||||
|
<div class="page-path">/products</div>
|
||||||
|
</div>
|
||||||
|
<div class="page-views">3,456</div>
|
||||||
|
</div>
|
||||||
|
<div class="page-item">
|
||||||
|
<div class="page-rank">3</div>
|
||||||
|
<div class="page-info">
|
||||||
|
<div class="page-title">详情页</div>
|
||||||
|
<div class="page-path">/product/123</div>
|
||||||
|
</div>
|
||||||
|
<div class="page-views">2,345</div>
|
||||||
|
</div>
|
||||||
|
<div class="page-item">
|
||||||
|
<div class="page-rank">4</div>
|
||||||
|
<div class="page-info">
|
||||||
|
<div class="page-title">关于我们</div>
|
||||||
|
<div class="page-path">/about</div>
|
||||||
|
</div>
|
||||||
|
<div class="page-views">1,234</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Analytics'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.page-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-range-selector {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-btn {
|
||||||
|
padding: 8px 16px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #606266;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #409eff;
|
||||||
|
border-color: #c6e2ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #409eff;
|
||||||
|
color: #fff;
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.analytics-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card h3 {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-placeholder {
|
||||||
|
height: 200px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-chart {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-pattern {
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
background-color: #dcdfe6;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
45deg,
|
||||||
|
transparent,
|
||||||
|
transparent 10px,
|
||||||
|
#409eff 10px,
|
||||||
|
#409eff 20px
|
||||||
|
);
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
left: 20%;
|
||||||
|
right: 30%;
|
||||||
|
height: 4px;
|
||||||
|
background-color: #409eff;
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow: 0 2px 6px rgba(64, 158, 255, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversion-steps {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 25px;
|
||||||
|
top: 50px;
|
||||||
|
width: 2px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: #dcdfe6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-number {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #409eff;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-right: 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-value {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #409eff;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-rate {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #67c23a;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-arrow {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #dcdfe6;
|
||||||
|
margin-left: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-name {
|
||||||
|
width: 100px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-bar {
|
||||||
|
flex: 1;
|
||||||
|
height: 8px;
|
||||||
|
background-color: #f0f2f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-progress {
|
||||||
|
height: 100%;
|
||||||
|
background-color: #409eff;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: width 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-percent {
|
||||||
|
width: 50px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-pages {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid #f5f7fa;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-rank {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
color: #606266;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&:nth-child(1) {
|
||||||
|
background-color: #fde2e2;
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(2) {
|
||||||
|
background-color: #e1f3d8;
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(3) {
|
||||||
|
background-color: #ebe5fe;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-path {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-views {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
200
src/views/Dashboard.vue
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-container">
|
||||||
|
<h2 class="page-title">📊 数据统计</h2>
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-number">12,345</div>
|
||||||
|
<div class="stat-label">总访问量</div>
|
||||||
|
<div class="stat-change positive">+12.5%</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-number">6,789</div>
|
||||||
|
<div class="stat-label">活跃用户</div>
|
||||||
|
<div class="stat-change positive">+8.3%</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-number">¥98,765</div>
|
||||||
|
<div class="stat-label">总收入</div>
|
||||||
|
<div class="stat-change positive">+15.2%</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-number">456</div>
|
||||||
|
<div class="stat-label">订单数</div>
|
||||||
|
<div class="stat-change negative">-2.1%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="charts-section">
|
||||||
|
<div class="card">
|
||||||
|
<h3>访问趋势</h3>
|
||||||
|
<div class="chart-placeholder">
|
||||||
|
<div class="chart-bar" style="height: 60%;"></div>
|
||||||
|
<div class="chart-bar" style="height: 85%;"></div>
|
||||||
|
<div class="chart-bar" style="height: 70%;"></div>
|
||||||
|
<div class="chart-bar" style="height: 95%;"></div>
|
||||||
|
<div class="chart-bar" style="height: 80%;"></div>
|
||||||
|
<div class="chart-bar" style="height: 90%;"></div>
|
||||||
|
<div class="chart-bar" style="height: 75%;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>用户分布</h3>
|
||||||
|
<div class="pie-chart-placeholder">
|
||||||
|
<div class="pie-slice slice-1"></div>
|
||||||
|
<div class="pie-slice slice-2"></div>
|
||||||
|
<div class="pie-slice slice-3"></div>
|
||||||
|
<div class="pie-slice slice-4"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Dashboard'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.page-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
transition: transform 0.3s, box-shadow 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-number {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-change {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&.positive {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.negative {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.charts-section {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 2fr 1fr;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card h3 {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-placeholder {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: space-around;
|
||||||
|
height: 200px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-bar {
|
||||||
|
width: 30px;
|
||||||
|
background-color: #409eff;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #66b1ff;
|
||||||
|
transform: scaleY(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pie-chart-placeholder {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pie-slice {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&.slice-1 {
|
||||||
|
background-color: #409eff;
|
||||||
|
clip-path: polygon(50% 50%, 50% 0%, 100% 0%, 100% 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.slice-2 {
|
||||||
|
background-color: #67c23a;
|
||||||
|
clip-path: polygon(50% 50%, 100% 50%, 100% 100%, 50% 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.slice-3 {
|
||||||
|
background-color: #e6a23c;
|
||||||
|
clip-path: polygon(50% 50%, 50% 100%, 0% 100%, 0% 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.slice-4 {
|
||||||
|
background-color: #f56c6c;
|
||||||
|
clip-path: polygon(50% 50%, 0% 50%, 0% 0%, 50% 0%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
217
src/views/Document.vue
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-container">
|
||||||
|
<h2 class="page-title">📚 文档中心</h2>
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="documents-grid">
|
||||||
|
<div class="document-card">
|
||||||
|
<div class="document-icon">📄</div>
|
||||||
|
<div class="document-info">
|
||||||
|
<div class="document-title">API 文档</div>
|
||||||
|
<div class="document-desc">系统 API 接口详细说明</div>
|
||||||
|
<div class="document-meta">
|
||||||
|
<span class="document-date">2024-01-20</span>
|
||||||
|
<span class="document-size">1.2 MB</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="document-actions">
|
||||||
|
<button class="btn btn-small">查看</button>
|
||||||
|
<button class="btn btn-small">下载</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="document-card">
|
||||||
|
<div class="document-icon">📋</div>
|
||||||
|
<div class="document-info">
|
||||||
|
<div class="document-title">使用手册</div>
|
||||||
|
<div class="document-desc">系统功能使用指南</div>
|
||||||
|
<div class="document-meta">
|
||||||
|
<span class="document-date">2024-01-15</span>
|
||||||
|
<span class="document-size">856 KB</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="document-actions">
|
||||||
|
<button class="btn btn-small">查看</button>
|
||||||
|
<button class="btn btn-small">下载</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="document-card">
|
||||||
|
<div class="document-icon">📊</div>
|
||||||
|
<div class="document-info">
|
||||||
|
<div class="document-title">数据字典</div>
|
||||||
|
<div class="document-desc">系统数据结构说明</div>
|
||||||
|
<div class="document-meta">
|
||||||
|
<span class="document-date">2024-01-10</span>
|
||||||
|
<span class="document-size">523 KB</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="document-actions">
|
||||||
|
<button class="btn btn-small">查看</button>
|
||||||
|
<button class="btn btn-small">下载</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="document-card">
|
||||||
|
<div class="document-icon">🔧</div>
|
||||||
|
<div class="document-info">
|
||||||
|
<div class="document-title">部署指南</div>
|
||||||
|
<div class="document-desc">系统部署和配置说明</div>
|
||||||
|
<div class="document-meta">
|
||||||
|
<span class="document-date">2024-01-05</span>
|
||||||
|
<span class="document-size">345 KB</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="document-actions">
|
||||||
|
<button class="btn btn-small">查看</button>
|
||||||
|
<button class="btn btn-small">下载</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="document-categories">
|
||||||
|
<h3>文档分类</h3>
|
||||||
|
<div class="categories-list">
|
||||||
|
<span class="category-tag active">全部</span>
|
||||||
|
<span class="category-tag">API 文档</span>
|
||||||
|
<span class="category-tag">使用手册</span>
|
||||||
|
<span class="category-tag">开发文档</span>
|
||||||
|
<span class="category-tag">部署文档</span>
|
||||||
|
<span class="category-tag">其他</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Document'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.page-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.documents-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
transition: transform 0.3s, box-shadow 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-right: 16px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-desc {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.document-date,
|
||||||
|
.document-size {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-small {
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-categories {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-categories h3 {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categories-list {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-tag {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
color: #606266;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #409eff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
330
src/views/Index.vue
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-container">
|
||||||
|
<div class="index-title">
|
||||||
|
AI 造万象,创意不设限
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="model-section-group">
|
||||||
|
<div class="model-section section-raw checked">
|
||||||
|
<div class="section-title"></div>
|
||||||
|
<div class="section-desc">图片生成,视频生成,音频生成</div>
|
||||||
|
<div class="section__checked-icon"></div>
|
||||||
|
</div>
|
||||||
|
<div class="model-section section-episode">
|
||||||
|
<div class="section-title"></div>
|
||||||
|
<div class="section-desc">剧本生成,分镜生成,自动配乐</div>
|
||||||
|
<div class="section__checked-icon"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="generate">
|
||||||
|
<div class="generate__mode" :class="`generate__mode__choose-${generateMode}`">
|
||||||
|
<div class="generate__mode-item generate__mode-item__image" :class="generateMode === generate_TYPES.IMAGE ? 'choosed': ''">
|
||||||
|
<div class="generate__mode-item__inner">
|
||||||
|
<div class="generate__mode-item__icon generate__mode-item__image-icon"></div>
|
||||||
|
<div class="generate__mode-item__text">图片生成</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="generate__mode-item generate__mode-item__video" :class="generateMode === generate_TYPES.VIDEO ? 'choosed': ''">
|
||||||
|
<div class="generate__mode-item__inner">
|
||||||
|
<div class="generate__mode-item__icon generate__mode-item__video-icon"></div>
|
||||||
|
<div class="generate__mode-item__text">视频生成</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="generate__form">
|
||||||
|
<div class="generate__form-input">
|
||||||
|
<div class="generate__form-input__upload"></div>
|
||||||
|
<div class="generate__form-input__textarea">
|
||||||
|
<textarea placeholder="请输入你的创意(按Enter发送)"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="generate__form-input__right">
|
||||||
|
<div class="trans-switch">英文翻译</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="generate__form-settings">
|
||||||
|
<model-select></model-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import modelSelect from './index/components/select/modelSelect.vue';
|
||||||
|
const generate_TYPES = {
|
||||||
|
IMAGE: 'image',
|
||||||
|
VIDEO: 'video'
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
name: 'Index',
|
||||||
|
components: { modelSelect },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
generate_TYPES,
|
||||||
|
generateMode: generate_TYPES.IMAGE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
@indexImages: "../assets/imgs/index";
|
||||||
|
|
||||||
|
.page-container {
|
||||||
|
width: 100%;
|
||||||
|
min-height: @main-height;
|
||||||
|
background: transparent;
|
||||||
|
padding: 53px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-title {
|
||||||
|
width: 100%;
|
||||||
|
font-family: Source Han Sans CN, Source Han Sans CN;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 40px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
margin: 0 auto 102px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-section-group {
|
||||||
|
width: 1200px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-content: center;
|
||||||
|
margin: 0 auto 45px;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.model-section {
|
||||||
|
width: 576px;
|
||||||
|
height: 94px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: transparent;
|
||||||
|
backdrop-filter: blur(30px);
|
||||||
|
background: rgba(162, 162, 162, 0.2);
|
||||||
|
padding-left: 36px;
|
||||||
|
padding-top: 24px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.section {
|
||||||
|
&-title {
|
||||||
|
width: 120px;
|
||||||
|
height: 24px;
|
||||||
|
.img-bg();
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
&-desc {
|
||||||
|
font-family: Source Han Sans CN, Source Han Sans CN;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #FAF2FF;
|
||||||
|
}
|
||||||
|
&__checked-icon {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 576px;
|
||||||
|
height: 170px;
|
||||||
|
.img-bg();
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.section {
|
||||||
|
&-episode {
|
||||||
|
.section {
|
||||||
|
&-title {
|
||||||
|
background-image: url('@{indexImages}/episode.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-raw {
|
||||||
|
.section {
|
||||||
|
&-title {
|
||||||
|
background-image: url('@{indexImages}/raw.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.checked {
|
||||||
|
&.model-section {
|
||||||
|
background: @button-bg;
|
||||||
|
}
|
||||||
|
& .section {
|
||||||
|
&-desc {
|
||||||
|
color: #000000 !important;
|
||||||
|
}
|
||||||
|
&__checked-icon {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.section {
|
||||||
|
&-raw {
|
||||||
|
.section {
|
||||||
|
&-title {
|
||||||
|
background-image: url('@{indexImages}/raw-checked.png');
|
||||||
|
}
|
||||||
|
&__checked-icon {
|
||||||
|
background-image: url('@{indexImages}/raw-checked-icon.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-episode {
|
||||||
|
.section {
|
||||||
|
&-title {
|
||||||
|
background-image: url('@{indexImages}/episode-checked.png');
|
||||||
|
}
|
||||||
|
&__checked-icon {
|
||||||
|
background-image: url('@{indexImages}/episode-checked-icon.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-galss {
|
||||||
|
backdrop-filter: blur(30px);
|
||||||
|
background: rgba(162, 162, 162, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-form-galss {
|
||||||
|
backdrop-filter: blur(30px);
|
||||||
|
background: rgba(162, 162, 162, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate {
|
||||||
|
width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&__mode {
|
||||||
|
width: 402px;
|
||||||
|
height: 57px;
|
||||||
|
.display-row-left();
|
||||||
|
position: relative;
|
||||||
|
&-item {
|
||||||
|
width: 200px;
|
||||||
|
height: 57px;
|
||||||
|
border-radius: 20px 20px 0 0;
|
||||||
|
.generate-galss();
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
.img-bg();
|
||||||
|
|
||||||
|
&__inner {
|
||||||
|
width: 200px;
|
||||||
|
height: 57px;
|
||||||
|
.center();
|
||||||
|
}
|
||||||
|
|
||||||
|
&__image {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 77px;
|
||||||
|
z-index: 2;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__video {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 240px;
|
||||||
|
z-index: 1;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
.img-bg();
|
||||||
|
margin-right: 13px;
|
||||||
|
}
|
||||||
|
&__text {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
.center();
|
||||||
|
}
|
||||||
|
|
||||||
|
&__image-icon {
|
||||||
|
background-image: url('@{indexImages}/generate-image.png');
|
||||||
|
}
|
||||||
|
&__video-icon {
|
||||||
|
background-image: url('@{indexImages}/generate-video.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
&.choosed {
|
||||||
|
&.generate__mode-item {
|
||||||
|
&__image {
|
||||||
|
background-image: url('@{indexImages}/image-choosed.png');
|
||||||
|
}
|
||||||
|
&__video {
|
||||||
|
background-image: url('@{indexImages}/video-choosed.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.generate__mode-item {
|
||||||
|
&__text {
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
&__image-icon {
|
||||||
|
background-image: url('@{indexImages}/generate-image-choosed.png');
|
||||||
|
}
|
||||||
|
&__video-icon {
|
||||||
|
background-image: url('@{indexImages}/generate-video-choosed.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__form {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 56px;
|
||||||
|
z-index: 4;
|
||||||
|
width: 1200px;
|
||||||
|
min-height: 276px;
|
||||||
|
border-radius: 20px 20px 20px 20px;
|
||||||
|
border: 1px solid #DFBFF2;
|
||||||
|
.generate-form-galss();
|
||||||
|
padding: 24px;
|
||||||
|
&-input {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
height: 163px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
&__upload {
|
||||||
|
width: 126px;
|
||||||
|
height: 163px;
|
||||||
|
border-radius: 8px;
|
||||||
|
.generate-form-galss();
|
||||||
|
}
|
||||||
|
|
||||||
|
&__textarea {
|
||||||
|
width: 858px;
|
||||||
|
height: 163px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
407
src/views/Message.vue
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-container">
|
||||||
|
<h2 class="page-title">💬 消息管理</h2>
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="messages-layout">
|
||||||
|
<div class="messages-sidebar">
|
||||||
|
<div class="message-filters">
|
||||||
|
<button class="filter-btn active">全部消息</button>
|
||||||
|
<button class="filter-btn">未读消息</button>
|
||||||
|
<button class="filter-btn">已读消息</button>
|
||||||
|
<button class="filter-btn">发送消息</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message-threads">
|
||||||
|
<div class="thread-item active">
|
||||||
|
<div class="thread-avatar">A</div>
|
||||||
|
<div class="thread-info">
|
||||||
|
<div class="thread-name">张三</div>
|
||||||
|
<div class="thread-preview">你好,请问有什么可以帮助你的吗?</div>
|
||||||
|
</div>
|
||||||
|
<div class="thread-meta">
|
||||||
|
<div class="thread-time">10:30</div>
|
||||||
|
<div class="thread-badge">3</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="thread-item">
|
||||||
|
<div class="thread-avatar">B</div>
|
||||||
|
<div class="thread-info">
|
||||||
|
<div class="thread-name">李四</div>
|
||||||
|
<div class="thread-preview">关于项目进度的更新</div>
|
||||||
|
</div>
|
||||||
|
<div class="thread-meta">
|
||||||
|
<div class="thread-time">昨天</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="thread-item">
|
||||||
|
<div class="thread-avatar">C</div>
|
||||||
|
<div class="thread-info">
|
||||||
|
<div class="thread-name">王五</div>
|
||||||
|
<div class="thread-preview">谢谢,收到了</div>
|
||||||
|
</div>
|
||||||
|
<div class="thread-meta">
|
||||||
|
<div class="thread-time">2天前</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="messages-main">
|
||||||
|
<div class="message-header">
|
||||||
|
<div class="header-info">
|
||||||
|
<div class="header-name">张三</div>
|
||||||
|
<div class="header-status">在线</div>
|
||||||
|
</div>
|
||||||
|
<div class="header-actions">
|
||||||
|
<button class="btn btn-small">📞</button>
|
||||||
|
<button class="btn btn-small">📷</button>
|
||||||
|
<button class="btn btn-small">⚙️</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="messages-content">
|
||||||
|
<div class="message received">
|
||||||
|
<div class="message-bubble">
|
||||||
|
<div class="message-text">你好,请问有什么可以帮助你的吗?</div>
|
||||||
|
<div class="message-time">10:25</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message sent">
|
||||||
|
<div class="message-bubble">
|
||||||
|
<div class="message-text">你好,我想了解一下产品的详细信息</div>
|
||||||
|
<div class="message-time">10:26</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message received">
|
||||||
|
<div class="message-bubble">
|
||||||
|
<div class="message-text">当然可以,请问你想了解哪方面的信息呢?</div>
|
||||||
|
<div class="message-time">10:27</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message-input-area">
|
||||||
|
<textarea placeholder="输入消息..." rows="3"></textarea>
|
||||||
|
<div class="input-actions">
|
||||||
|
<button class="btn btn-small">📎</button>
|
||||||
|
<button class="btn btn-small">😊</button>
|
||||||
|
<button class="btn btn-primary">发送</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Message'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.page-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages-layout {
|
||||||
|
display: flex;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
height: 600px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages-sidebar {
|
||||||
|
width: 300px;
|
||||||
|
border-right: 1px solid #e4e7ed;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-filters {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 1px solid #e4e7ed;
|
||||||
|
|
||||||
|
.filter-btn {
|
||||||
|
flex: 1;
|
||||||
|
padding: 16px;
|
||||||
|
border: none;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: #409eff;
|
||||||
|
border-bottom-color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-threads {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thread-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
border-bottom: 1px solid #f5f7fa;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.thread-avatar {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #409eff;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thread-info {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thread-name {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thread-preview {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #606266;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thread-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thread-time {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thread-badge {
|
||||||
|
background-color: #f56c6c;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 10px;
|
||||||
|
min-width: 18px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages-main {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid #e4e7ed;
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-status {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #67c23a;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background-color: #67c23a;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-small {
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #606266;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #409eff;
|
||||||
|
border-color: #c6e2ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages-content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
&.sent {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.received {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-bubble {
|
||||||
|
max-width: 60%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.message-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-time {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.sent .message-bubble {
|
||||||
|
background-color: #409eff;
|
||||||
|
color: #fff;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
|
||||||
|
.message-text {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-time {
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.received .message-bubble {
|
||||||
|
background-color: #fff;
|
||||||
|
color: #303133;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-input-area {
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-top: 1px solid #e4e7ed;
|
||||||
|
background-color: #fafafa;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-input-area textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
resize: none;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.3s;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-actions .btn-primary {
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
184
src/views/Notification.vue
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-container">
|
||||||
|
<h2 class="page-title">🔔 通知中心</h2>
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="notifications-list">
|
||||||
|
<div class="notification-item unread">
|
||||||
|
<div class="notification-icon">📢</div>
|
||||||
|
<div class="notification-content">
|
||||||
|
<div class="notification-title">系统公告</div>
|
||||||
|
<div class="notification-text">新功能已上线,快来体验吧!</div>
|
||||||
|
<div class="notification-time">2分钟前</div>
|
||||||
|
</div>
|
||||||
|
<div class="notification-actions">
|
||||||
|
<button class="btn btn-small">查看</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="notification-item">
|
||||||
|
<div class="notification-icon">📦</div>
|
||||||
|
<div class="notification-content">
|
||||||
|
<div class="notification-title">订单通知</div>
|
||||||
|
<div class="notification-text">您的订单 #12345 已发货</div>
|
||||||
|
<div class="notification-time">1小时前</div>
|
||||||
|
</div>
|
||||||
|
<div class="notification-actions">
|
||||||
|
<button class="btn btn-small">查看</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="notification-item">
|
||||||
|
<div class="notification-icon">👥</div>
|
||||||
|
<div class="notification-content">
|
||||||
|
<div class="notification-title">用户操作</div>
|
||||||
|
<div class="notification-text">张三评论了您的文章</div>
|
||||||
|
<div class="notification-time">3小时前</div>
|
||||||
|
</div>
|
||||||
|
<div class="notification-actions">
|
||||||
|
<button class="btn btn-small">查看</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="notification-item">
|
||||||
|
<div class="notification-icon">🔧</div>
|
||||||
|
<div class="notification-content">
|
||||||
|
<div class="notification-title">系统维护</div>
|
||||||
|
<div class="notification-text">系统将于明天凌晨2点进行维护</div>
|
||||||
|
<div class="notification-time">昨天</div>
|
||||||
|
</div>
|
||||||
|
<div class="notification-actions">
|
||||||
|
<button class="btn btn-small">查看</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="notification-stats">
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-number">12</span>
|
||||||
|
<span class="stat-text">未读通知</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-number">89</span>
|
||||||
|
<span class="stat-text">本月通知</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-number">543</span>
|
||||||
|
<span class="stat-text">全部通知</span>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-small">标记全部已读</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Notification'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.page-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notifications-list {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.unread {
|
||||||
|
border-left: 4px solid #409eff;
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-right: 16px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-time {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-actions {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-small {
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-stats {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.stat-number {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #409eff;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
264
src/views/Product.vue
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-container">
|
||||||
|
<h2 class="page-title">📦 产品管理</h2>
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="actions-bar">
|
||||||
|
<button class="btn btn-primary">添加产品</button>
|
||||||
|
<div class="search-box">
|
||||||
|
<input type="text" placeholder="搜索产品..." />
|
||||||
|
<button class="search-btn">🔍</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="products-table-container">
|
||||||
|
<table class="products-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>产品名称</th>
|
||||||
|
<th>价格</th>
|
||||||
|
<th>库存</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>1</td>
|
||||||
|
<td>智能手表</td>
|
||||||
|
<td>¥1,299</td>
|
||||||
|
<td>123</td>
|
||||||
|
<td><span class="status active">在售</span></td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-small">编辑</button>
|
||||||
|
<button class="btn btn-small btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>2</td>
|
||||||
|
<td>无线耳机</td>
|
||||||
|
<td>¥799</td>
|
||||||
|
<td>456</td>
|
||||||
|
<td><span class="status active">在售</span></td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-small">编辑</button>
|
||||||
|
<button class="btn btn-small btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>3</td>
|
||||||
|
<td>平板电脑</td>
|
||||||
|
<td>¥2,499</td>
|
||||||
|
<td>78</td>
|
||||||
|
<td><span class="status active">在售</span></td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-small">编辑</button>
|
||||||
|
<button class="btn btn-small btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>4</td>
|
||||||
|
<td>智能音箱</td>
|
||||||
|
<td>¥599</td>
|
||||||
|
<td>0</td>
|
||||||
|
<td><span class="status inactive">缺货</span></td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-small">编辑</button>
|
||||||
|
<button class="btn btn-small btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>5</td>
|
||||||
|
<td>运动手环</td>
|
||||||
|
<td>¥399</td>
|
||||||
|
<td>234</td>
|
||||||
|
<td><span class="status active">在售</span></td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-small">编辑</button>
|
||||||
|
<button class="btn btn-small btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pagination">
|
||||||
|
<button class="page-btn">上一页</button>
|
||||||
|
<button class="page-btn active">1</button>
|
||||||
|
<button class="page-btn">2</button>
|
||||||
|
<button class="page-btn">3</button>
|
||||||
|
<button class="page-btn">4</button>
|
||||||
|
<button class="page-btn">5</button>
|
||||||
|
<button class="page-btn">下一页</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Product'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.page-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
input {
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
font-size: 14px;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn {
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: none;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #e4e7ed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.products-table-container {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.products-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
|
||||||
|
thead {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
|
||||||
|
th {
|
||||||
|
padding: 16px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
border-bottom: 2px solid #e4e7ed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
border-bottom: 1px solid #e4e7ed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #f0f9eb;
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.inactive {
|
||||||
|
background-color: #fef0f0;
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-small {
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-right: 8px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-danger {
|
||||||
|
background-color: #fef0f0;
|
||||||
|
color: #f56c6c;
|
||||||
|
border-color: #fbc4c4;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #fde2e2;
|
||||||
|
border-color: #fbc4c4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.page-btn {
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin: 0 4px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #606266;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #409eff;
|
||||||
|
border-color: #c6e2ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #409eff;
|
||||||
|
color: #fff;
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
445
src/views/Setting.vue
Normal file
@@ -0,0 +1,445 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-container">
|
||||||
|
<h2 class="page-title">⚙️ 系统设置</h2>
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="settings-grid">
|
||||||
|
<div class="setting-card">
|
||||||
|
<h3>基本设置</h3>
|
||||||
|
<div class="setting-item">
|
||||||
|
<label class="setting-label">网站名称</label>
|
||||||
|
<input type="text" class="setting-input" v-model="inputValue" placeholder="输入网站名称" />
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<label class="setting-label">网站域名</label>
|
||||||
|
<input type="text" class="setting-input" placeholder="输入网站域名" />
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<label class="setting-label">联系邮箱</label>
|
||||||
|
<input type="email" class="setting-input" placeholder="输入联系邮箱" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-card">
|
||||||
|
<h3>安全设置</h3>
|
||||||
|
<div class="setting-item">
|
||||||
|
<label class="setting-label">登录验证</label>
|
||||||
|
<div class="setting-switch">
|
||||||
|
<input type="checkbox" id="login-auth" checked />
|
||||||
|
<label for="login-auth"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<label class="setting-label">密码强度</label>
|
||||||
|
<select class="setting-select">
|
||||||
|
<option>低</option>
|
||||||
|
<option selected>中</option>
|
||||||
|
<option>高</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<label class="setting-label">Session过期时间</label>
|
||||||
|
<input type="number" class="setting-input" placeholder="3600" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-card">
|
||||||
|
<h3>通知设置</h3>
|
||||||
|
<div class="setting-item">
|
||||||
|
<label class="setting-label">邮件通知</label>
|
||||||
|
<div class="setting-switch">
|
||||||
|
<input type="checkbox" id="email-notify" checked />
|
||||||
|
<label for="email-notify"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<label class="setting-label">短信通知</label>
|
||||||
|
<div class="setting-switch">
|
||||||
|
<input type="checkbox" id="sms-notify" />
|
||||||
|
<label for="sms-notify"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<label class="setting-label">推送通知</label>
|
||||||
|
<div class="setting-switch">
|
||||||
|
<input type="checkbox" id="push-notify" checked />
|
||||||
|
<label for="push-notify"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 自定义表单组件展示区域 -->
|
||||||
|
<div class="custom-form-section">
|
||||||
|
<h2 class="section-title">自定义表单组件演示</h2>
|
||||||
|
|
||||||
|
<div class="form-components-grid">
|
||||||
|
<!-- Switch 组件 -->
|
||||||
|
<div class="form-component-item">
|
||||||
|
<h3>Switch 组件</h3>
|
||||||
|
<div class="component-demo">
|
||||||
|
<CustomSwitch
|
||||||
|
v-model="switchValue"
|
||||||
|
label="启用功能"
|
||||||
|
@change="handleSwitchChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 原生 Input 组件 -->
|
||||||
|
<div class="form-component-item">
|
||||||
|
<h3>Input 组件</h3>
|
||||||
|
<div class="component-demo">
|
||||||
|
<input
|
||||||
|
class="custom-input"
|
||||||
|
v-model="inputValue"
|
||||||
|
placeholder="请输入内容"
|
||||||
|
@input="handleInputChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Select 组件 -->
|
||||||
|
<div class="form-component-item">
|
||||||
|
<h3>Select 组件</h3>
|
||||||
|
<div class="component-demo">
|
||||||
|
<CustomSelect
|
||||||
|
v-model="selectValue"
|
||||||
|
:options="selectOptions"
|
||||||
|
placeholder="请选择选项"
|
||||||
|
:loading="selectLoading"
|
||||||
|
@change="handleSelectChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- RadioGroup 组件 -->
|
||||||
|
<div class="form-component-item">
|
||||||
|
<h3>RadioGroup 组件</h3>
|
||||||
|
<div class="component-demo">
|
||||||
|
<CustomRadioGroup
|
||||||
|
v-model="radioValue"
|
||||||
|
:options="radioOptions"
|
||||||
|
label="选择类型"
|
||||||
|
@change="handleRadioChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Textarea 组件 -->
|
||||||
|
<div class="form-component-item full-width">
|
||||||
|
<h3>Textarea 组件</h3>
|
||||||
|
<div class="component-demo">
|
||||||
|
<CustomTextarea
|
||||||
|
v-model="textareaValue"
|
||||||
|
placeholder="请输入详细内容..."
|
||||||
|
:maxlength="100"
|
||||||
|
:showWordLimit="true"
|
||||||
|
@input="handleTextareaChange"
|
||||||
|
>
|
||||||
|
<template #left>
|
||||||
|
<span>左下方slot</span>
|
||||||
|
</template>
|
||||||
|
<template #right>
|
||||||
|
<span>右下方slot</span>
|
||||||
|
</template>
|
||||||
|
</CustomTextarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions-footer">
|
||||||
|
<button class="btn btn-primary">保存设置</button>
|
||||||
|
<button class="btn">重置</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 导入自定义表单组件
|
||||||
|
import CustomSwitch from '../components/form/Switch.vue'
|
||||||
|
import CustomSelect from '../components/form/Select.vue'
|
||||||
|
import CustomRadioGroup from '../components/form/RadioGroup.vue'
|
||||||
|
import CustomTextarea from '../components/form/Textarea.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Setting',
|
||||||
|
components: {
|
||||||
|
CustomSwitch,
|
||||||
|
CustomSelect,
|
||||||
|
CustomRadioGroup,
|
||||||
|
CustomTextarea
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// Switch 组件数据
|
||||||
|
switchValue: true,
|
||||||
|
|
||||||
|
// Input 组件数据
|
||||||
|
inputValue: '',
|
||||||
|
|
||||||
|
// Select 组件数据
|
||||||
|
selectValue: '',
|
||||||
|
selectOptions: [
|
||||||
|
{ value: 'option1', label: '选项一' },
|
||||||
|
{ value: 'option2', label: '选项二' },
|
||||||
|
{ value: 'option3', label: '选项三' },
|
||||||
|
{ value: 'option4', label: '选项四' },
|
||||||
|
{ value: 'option5', label: '选项五' }
|
||||||
|
],
|
||||||
|
selectLoading: false,
|
||||||
|
|
||||||
|
// RadioGroup 组件数据
|
||||||
|
radioValue: 'type1',
|
||||||
|
radioOptions: [
|
||||||
|
{ value: 'type1', label: '类型一' },
|
||||||
|
{ value: 'type2', label: '类型二' },
|
||||||
|
{ value: 'type3', label: '类型三' }
|
||||||
|
],
|
||||||
|
|
||||||
|
// Textarea 组件数据
|
||||||
|
textareaValue: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// Switch 组件事件
|
||||||
|
handleSwitchChange(value) {
|
||||||
|
console.log('Switch 状态改变:', value)
|
||||||
|
},
|
||||||
|
|
||||||
|
// Input 组件事件
|
||||||
|
handleInputChange(e) {
|
||||||
|
console.log('Input 输入:', e.target.value)
|
||||||
|
},
|
||||||
|
|
||||||
|
// Select 组件事件
|
||||||
|
handleSelectChange(value, option) {
|
||||||
|
console.log('Select 选择:', value, option)
|
||||||
|
},
|
||||||
|
|
||||||
|
// RadioGroup 组件事件
|
||||||
|
handleRadioChange(value, option) {
|
||||||
|
console.log('Radio 选择:', value, option)
|
||||||
|
},
|
||||||
|
|
||||||
|
// Textarea 组件事件
|
||||||
|
handleTextareaChange(e) {
|
||||||
|
console.log('Textarea 输入:', e.target.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.page-container {
|
||||||
|
width: 100%;
|
||||||
|
background-color: @bg-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-card {
|
||||||
|
background-color: @bg-secondary;
|
||||||
|
border: 1px solid @border-color;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: @shadow-light;
|
||||||
|
transition: @transition;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: @shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid @border-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-input,
|
||||||
|
.setting-select {
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: @bg-tertiary;
|
||||||
|
color: @text-primary;
|
||||||
|
border: 1px solid @border-color;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
width: 200px;
|
||||||
|
outline: none;
|
||||||
|
transition: @transition;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: @primary-color;
|
||||||
|
background-color: @bg-secondary;
|
||||||
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: @text-placeholder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-select {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: @bg-tertiary;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: @primary-hover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 40px;
|
||||||
|
height: 20px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
|
||||||
|
&:checked + label {
|
||||||
|
background-color: @primary-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:checked + label:before {
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: @border-color;
|
||||||
|
transition: @transition;
|
||||||
|
border-radius: 20px;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
left: 2px;
|
||||||
|
bottom: 2px;
|
||||||
|
background-color: @bg-primary;
|
||||||
|
transition: @transition;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: @border-color-light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 自定义表单组件展示区域样式 */
|
||||||
|
.custom-form-section {
|
||||||
|
margin-top: 32px;
|
||||||
|
padding: 24px;
|
||||||
|
background-color: @bg-secondary;
|
||||||
|
border: 1px solid @border-color;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: @shadow-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-components-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-component-item {
|
||||||
|
background-color: @bg-tertiary;
|
||||||
|
border: 1px solid @border-color;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: @shadow-light;
|
||||||
|
transition: @transition;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: @bg-hover;
|
||||||
|
box-shadow: @shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.full-width {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.component-demo {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
/* 确保组件占满宽度 */
|
||||||
|
.custom-input,
|
||||||
|
:deep(.custom-select),
|
||||||
|
:deep(.custom-textarea),
|
||||||
|
:deep(.custom-radio-group) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Textarea 组件特殊处理 */
|
||||||
|
:deep(.custom-textarea) {
|
||||||
|
min-height: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
218
src/views/User.vue
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-container">
|
||||||
|
<h2 class="page-title">👥 用户管理</h2>
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="users-grid">
|
||||||
|
<div class="user-card">
|
||||||
|
<div class="user-avatar">A</div>
|
||||||
|
<div class="user-info">
|
||||||
|
<div class="user-name">张三</div>
|
||||||
|
<div class="user-email">zhangsan@example.com</div>
|
||||||
|
<div class="user-role">管理员</div>
|
||||||
|
</div>
|
||||||
|
<div class="user-actions">
|
||||||
|
<button class="btn btn-small">查看</button>
|
||||||
|
<button class="btn btn-small btn-danger">禁用</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="user-card">
|
||||||
|
<div class="user-avatar">B</div>
|
||||||
|
<div class="user-info">
|
||||||
|
<div class="user-name">李四</div>
|
||||||
|
<div class="user-email">lisi@example.com</div>
|
||||||
|
<div class="user-role">普通用户</div>
|
||||||
|
</div>
|
||||||
|
<div class="user-actions">
|
||||||
|
<button class="btn btn-small">查看</button>
|
||||||
|
<button class="btn btn-small btn-danger">禁用</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="user-card">
|
||||||
|
<div class="user-avatar">C</div>
|
||||||
|
<div class="user-info">
|
||||||
|
<div class="user-name">王五</div>
|
||||||
|
<div class="user-email">wangwu@example.com</div>
|
||||||
|
<div class="user-role">普通用户</div>
|
||||||
|
</div>
|
||||||
|
<div class="user-actions">
|
||||||
|
<button class="btn btn-small">查看</button>
|
||||||
|
<button class="btn btn-small btn-danger">禁用</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="user-card">
|
||||||
|
<div class="user-avatar">D</div>
|
||||||
|
<div class="user-info">
|
||||||
|
<div class="user-name">赵六</div>
|
||||||
|
<div class="user-email">zhaoliu@example.com</div>
|
||||||
|
<div class="user-role">普通用户</div>
|
||||||
|
</div>
|
||||||
|
<div class="user-actions">
|
||||||
|
<button class="btn btn-small">查看</button>
|
||||||
|
<button class="btn btn-small btn-danger">禁用</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="user-stats">
|
||||||
|
<div class="stat-box">
|
||||||
|
<div class="stat-value">2,345</div>
|
||||||
|
<div class="stat-desc">总用户数</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-box">
|
||||||
|
<div class="stat-value">1,678</div>
|
||||||
|
<div class="stat-desc">活跃用户</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-box">
|
||||||
|
<div class="stat-value">456</div>
|
||||||
|
<div class="stat-desc">新增用户</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-box">
|
||||||
|
<div class="stat-value">211</div>
|
||||||
|
<div class="stat-desc">禁用用户</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'User'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.page-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.users-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
transition: transform 0.3s, box-shadow 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #409eff;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-email {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-role {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-small {
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&.btn-danger {
|
||||||
|
background-color: #fef0f0;
|
||||||
|
color: #f56c6c;
|
||||||
|
border-color: #fbc4c4;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #fde2e2;
|
||||||
|
border-color: #fbc4c4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-stats {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-box {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
text-align: center;
|
||||||
|
transition: transform 0.3s, box-shadow 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #409eff;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-desc {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
366
src/views/index/components/select/modelSelect.vue
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
<template>
|
||||||
|
<div class="model-select" :class="showOptions ? 'show-options': ''">
|
||||||
|
<div class="label" @click="toggleOption">
|
||||||
|
<div class="label-content">
|
||||||
|
<div class="label-content__icon"></div>
|
||||||
|
<div class="label-content__text">齐蜂AI大模型</div>
|
||||||
|
</div>
|
||||||
|
<div class="label-arrow"></div>
|
||||||
|
</div>
|
||||||
|
<div v-show="showOptions" class="options">
|
||||||
|
<div class="options__left">
|
||||||
|
<div
|
||||||
|
v-for="item in leftOptions"
|
||||||
|
class="options__left-item"
|
||||||
|
:class="leftValue === item.value ? 'left-choosed' : ''"
|
||||||
|
v-bind:key="item.value"
|
||||||
|
@click="handleLeftOptionClick(item)"
|
||||||
|
>
|
||||||
|
<div class="options__left-item__icon" :class="`options__left-item__icon__${item.value}`"></div>
|
||||||
|
<div class="options__left-item__text">
|
||||||
|
{{ item.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="options__right">
|
||||||
|
<div
|
||||||
|
class="options__right-item"
|
||||||
|
v-for="item in rightOptons"
|
||||||
|
:class="rightValue === item.value ? 'right-choosed' : ''"
|
||||||
|
v-bind:key="item.value"
|
||||||
|
@click="handleRightOptionClick(item)"
|
||||||
|
>
|
||||||
|
<div :style="`background-image: url('${item.icon}')`" class="options__right-item__icon"></div>
|
||||||
|
<div class="options__right-item__line options__right-item__line-1">
|
||||||
|
<div class="options__right-item__title">{{ item.label }}</div>
|
||||||
|
<div v-for="tagItem in item.tags" class="options__right-item__tag" v-bind:key="tagItem">
|
||||||
|
{{ tagItem }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="options__right-item__line options__right-item__line-2">
|
||||||
|
<div class="options__right-item__desc">{{ item.desc }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="options__right-item__line options__right-item__line-3">
|
||||||
|
<div class="options__right-item__cost-icon"></div>
|
||||||
|
<div class="options__right-item__cost-text">{{ item.cost }}</div>
|
||||||
|
<div class="options__right-item__split"></div>
|
||||||
|
<div class="options__right-item__time-icon"></div>
|
||||||
|
<div class="options__right-item__time-text">{{ item.time }}s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'modelSelect',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showOptions: false,
|
||||||
|
leftValue: 'text',
|
||||||
|
leftOptions: [
|
||||||
|
{
|
||||||
|
value: 'text',
|
||||||
|
label: '文生图',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'image',
|
||||||
|
label: '图生图',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
rightValue: 'qifeng1',
|
||||||
|
rightOptons: [
|
||||||
|
{
|
||||||
|
value: 'qifeng1',
|
||||||
|
icon: 'https://www.fofba.com/wp-content/uploads/2025/07/1752564018-u41568515953102493196fm253fmtautoapp138fJPEG.webp',
|
||||||
|
label: '齐蜂AI大模型1',
|
||||||
|
tags: ['文生图', '图生图'],
|
||||||
|
desc: '齐蜂团队最新的AI模型25.12融合加速模型',
|
||||||
|
cost: 4,
|
||||||
|
time: 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'qifeng2',
|
||||||
|
icon: 'https://www.fofba.com/wp-content/uploads/2025/07/1752564018-u41568515953102493196fm253fmtautoapp138fJPEG.webp',
|
||||||
|
label: '齐蜂AI大模型2',
|
||||||
|
tags: ['文生图', '图生图'],
|
||||||
|
desc: '齐蜂团队最新的AI模型25.12融合加速模型',
|
||||||
|
cost: 4,
|
||||||
|
time: 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'qifeng3',
|
||||||
|
icon: 'https://www.fofba.com/wp-content/uploads/2025/07/1752564018-u41568515953102493196fm253fmtautoapp138fJPEG.webp',
|
||||||
|
label: '齐蜂AI大模型3',
|
||||||
|
tags: ['文生图', '图生图'],
|
||||||
|
desc: '齐蜂团队最新的AI模型25.12融合加速模型',
|
||||||
|
cost: 4,
|
||||||
|
time: 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'qifeng4',
|
||||||
|
icon: 'https://www.fofba.com/wp-content/uploads/2025/07/1752564018-u41568515953102493196fm253fmtautoapp138fJPEG.webp',
|
||||||
|
label: '齐蜂AI大模型4',
|
||||||
|
tags: ['文生图', '图生图'],
|
||||||
|
desc: '齐蜂团队最新的AI模型25.12融合加速模型',
|
||||||
|
cost: 4,
|
||||||
|
time: 12
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleOption() {
|
||||||
|
this.showOptions = !this.showOptions
|
||||||
|
},
|
||||||
|
handleLeftOptionClick (item) {
|
||||||
|
const { value } = item || {};
|
||||||
|
if (value) {
|
||||||
|
this.leftValue = value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleRightOptionClick (item) {
|
||||||
|
const { value } = item || {};
|
||||||
|
if (value) {
|
||||||
|
this.rightValue = value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.glass {
|
||||||
|
backdrop-filter: blur(30px);
|
||||||
|
background: rgba(162, 162, 162, 0.2);
|
||||||
|
}
|
||||||
|
.model-select {
|
||||||
|
width: 196px;
|
||||||
|
height: 46px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
.label {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 20px 10px 18px;
|
||||||
|
.glass();
|
||||||
|
width: 196px;
|
||||||
|
height: 46px;
|
||||||
|
border-radius: 23px;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
&-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
&__icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
margin-right: 8px;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
&__text {
|
||||||
|
font-family: Source Han Sans CN, Source Han Sans CN;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-arrow {
|
||||||
|
width: 19px;
|
||||||
|
height: 19px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
transform: rotate(0deg);
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.show-options {
|
||||||
|
.label {
|
||||||
|
background: #FFFFFF;
|
||||||
|
.label-content {
|
||||||
|
&__icon {
|
||||||
|
background: #000000;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
&__text {
|
||||||
|
color: #000000;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.label-arrow {
|
||||||
|
background: #000000;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.options {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 56px;
|
||||||
|
width: 1236px;
|
||||||
|
height: 434px;
|
||||||
|
background: #313131;
|
||||||
|
box-shadow: 0px 0px 6px 1px rgba(0,0,0,0.5);
|
||||||
|
border-radius: 12px 12px 12px 12px;
|
||||||
|
border: 1px solid #4E4E4E;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
padding: 0 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
&__left {
|
||||||
|
width: 106px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 30px;
|
||||||
|
padding: 20px 0;
|
||||||
|
&-item {
|
||||||
|
width: 106px;
|
||||||
|
height: 72px;
|
||||||
|
background: #5A5A5A;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
&__icon {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
.img-bg();
|
||||||
|
margin-bottom: 6px;
|
||||||
|
&__text {
|
||||||
|
background-image: url('../../../../assets/imgs/index/options-image.png');
|
||||||
|
}
|
||||||
|
&__image {
|
||||||
|
background-image: url('../../../../assets/imgs/index/options-image.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__text {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
&.left-choosed {
|
||||||
|
opacity: 1;
|
||||||
|
background: #8E67A7;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__right {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
max-height: 434px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding: 20px 0;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
width: 340px;
|
||||||
|
background: #5A5A5A;
|
||||||
|
border-radius: 8px 8px 8px 8px;
|
||||||
|
position: relative;
|
||||||
|
padding: 15px 0 10px 44px;
|
||||||
|
margin: 0 10px 10px 0;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
&__icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
.img-bg();
|
||||||
|
position: absolute;
|
||||||
|
left: 11px;
|
||||||
|
top: 13px;
|
||||||
|
}
|
||||||
|
&__line {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
&-1 {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
&-2 {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
&__tag {
|
||||||
|
width: 49px;
|
||||||
|
height: 18px;
|
||||||
|
background: #747474;
|
||||||
|
color: #404040;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 18px;
|
||||||
|
margin-right: 6px;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
&__desc {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #8A8A8A;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
&__cost-icon , &__time-icon{
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
&__cost-icon{
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
&__time-icon{
|
||||||
|
margin-right: 2px;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
&__cost-text {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #8A8A8A;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
&__split {
|
||||||
|
width: 1px;
|
||||||
|
height: 14px;
|
||||||
|
background: #8A8A8A;
|
||||||
|
}
|
||||||
|
&__time-text {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #8A8A8A;
|
||||||
|
}
|
||||||
|
&.right-choosed {
|
||||||
|
background: #8E67A7;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
.options__right-item {
|
||||||
|
&__desc {
|
||||||
|
color: #B695CE;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
&__tag {
|
||||||
|
background: #FFFFFF;
|
||||||
|
color: #8E67A7;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
14
vite.config.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
css: {
|
||||||
|
preprocessorOptions: {
|
||||||
|
less: {
|
||||||
|
javascriptEnabled: true,
|
||||||
|
additionalData: '@import "./src/styles/theme.less";'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||