Compare commits
29 Commits
main
...
huangsh_br
| Author | SHA1 | Date |
|---|---|---|
|
|
43606403cb | 6 months ago |
|
|
ff15bbb610 | 6 months ago |
|
|
7783d33286 | 6 months ago |
|
|
dac7f96e49 | 6 months ago |
|
|
ecc65315ef | 6 months ago |
|
|
94f8230b07 | 6 months ago |
|
|
1038ca7436 | 6 months ago |
|
|
0bb2ace802 | 6 months ago |
|
|
07ffaa2213 | 6 months ago |
|
|
d3e89bf3cc | 6 months ago |
|
|
d2463201dc | 6 months ago |
|
|
843f5994de | 6 months ago |
|
|
8612b5fcfb | 6 months ago |
|
|
1595226bf9 | 6 months ago |
|
|
3f054c2024 | 6 months ago |
|
|
4a64f100de | 6 months ago |
|
|
a507f60281 | 6 months ago |
|
|
10d1d02321 | 6 months ago |
|
|
60838797b4 | 6 months ago |
|
|
f81bb91be4 | 6 months ago |
|
|
82666c814b | 6 months ago |
|
|
6615d29c14 | 6 months ago |
|
|
76081494ea | 6 months ago |
|
|
031d002033 | 6 months ago |
|
|
382ab4e9ff | 6 months ago |
|
|
9f6d85d2ab | 6 months ago |
|
|
191a1ef20d | 6 months ago |
|
|
1d3c3cb4be | 6 months ago |
|
|
81f6e6a9d1 | 6 months ago |
@ -1,27 +1,22 @@
|
||||
{
|
||||
"miniprogramRoot": "src/wx_app/",
|
||||
"libVersion": "3.11.2",
|
||||
"projectname": "GlucoWise",
|
||||
"setting": {
|
||||
"es6": true,
|
||||
"postcss": true,
|
||||
"minified": true,
|
||||
"uglifyFileName": false,
|
||||
"enhance": true,
|
||||
"packNpmRelationList": [],
|
||||
"babelSetting": {
|
||||
"ignore": [],
|
||||
"disablePlugins": [],
|
||||
"outputPath": ""
|
||||
},
|
||||
"useCompilerPlugins": false,
|
||||
"minifyWXML": true
|
||||
},
|
||||
"compileType": "miniprogram",
|
||||
"simulatorPluginLibVersion": {},
|
||||
"packOptions": {
|
||||
"ignore": [],
|
||||
"include": []
|
||||
},
|
||||
"appid": "wxce6ab13cde21f6da",
|
||||
"miniprogramRoot": "src/wx_app/",
|
||||
"editorSetting": {}
|
||||
"urlCheck": true,
|
||||
"coverView": false,
|
||||
"lazyloadPlaceholderEnable": false,
|
||||
"skylineRenderEnable": false,
|
||||
"preloadBackgroundData": false,
|
||||
"autoAudits": false,
|
||||
"useApiHook": true,
|
||||
"useApiHostProcess": true,
|
||||
"showShadowRootInWxmlPanel": false,
|
||||
"useStaticServer": false,
|
||||
"useLanDebug": false,
|
||||
"showES6CompileOption": false,
|
||||
"compileHotReLoad": true,
|
||||
"checkInvalidKey": true,
|
||||
"ignoreDevUnusedFiles": true,
|
||||
"bigPackageSizeSupport": false
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
# 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?
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
# Vue 3 + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).
|
||||
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<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>frontend</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"serve": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.24",
|
||||
"vue-router": "^4.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"vite": "^7.2.2"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,7 @@
|
||||
<script setup>
|
||||
import { RouterView } from 'vue-router'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouterView />
|
||||
</template>
|
||||
@ -0,0 +1,86 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
--vt-c-black: #181818;
|
||||
--vt-c-black-soft: #222222;
|
||||
--vt-c-black-mute: #282828;
|
||||
|
||||
--vt-c-indigo: #2c3e50;
|
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
:root {
|
||||
--color-background: var(--vt-c-white);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
--section-gap: 160px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition:
|
||||
color 0.5s,
|
||||
background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family:
|
||||
Inter,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Fira Sans',
|
||||
'Droid Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-size: 15px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
After Width: | Height: | Size: 276 B |
@ -0,0 +1,35 @@
|
||||
@import './base.css';
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
a,
|
||||
.green {
|
||||
text-decoration: none;
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
transition: 0.4s;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
a:hover {
|
||||
background-color: hsla(160, 100%, 37%, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
body {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 496 B |
@ -0,0 +1,101 @@
|
||||
<script setup>
|
||||
import SidebarNav from './SidebarNav.vue'
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const profile = {
|
||||
name: '张三',
|
||||
avatar:
|
||||
'https://images.unsplash.com/photo-1504595403659-9088ce801e29?auto=format&fit=crop&w=200&q=80',
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<SidebarNav />
|
||||
<div class="content">
|
||||
<header class="topbar">
|
||||
<div class="page-title">{{ props.title }}</div>
|
||||
<div class="user">
|
||||
<img class="avatar" :src="profile.avatar" alt="avatar" />
|
||||
<span class="name">{{ profile.name }}</span>
|
||||
</div>
|
||||
</header>
|
||||
<main class="main-area">
|
||||
<slot />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
min-height: 100vh;
|
||||
background: #f2f6fb;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.topbar {
|
||||
height: 62px;
|
||||
padding: 0 24px;
|
||||
background: #428cf9;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 6px 16px rgba(66, 140, 249, 0.2);
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 2px solid rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.name {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.main-area {
|
||||
padding: 20px 24px 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.page {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,74 @@
|
||||
<script setup>
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
const menuItems = [
|
||||
{ icon: '🧭', label: '个人主页', path: '/home' },
|
||||
{ icon: '🖼️', label: '首页轮播图管理', path: '/banners' },
|
||||
{ icon: '💬', label: '糖友圈发布管理', path: '/circle/publish' },
|
||||
{ icon: '🚨', label: '糖友圈举报管理', path: '/circle/reports' },
|
||||
{ icon: '⛔', label: '糖友圈恶意用户', path: '/circle/malicious' },
|
||||
{ icon: '🤖', label: '糖友圈智能审核设置', path: '/circle/auto-review' },
|
||||
{ icon: '📚', label: '知识百科管理', path: '/knowledge' },
|
||||
{ icon: '👥', label: '客户管理', path: '/clients' },
|
||||
{ icon: '⚙️', label: '系统管理', path: '/system' },
|
||||
{ icon: '🔧', label: '设备管理', path: '/devices' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<aside class="sidebar">
|
||||
<div class="brand">智慧糖管理系统</div>
|
||||
<nav class="menu">
|
||||
<RouterLink v-for="item in menuItems" :key="item.label" class="menu-item" :to="item.path">
|
||||
<span class="icon">{{ item.icon }}</span>
|
||||
<span>{{ item.label }}</span>
|
||||
</RouterLink>
|
||||
</nav>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.sidebar {
|
||||
width: 220px;
|
||||
background: linear-gradient(180deg, #3380ff 0%, #2c63f4 100%);
|
||||
color: #fff;
|
||||
padding: 22px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.brand {
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
padding: 0 24px 20px 24px;
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 14px;
|
||||
border-radius: 10px;
|
||||
color: #dfe9ff;
|
||||
transition: background 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.menu-item:hover,
|
||||
.menu-item.router-link-active {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 20px;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,6 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import './style.css'
|
||||
|
||||
createApp(App).use(router).mount('#app')
|
||||
@ -0,0 +1,35 @@
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import LoginView from '../views/LoginView.vue'
|
||||
import DashboardView from '../views/DashboardView.vue'
|
||||
import BannerView from '../views/BannerView.vue'
|
||||
import KnowledgeView from '../views/KnowledgeView.vue'
|
||||
import ClientsView from '../views/ClientsView.vue'
|
||||
import SystemView from '../views/SystemView.vue'
|
||||
import DevicesView from '../views/DevicesView.vue'
|
||||
import CirclePublishView from '../views/CirclePublishView.vue'
|
||||
import CircleReportView from '../views/CircleReportView.vue'
|
||||
import CircleMaliciousView from '../views/CircleMaliciousView.vue'
|
||||
import CircleAutoReviewView from '../views/CircleAutoReviewView.vue'
|
||||
|
||||
const routes = [
|
||||
{ path: '/', redirect: '/login' },
|
||||
{ path: '/login', name: 'login', component: LoginView },
|
||||
{ path: '/home', name: 'home', component: DashboardView },
|
||||
{ path: '/banners', name: 'banners', component: BannerView },
|
||||
{ path: '/knowledge', name: 'knowledge', component: KnowledgeView },
|
||||
{ path: '/clients', name: 'clients', component: ClientsView },
|
||||
{ path: '/system', name: 'system', component: SystemView },
|
||||
{ path: '/devices', name: 'devices', component: DevicesView },
|
||||
{ path: '/circle/publish', name: 'circlePublish', component: CirclePublishView },
|
||||
{ path: '/circle/reports', name: 'circleReports', component: CircleReportView },
|
||||
{ path: '/circle/malicious', name: 'circleMalicious', component: CircleMaliciousView },
|
||||
{ path: '/circle/auto-review', name: 'circleAutoReview', component: CircleAutoReviewView },
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes,
|
||||
scrollBehavior: () => ({ top: 0 }),
|
||||
})
|
||||
|
||||
export default router
|
||||
@ -0,0 +1,954 @@
|
||||
:root {
|
||||
/* 字体系统 */
|
||||
font-family: 'Inter', 'PingFang SC', 'Microsoft YaHei', system-ui, -apple-system, sans-serif;
|
||||
line-height: 1.6;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* 色彩系统 - 完整的调色板 */
|
||||
--color-primary-50: #eff6ff;
|
||||
--color-primary-100: #dbeafe;
|
||||
--color-primary-200: #bfdbfe;
|
||||
--color-primary-300: #93c5fd;
|
||||
--color-primary-400: #60a5fa;
|
||||
--color-primary-500: #3b82f6;
|
||||
--color-primary-600: #2563eb;
|
||||
--color-primary-700: #1d4ed8;
|
||||
--color-primary-800: #1e40af;
|
||||
--color-primary-900: #1e3a8a;
|
||||
|
||||
--color-success-50: #ecfdf5;
|
||||
--color-success-100: #d1fae5;
|
||||
--color-success-500: #10b981;
|
||||
--color-success-600: #059669;
|
||||
--color-success-700: #047857;
|
||||
|
||||
--color-warning-50: #fffbeb;
|
||||
--color-warning-100: #fef3c7;
|
||||
--color-warning-500: #f59e0b;
|
||||
--color-warning-600: #d97706;
|
||||
--color-warning-700: #b45309;
|
||||
|
||||
--color-error-50: #fef2f2;
|
||||
--color-error-100: #fee2e2;
|
||||
--color-error-500: #ef4444;
|
||||
--color-error-600: #dc2626;
|
||||
--color-error-700: #b91c1c;
|
||||
|
||||
--color-gray-50: #f8fafc;
|
||||
--color-gray-100: #f1f5f9;
|
||||
--color-gray-200: #e2e8f0;
|
||||
--color-gray-300: #cbd5e1;
|
||||
--color-gray-400: #94a3b8;
|
||||
--color-gray-500: #64748b;
|
||||
--color-gray-600: #475569;
|
||||
--color-gray-700: #334155;
|
||||
--color-gray-800: #1e293b;
|
||||
--color-gray-900: #0f172a;
|
||||
|
||||
/* 字体大小系统 */
|
||||
--text-xs: 0.75rem; /* 12px */
|
||||
--text-sm: 0.875rem; /* 14px */
|
||||
--text-base: 1rem; /* 16px */
|
||||
--text-lg: 1.125rem; /* 18px */
|
||||
--text-xl: 1.25rem; /* 20px */
|
||||
--text-2xl: 1.5rem; /* 24px */
|
||||
--text-3xl: 1.875rem; /* 30px */
|
||||
|
||||
/* 行高系统 */
|
||||
--line-height-tight: 1.25;
|
||||
--line-height-normal: 1.5;
|
||||
--line-height-relaxed: 1.75;
|
||||
|
||||
/* 间距系统 */
|
||||
--space-1: 0.25rem; /* 4px */
|
||||
--space-2: 0.5rem; /* 8px */
|
||||
--space-3: 0.75rem; /* 12px */
|
||||
--space-4: 1rem; /* 16px */
|
||||
--space-5: 1.25rem; /* 20px */
|
||||
--space-6: 1.5rem; /* 24px */
|
||||
--space-8: 2rem; /* 32px */
|
||||
--space-10: 2.5rem; /* 40px */
|
||||
|
||||
/* 阴影系统 */
|
||||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
|
||||
/* 圆角系统 */
|
||||
--radius-sm: 0.25rem; /* 4px */
|
||||
--radius: 0.375rem; /* 6px */
|
||||
--radius-md: 0.5rem; /* 8px */
|
||||
--radius-lg: 0.75rem; /* 12px */
|
||||
--radius-xl: 1rem; /* 16px */
|
||||
--radius-2xl: 1.5rem; /* 24px */
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background: var(--color-gray-50);
|
||||
color: var(--color-gray-800);
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--line-height-normal);
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
button {
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* 固定侧边菜单栏 */
|
||||
.layout-sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: 260px;
|
||||
background: #fff;
|
||||
box-shadow: var(--shadow-lg);
|
||||
z-index: 1000;
|
||||
overflow-y: auto;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.layout-main {
|
||||
margin-left: 260px;
|
||||
min-height: 100vh;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
/* 移动端侧边栏隐藏 */
|
||||
@media (max-width: 1024px) {
|
||||
.layout-sidebar {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.layout-sidebar.open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.layout-main {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏滚动条样式 */
|
||||
.layout-sidebar::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.layout-sidebar::-webkit-scrollbar-track {
|
||||
background: var(--color-gray-100);
|
||||
}
|
||||
|
||||
.layout-sidebar::-webkit-scrollbar-thumb {
|
||||
background: var(--color-gray-400);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
.layout-sidebar::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--color-gray-500);
|
||||
}
|
||||
|
||||
/* 卡片样式 - 增强版 */
|
||||
.app-card {
|
||||
background: #fff;
|
||||
border-radius: var(--radius-xl);
|
||||
box-shadow: var(--shadow);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.app-card:hover {
|
||||
box-shadow: var(--shadow-lg);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 面板头部 - 增强版 */
|
||||
.panel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: var(--space-6) var(--space-6) var(--space-4);
|
||||
border-bottom: 1px solid var(--color-gray-100);
|
||||
background: linear-gradient(to bottom, #fff, var(--color-gray-50));
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: var(--text-xl);
|
||||
font-weight: 700;
|
||||
color: var(--color-gray-900);
|
||||
letter-spacing: -0.025em;
|
||||
line-height: var(--line-height-tight);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 统一统计卡片字体大小 */
|
||||
.cards-grid .stat-card .stat-value {
|
||||
font-size: 2.25rem !important;
|
||||
font-weight: 800;
|
||||
color: var(--color-gray-900);
|
||||
line-height: 1;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
.cards-grid .stat-card .muted {
|
||||
color: var(--color-gray-500);
|
||||
font-size: var(--text-sm) !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.cards-grid .stat-card .stat-trend {
|
||||
font-size: var(--text-xs) !important;
|
||||
font-weight: 600;
|
||||
margin-top: var(--space-1);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
/* 分段控制器 - 增强版 */
|
||||
.segmented {
|
||||
display: inline-flex;
|
||||
background: var(--color-gray-100);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-1);
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.segmented button {
|
||||
border: none;
|
||||
padding: var(--space-2) var(--space-4);
|
||||
border-radius: var(--radius-md);
|
||||
background: transparent;
|
||||
color: var(--color-gray-600);
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
font-size: var(--text-sm);
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.segmented .active {
|
||||
background: #fff;
|
||||
color: var(--color-primary-600);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
/* 分页 - 增强版 */
|
||||
.pagination {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--space-6) 0;
|
||||
}
|
||||
|
||||
.pagination button {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
border: 1px solid var(--color-gray-300);
|
||||
border-radius: var(--radius-md);
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
color: var(--color-gray-600);
|
||||
font-weight: 600;
|
||||
font-size: var(--text-sm);
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pagination .active {
|
||||
background: var(--color-primary-600);
|
||||
color: #fff;
|
||||
border-color: var(--color-primary-600);
|
||||
}
|
||||
|
||||
.pagination button:hover:not(.active) {
|
||||
border-color: var(--color-primary-500);
|
||||
color: var(--color-primary-600);
|
||||
}
|
||||
|
||||
/* 搜索行 */
|
||||
.search-row {
|
||||
display: flex;
|
||||
gap: var(--space-3);
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* 选择框 - 增强版 */
|
||||
.select {
|
||||
border: 1px solid var(--color-gray-300);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
background: #fff;
|
||||
min-width: 8rem;
|
||||
font-size: var(--text-sm);
|
||||
color: var(--color-gray-700);
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%2364748b' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e");
|
||||
background-position: right 0.5rem center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1.5em 1.5em;
|
||||
padding-right: 2.5rem;
|
||||
}
|
||||
|
||||
.select:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary-500);
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
/* 表格样式 - 增强版 */
|
||||
.table-container {
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--color-gray-200);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: #fff;
|
||||
font-size: var(--text-sm);
|
||||
line-height: var(--line-height-relaxed);
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
padding: var(--space-4) var(--space-4);
|
||||
border-bottom: 1px solid var(--color-gray-100);
|
||||
text-align: left;
|
||||
color: var(--color-gray-700);
|
||||
}
|
||||
|
||||
.table th {
|
||||
color: var(--color-gray-600);
|
||||
font-weight: 600;
|
||||
background: var(--color-gray-50);
|
||||
font-size: var(--text-xs);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
border-bottom: 1px solid var(--color-gray-200);
|
||||
}
|
||||
|
||||
.table tbody tr {
|
||||
transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background-color: var(--color-primary-50);
|
||||
}
|
||||
|
||||
.table tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* 标签样式 - 增强版 */
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: var(--space-1) var(--space-3);
|
||||
border-radius: var(--radius-xl);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
line-height: 1;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.tag.blue {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
color: var(--color-primary-700);
|
||||
border-color: rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
.tag.green {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
color: var(--color-success-700);
|
||||
border-color: rgba(16, 185, 129, 0.2);
|
||||
}
|
||||
|
||||
.tag.orange {
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
color: var(--color-warning-700);
|
||||
border-color: rgba(245, 158, 11, 0.2);
|
||||
}
|
||||
|
||||
.tag.red {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
color: var(--color-error-700);
|
||||
border-color: rgba(239, 68, 68, 0.2);
|
||||
}
|
||||
|
||||
/* 工具栏 - 增强版 */
|
||||
.toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
flex-wrap: wrap;
|
||||
padding: var(--space-5);
|
||||
background: var(--color-gray-50);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
margin: var(--space-5);
|
||||
}
|
||||
|
||||
/* 输入框 - 增强版 */
|
||||
.input {
|
||||
border: 1px solid var(--color-gray-300);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
min-width: 12rem;
|
||||
font-size: var(--text-sm);
|
||||
background: #fff;
|
||||
color: var(--color-gray-700);
|
||||
transition: all 0.2s ease;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary-500);
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.input::placeholder {
|
||||
color: var(--color-gray-400);
|
||||
}
|
||||
|
||||
/* 按钮样式 - 增强版 */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--space-2);
|
||||
border: none;
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-3) var(--space-5);
|
||||
font-weight: 600;
|
||||
font-size: var(--text-sm);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
line-height: 1;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
|
||||
transition: left 0.5s;
|
||||
}
|
||||
|
||||
.btn:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.btn.primary {
|
||||
background: linear-gradient(135deg, var(--color-primary-500) 0%, var(--color-primary-600) 100%);
|
||||
color: #fff;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.btn.primary:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.btn.primary:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.btn.secondary {
|
||||
background: #fff;
|
||||
color: var(--color-gray-700);
|
||||
border: 1px solid var(--color-gray-300);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.btn.secondary:hover {
|
||||
background: var(--color-gray-50);
|
||||
border-color: var(--color-gray-400);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn.small {
|
||||
padding: var(--space-2) var(--space-3);
|
||||
font-size: var(--text-xs);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
/* 统计卡片网格 - 增强版 */
|
||||
.cards-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
|
||||
gap: var(--space-5);
|
||||
margin: var(--space-5);
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
height: 150px;
|
||||
padding: var(--space-5);
|
||||
border-radius: var(--radius-xl);
|
||||
background: #fff;
|
||||
box-shadow: var(--shadow);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stat-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
background: var(--color-primary-500);
|
||||
}
|
||||
|
||||
.stat-card.warning::before {
|
||||
background: var(--color-warning-500);
|
||||
}
|
||||
|
||||
.stat-card.danger::before {
|
||||
background: var(--color-error-500);
|
||||
}
|
||||
|
||||
.stat-card.success::before {
|
||||
background: var(--color-success-500);
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.stat-trend.positive {
|
||||
color: var(--color-success-600);
|
||||
}
|
||||
|
||||
.stat-trend.negative {
|
||||
color: var(--color-error-600);
|
||||
}
|
||||
|
||||
.stat-trend.warning {
|
||||
color: var(--color-warning-600);
|
||||
}
|
||||
|
||||
/* 链接按钮 - 增强版 */
|
||||
.link-btn {
|
||||
color: var(--color-primary-600);
|
||||
font-weight: 600;
|
||||
font-size: var(--text-sm);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
border-radius: var(--radius);
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.link-btn:hover {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
color: var(--color-primary-700);
|
||||
}
|
||||
|
||||
/* 表格底部 */
|
||||
.table-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: var(--space-5);
|
||||
border-top: 1px solid var(--color-gray-200);
|
||||
background: var(--color-gray-50);
|
||||
}
|
||||
|
||||
.pagination-info {
|
||||
color: var(--color-gray-500);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.loading::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
transform: translateX(-100%);
|
||||
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
|
||||
animation: shimmer 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
100% { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
/* 响应式设计 - 增强版 */
|
||||
@media (max-width: 768px) {
|
||||
.toolbar {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: var(--space-3);
|
||||
margin: var(--space-4);
|
||||
}
|
||||
|
||||
.input, .select {
|
||||
min-width: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.cards-grid {
|
||||
grid-template-columns: 1fr;
|
||||
margin: var(--space-4);
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.table {
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
padding: var(--space-3) var(--space-3);
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
align-items: stretch;
|
||||
padding: var(--space-4);
|
||||
}
|
||||
|
||||
.table-footer {
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
/* 移动端卡片式表格 */
|
||||
@media (max-width: 640px) {
|
||||
.table-container {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.table-mobile-card {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.table-mobile-card tr {
|
||||
display: block;
|
||||
margin-bottom: var(--space-4);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-lg);
|
||||
background: #fff;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.table-mobile-card td {
|
||||
display: block;
|
||||
border: none;
|
||||
padding: var(--space-3);
|
||||
border-bottom: 1px solid var(--color-gray-100);
|
||||
}
|
||||
|
||||
.table-mobile-card td:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.table-mobile-card td::before {
|
||||
content: attr(data-label);
|
||||
font-weight: 600;
|
||||
color: var(--color-gray-600);
|
||||
display: block;
|
||||
font-size: var(--text-xs);
|
||||
margin-bottom: var(--space-1);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
}
|
||||
|
||||
/* 可访问性改进 */
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.btn:focus,
|
||||
.input:focus,
|
||||
.select:focus {
|
||||
outline: 2px solid var(--color-primary-300);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--color-gray-100);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--color-gray-400);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--color-gray-500);
|
||||
}
|
||||
/* 在现有的样式基础上添加以下响应式代码 */
|
||||
|
||||
/* 超大屏幕 */
|
||||
@media (min-width: 1536px) {
|
||||
.container {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* 大屏幕 */
|
||||
@media (max-width: 1280px) {
|
||||
.cards-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* 中等屏幕 */
|
||||
@media (max-width: 1024px) {
|
||||
.cards-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.toolbar > * {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-group {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
}
|
||||
|
||||
/* 平板 */
|
||||
@media (max-width: 768px) {
|
||||
.cards-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
padding: var(--space-3);
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: var(--space-2) var(--space-3);
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
|
||||
.input,
|
||||
.select {
|
||||
padding: var(--space-2) var(--space-3);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机 */
|
||||
@media (max-width: 640px) {
|
||||
.app-card {
|
||||
margin: var(--space-2);
|
||||
padding: var(--space-3);
|
||||
}
|
||||
|
||||
.table-footer {
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.action-buttons .btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* 小手机 */
|
||||
@media (max-width: 480px) {
|
||||
.app-card {
|
||||
margin: var(--space-1);
|
||||
padding: var(--space-2);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
padding: var(--space-3);
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
padding: var(--space-3);
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
padding: var(--space-2);
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
}
|
||||
|
||||
/* 移动端表格卡片式布局 */
|
||||
@media (max-width: 768px) {
|
||||
.table-mobile {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.table-mobile thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.table-mobile tbody,
|
||||
.table-mobile tr,
|
||||
.table-mobile td {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-mobile tr {
|
||||
margin-bottom: var(--space-4);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-lg);
|
||||
background: #fff;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.table-mobile td {
|
||||
padding: var(--space-3);
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--color-gray-100);
|
||||
position: relative;
|
||||
padding-left: 40%;
|
||||
}
|
||||
|
||||
.table-mobile td:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.table-mobile td::before {
|
||||
content: attr(data-label);
|
||||
position: absolute;
|
||||
left: var(--space-3);
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-weight: 600;
|
||||
color: var(--color-gray-600);
|
||||
font-size: var(--text-xs);
|
||||
width: 35%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 防止水平滚动 */
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 确保图片响应式 */
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* 响应式文本 */
|
||||
@media (max-width: 768px) {
|
||||
.panel-title {
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
|
||||
.muted {
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
})
|
||||
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 692 B |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 45 KiB |
@ -0,0 +1,3 @@
|
||||
{
|
||||
"usingComponents": {}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
<!-- pages/family/invite/invite.wxml -->
|
||||
<view class="page">
|
||||
<!-- 搜索栏(仿微信样式) -->
|
||||
<view class="wx-search-bar">
|
||||
<view class="wx-search-input-box">
|
||||
<image class="wx-search-icon" src="/images/icons/search.png"></image>
|
||||
<input
|
||||
class="wx-search-input"
|
||||
placeholder="输入昵称或ID号搜索"
|
||||
placeholder-class="wx-search-placeholder"
|
||||
bindinput="onInput"
|
||||
confirm-type="search"
|
||||
bindconfirm="onSearch"
|
||||
value="{{keyword}}"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="wx-search-btn" bindtap="onSearch">搜索</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view wx:if="{{searching && results.length === 0}}" class="empty">
|
||||
未找到匹配用户
|
||||
</view>
|
||||
|
||||
<view wx:for="{{results}}" wx:key="id" class="result-item" bindtap="onSelectUser" data-user="{{item}}">
|
||||
<image class="avatar" src="{{item.avatar}}" mode="aspectFill" />
|
||||
<view class="info">
|
||||
<text class="nickname">{{item.nickname}}</text>
|
||||
<text class="id">{{item.id}}</text>
|
||||
</view>
|
||||
<button class="invite-btn" bindtap="onSendInvite" data-user="{{item}}">邀请</button>
|
||||
</view>
|
||||
|
||||
<view class="tips">
|
||||
提示:当前为本地模拟搜索。
|
||||
</view>
|
||||
</view>
|
||||
@ -0,0 +1,110 @@
|
||||
/* 整体页面 */
|
||||
.page {
|
||||
padding: 0;
|
||||
background: #f5f6f7;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ========== 微信风格搜索栏 ========== */
|
||||
.wx-search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 12px;
|
||||
background: #ffffff;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.wx-search-input-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f2f2f2;
|
||||
border-radius: 999px; /* 和微信一样的巨大圆角 */
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.wx-search-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
opacity: 0.6;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.wx-search-input {
|
||||
flex: 1;
|
||||
height: 34px;
|
||||
font-size: 15px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.wx-search-placeholder {
|
||||
color: #b5b5b5;
|
||||
}
|
||||
|
||||
.wx-search-btn {
|
||||
margin-left: 10px;
|
||||
padding: 6px 16px;
|
||||
background: #1aad19;
|
||||
color: #fff;
|
||||
border-radius: 8px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/* ========== 搜索结果 ========== */
|
||||
|
||||
.result-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
background: #ffffff;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.wxid {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.invite-btn {
|
||||
padding: 6px 14px;
|
||||
background: #1aad19;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 底部提示 */
|
||||
.tips {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
padding: 20px 0;
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
// family-members.js
|
||||
Page({
|
||||
data: {
|
||||
familyList: []
|
||||
},
|
||||
// family-members.js 中修改 onLoad 方法
|
||||
onLoad() {
|
||||
let familyList = wx.getStorageSync('familyData');
|
||||
// 若缓存为空,从默认数据初始化(可从 family.js 的默认数据复制一份作为备用)
|
||||
if (!familyList || familyList.length === 0) {
|
||||
// 这里的默认数据应与 family.js 中的默认 familyList 保持一致
|
||||
familyList = [
|
||||
{
|
||||
id: 1,
|
||||
name: '温馨小家',
|
||||
description: '共同关注家人健康',
|
||||
members: [
|
||||
{ id: 1, role: '爸爸', nickname: '不吃芹菜', avatar: '/images/avatars/father.png' },
|
||||
{ id: 2, role: '爷爷', nickname: '最爱打麻将', avatar: '/images/avatars/grandfather.png' },
|
||||
{ id: 3, role: '女儿', nickname: '爱吃香菜', avatar: '/images/avatars/daughter.png' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '健康之家',
|
||||
description: '科学管理全家健康',
|
||||
members: [
|
||||
{ id: 4, role: '妈妈', nickname: '健康达人', avatar: '/images/avatars/mother.png' },
|
||||
{ id: 5, role: '儿子', nickname: '运动小将', avatar: '/images/avatars/son.png' }
|
||||
]
|
||||
}
|
||||
];
|
||||
// 写入缓存,避免下次加载仍为空
|
||||
wx.setStorageSync('familyData', familyList);
|
||||
}
|
||||
this.setData({ familyList });
|
||||
},
|
||||
selectMember(e) {
|
||||
const member = e.currentTarget.dataset.member;
|
||||
if (!member || !member.id) {
|
||||
wx.showToast({ title: '成员信息异常', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const eventChannel = this.getOpenerEventChannel();
|
||||
eventChannel.emit('selectMember', member);
|
||||
wx.navigateBack();
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"navigationBarTitleText": "选择家庭成员"
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
<view class="page-container">
|
||||
<view class="section-title">请选择要查看的家庭成员</view>
|
||||
|
||||
<!-- 遍历家庭列表 -->
|
||||
<block wx:for="{{familyList}}" wx:key="id">
|
||||
<view class="family-block">
|
||||
<text class="family-name">{{item.name}}</text>
|
||||
|
||||
<!-- 遍历成员列表 -->
|
||||
<block wx:for="{{item.members}}" wx:key="id">
|
||||
<view class="member-item" bindtap="selectMember" data-member="{{item}}">
|
||||
<image src="{{item.avatar}}" class="avatar" />
|
||||
<view class="info">
|
||||
<text class="nickname">{{item.nickname}}</text>
|
||||
<text class="role">{{item.role}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
@ -0,0 +1,54 @@
|
||||
.page-container {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.family-block {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.family-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #4CAF50;
|
||||
margin-bottom: 10rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.member-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
box-shadow: 0 4rpx 10rpx rgba(0,0,0,0.05);
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.role {
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
}
|
||||
@ -1,66 +0,0 @@
|
||||
// pages/history/manual-entry.js
|
||||
Page({
|
||||
|
||||
/**
|
||||
* 页面的初始数据
|
||||
*/
|
||||
data: {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面初次渲染完成
|
||||
*/
|
||||
onReady() {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow() {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面隐藏
|
||||
*/
|
||||
onHide() {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面卸载
|
||||
*/
|
||||
onUnload() {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面相关事件处理函数--监听用户下拉动作
|
||||
*/
|
||||
onPullDownRefresh() {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面上拉触底事件的处理函数
|
||||
*/
|
||||
onReachBottom() {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户点击右上角分享
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
|
||||
}
|
||||
})
|
||||
@ -1,2 +0,0 @@
|
||||
<!--pages/history/manual-entry.wxml-->
|
||||
<text>pages/history/manual-entry.wxml</text>
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"navigationBarTitleText": "成员历史数据",
|
||||
"enablePullDownRefresh": true,
|
||||
"backgroundColor": "#f8fbff",
|
||||
"backgroundTextStyle": "dark",
|
||||
"onReachBottomDistance": 50
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
<view class="container">
|
||||
<!-- 文章标题 -->
|
||||
<view class="article-header">
|
||||
<text class="article-title">{{articleDetail.title}}</text>
|
||||
<view class="article-meta">
|
||||
<text class="author">作者:{{articleDetail.author}}</text>
|
||||
<text class="date">{{articleDetail.date}}</text>
|
||||
<text class="read-count">{{articleDetail.readCount}} 阅读</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 文章封面 -->
|
||||
<image wx:if="{{articleDetail.cover}}" src="{{articleDetail.cover}}" class="article-cover" mode="widthFix"></image>
|
||||
|
||||
<!-- 文章内容 -->
|
||||
<view class="article-content">
|
||||
<text class="content-text">{{articleDetail.content}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 相关推荐 -->
|
||||
<view class="related-articles" wx:if="{{relatedArticles.length > 0}}">
|
||||
<view class="section-title">相关推荐</view>
|
||||
<view class="related-list">
|
||||
<view class="related-item" wx:for="{{relatedArticles}}" wx:key="id" bindtap="viewRelatedArticle" data-id="{{item.id}}">
|
||||
<text class="related-title">{{item.title}}</text>
|
||||
<text class="related-read">{{item.readCount}} 阅读</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -0,0 +1,83 @@
|
||||
.container {
|
||||
padding: 30rpx;
|
||||
background: #f8f8f8;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.article-header {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.article-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.article-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.article-cover {
|
||||
width: 100%;
|
||||
border-radius: 10rpx;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.article-content {
|
||||
background: #fff;
|
||||
padding: 40rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.content-text {
|
||||
font-size: 30rpx;
|
||||
line-height: 1.8;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.related-articles {
|
||||
background: #fff;
|
||||
padding: 30rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 30rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.related-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.related-title {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-right: 20rpx;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.related-read {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
{
|
||||
"usingComponents": {},
|
||||
"navigationBarTitleText": "登录"
|
||||
"usingComponents": {}
|
||||
}
|
||||
@ -0,0 +1,197 @@
|
||||
// chat.js
|
||||
Page({
|
||||
data: {
|
||||
chatUser: {
|
||||
id: '',
|
||||
name: '',
|
||||
avatar: '',
|
||||
online: true
|
||||
},
|
||||
userInfo: {
|
||||
avatar: '/images/my-avatar.png'
|
||||
},
|
||||
messageList: [],
|
||||
inputMessage: '',
|
||||
scrollToView: '',
|
||||
timeDividers: []
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
// 从消息列表传递过来的用户ID
|
||||
const userId = options.id;
|
||||
|
||||
// 根据用户ID获取聊天用户信息和历史消息
|
||||
this.getChatUserInfo(userId);
|
||||
this.getMessageHistory(userId);
|
||||
},
|
||||
|
||||
// 获取聊天用户信息
|
||||
getChatUserInfo(userId) {
|
||||
// 模拟数据,实际应从服务器获取
|
||||
const users = {
|
||||
'1': { id: '1', name: '张三', avatar: '/images/avatar1.png', online: true },
|
||||
'2': { id: '2', name: '李四', avatar: '/images/avatar2.png', online: false },
|
||||
'3': { id: '3', name: '王五', avatar: '/images/avatar3.png', online: true }
|
||||
};
|
||||
|
||||
this.setData({
|
||||
chatUser: users[userId] || { id: userId, name: '糖友', avatar: '/images/default-avatar.png', online: true }
|
||||
});
|
||||
|
||||
// 设置页面标题
|
||||
wx.setNavigationBarTitle({
|
||||
title: this.data.chatUser.name
|
||||
});
|
||||
},
|
||||
|
||||
// 获取历史消息
|
||||
getMessageHistory(userId) {
|
||||
// 模拟数据,实际应从服务器获取
|
||||
const messages = [
|
||||
{ id: 1, content: '你好,最近血糖控制得怎么样?', time: '10:30', isSelf: false, status: 'read' },
|
||||
{ id: 2, content: '还不错,最近饮食控制得比较好', time: '10:32', isSelf: true, status: 'read' },
|
||||
{ id: 3, content: '那太好了,有什么经验可以分享吗?', time: '10:33', isSelf: false, status: 'read' },
|
||||
{ id: 4, content: '主要是减少了主食摄入,增加了蔬菜比例', time: '10:35', isSelf: true, status: 'read' }
|
||||
];
|
||||
|
||||
this.setData({
|
||||
messageList: messages
|
||||
});
|
||||
|
||||
// 添加时间分隔线
|
||||
this.addTimeDividers();
|
||||
|
||||
// 滚动到底部
|
||||
this.scrollToBottom();
|
||||
},
|
||||
|
||||
// 输入框内容变化
|
||||
onInputChange(e) {
|
||||
this.setData({
|
||||
inputMessage: e.detail.value
|
||||
});
|
||||
},
|
||||
|
||||
// 发送消息
|
||||
sendMessage() {
|
||||
const message = this.data.inputMessage.trim();
|
||||
if (!message) return;
|
||||
|
||||
// 生成新消息
|
||||
const newMessage = {
|
||||
id: Date.now(),
|
||||
content: message,
|
||||
time: this.getCurrentTime(),
|
||||
isSelf: true,
|
||||
status: 'sending'
|
||||
};
|
||||
|
||||
// 添加到消息列表
|
||||
this.setData({
|
||||
messageList: [...this.data.messageList, newMessage],
|
||||
inputMessage: ''
|
||||
});
|
||||
|
||||
// 滚动到底部
|
||||
this.scrollToBottom();
|
||||
|
||||
// 模拟发送到服务器
|
||||
setTimeout(() => {
|
||||
this.updateMessageStatus(newMessage.id, 'sent');
|
||||
|
||||
// 模拟对方回复
|
||||
setTimeout(() => {
|
||||
this.addReplyMessage();
|
||||
}, 1000);
|
||||
}, 500);
|
||||
},
|
||||
|
||||
// 更新消息状态
|
||||
updateMessageStatus(messageId, status) {
|
||||
const messages = this.data.messageList.map(msg => {
|
||||
if (msg.id === messageId) {
|
||||
return { ...msg, status };
|
||||
}
|
||||
return msg;
|
||||
});
|
||||
|
||||
this.setData({
|
||||
messageList: messages
|
||||
});
|
||||
},
|
||||
|
||||
// 添加回复消息(模拟)
|
||||
addReplyMessage() {
|
||||
const replies = [
|
||||
'谢谢分享,我也试试这个方法',
|
||||
'最近我的血糖波动比较大,有点担心',
|
||||
'你平时做什么运动来控制血糖?',
|
||||
'我最近发现了一种新的低糖食谱,效果不错'
|
||||
];
|
||||
|
||||
const randomReply = replies[Math.floor(Math.random() * replies.length)];
|
||||
|
||||
const replyMessage = {
|
||||
id: Date.now() + 1,
|
||||
content: randomReply,
|
||||
time: this.getCurrentTime(),
|
||||
isSelf: false,
|
||||
status: 'read'
|
||||
};
|
||||
|
||||
this.setData({
|
||||
messageList: [...this.data.messageList, replyMessage]
|
||||
});
|
||||
|
||||
// 滚动到底部
|
||||
this.scrollToBottom();
|
||||
},
|
||||
|
||||
// 滚动到底部
|
||||
scrollToBottom() {
|
||||
if (this.data.messageList.length > 0) {
|
||||
const lastMsgId = this.data.messageList[this.data.messageList.length - 1].id;
|
||||
this.setData({
|
||||
scrollToView: `msg-${lastMsgId}`
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 添加时间分隔线
|
||||
addTimeDividers() {
|
||||
// 在实际应用中,可以根据消息时间添加分隔线
|
||||
// 这里简单添加一个示例
|
||||
this.setData({
|
||||
timeDividers: ['今天']
|
||||
});
|
||||
},
|
||||
|
||||
// 获取当前时间
|
||||
getCurrentTime() {
|
||||
const now = new Date();
|
||||
return `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`;
|
||||
},
|
||||
|
||||
// 跳转到用户资料页面
|
||||
goToUserProfile(e) {
|
||||
const userId = e.currentTarget.dataset.userid;
|
||||
const isSelf = e.currentTarget.dataset.isself;
|
||||
|
||||
if (isSelf) {
|
||||
// 如果是自己,跳转到个人资料页
|
||||
wx.navigateTo({
|
||||
url: '/pages/profile/profile'
|
||||
});
|
||||
} else {
|
||||
// 如果是他人,跳转到他人资料页
|
||||
wx.navigateTo({
|
||||
url: `/pages/otherprofile/otherprofile?userId=${userId}`
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 返回消息列表
|
||||
goBack() {
|
||||
wx.navigateBack();
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,8 @@
|
||||
|
||||
{
|
||||
"usingComponents": {},
|
||||
"navigationBarTitleText": "聊天",
|
||||
"navigationBarTextStyle": "white",
|
||||
"enablePullDownRefresh": true,
|
||||
"onReachBottomDistance": 50
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
<!-- chat.wxml -->
|
||||
<view class="chat-container">
|
||||
|
||||
<!-- 聊天消息区域 -->
|
||||
<scroll-view class="chat-messages" scroll-y scroll-into-view="{{scrollToView}}" scroll-with-animation>
|
||||
<view class="messages-container">
|
||||
<!-- 时间分隔线 -->
|
||||
<view class="time-divider" wx:for="{{timeDividers}}" wx:key="*this">
|
||||
<text class="time-text">{{item}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<!-- 在消息行的头像部分添加点击事件 -->
|
||||
<view class="message-row {{item.isSelf ? 'self' : 'other'}}"
|
||||
wx:for="{{messageList}}"
|
||||
wx:key="id"
|
||||
id="msg-{{item.id}}">
|
||||
<!-- 添加 bindtap 跳转事件 -->
|
||||
<image class="message-avatar"
|
||||
src="{{item.isSelf ? userInfo.avatar : chatUser.avatar}}"
|
||||
bindtap="goToUserProfile"
|
||||
data-userid="{{item.isSelf ? userInfo.id : chatUser.id}}"
|
||||
data-isself="{{item.isSelf}}"></image>
|
||||
<view class="message-bubble">
|
||||
<text class="message-content">{{item.content}}</text>
|
||||
<view class="message-meta">
|
||||
<text class="message-time">{{item.time}}</text>
|
||||
<text class="message-status" wx:if="{{item.isSelf}}">
|
||||
{{item.status === 'sending' ? '发送中' : (item.status === 'sent' ? '已发送' : '已读')}}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 输入区域 -->
|
||||
<view class="input-area">
|
||||
<view class="input-container">
|
||||
<view class="input-center">
|
||||
<input class="message-input"
|
||||
value="{{inputMessage}}"
|
||||
bindinput="onInputChange"
|
||||
placeholder="输入消息..."
|
||||
confirm-type="send"
|
||||
bindconfirm="sendMessage"/>
|
||||
</view>
|
||||
<view class="input-right">
|
||||
<image src="/images/add.png" class="input-icon"></image>
|
||||
<button class="send-btn {{inputMessage ? 'active' : ''}}"
|
||||
bindtap="sendMessage"
|
||||
disabled="{{!inputMessage}}">
|
||||
{{inputMessage ? '发送' : '语音'}}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -1,5 +1,8 @@
|
||||
{
|
||||
"usingComponents": {},
|
||||
"navigationBarTitleText": "调整监测计划",
|
||||
"enablePullDownRefresh": false
|
||||
"navigationBarTitleText": "测量闹钟",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarBackgroundColor": "#2196F3",
|
||||
"backgroundColor": "#f5f5f5"
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
{
|
||||
"usingComponents": {},
|
||||
"navigationBarTitleText": "监测计划",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarBackgroundColor": "#2196f3"
|
||||
}
|
||||
|
||||
@ -0,0 +1,114 @@
|
||||
// other-profile.js
|
||||
Page({
|
||||
data: {
|
||||
userInfo: {},
|
||||
isFollowing: false,
|
||||
feedList: [],
|
||||
mutualFriends: []
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const userId = options.userId;
|
||||
this.loadUserProfile(userId);
|
||||
},
|
||||
|
||||
// 加载用户资料
|
||||
loadUserProfile(userId) {
|
||||
// 模拟数据
|
||||
const userInfo = {
|
||||
id: userId,
|
||||
name: '风之海',
|
||||
avatar: '/images/avatars/user1.jpg',
|
||||
cover: '/images/covers/cover1.jpg',
|
||||
gender: 'male',
|
||||
age: 28,
|
||||
birthday: '1995-06-15',
|
||||
bio: '热爱生活,喜欢分享的糖友。让我们一起控糖,享受健康生活!',
|
||||
posts: 24,
|
||||
followers: 156,
|
||||
following: 89,
|
||||
registerTime: '2023-03-15',
|
||||
lastActive: '2小时前',
|
||||
location: '北京'
|
||||
};
|
||||
|
||||
const feedList = [
|
||||
{
|
||||
id: 1,
|
||||
title: '今日早餐分享',
|
||||
images: ['/images/posts/breakfast.jpg'],
|
||||
likes: 23,
|
||||
comments: 5
|
||||
},
|
||||
// ... 更多帖子数据
|
||||
];
|
||||
|
||||
const mutualFriends = [
|
||||
{ id: 2, name: '小明', avatar: '/images/avatars/user2.jpg' },
|
||||
{ id: 3, name: '小红', avatar: '/images/avatars/user3.jpg' },
|
||||
// ... 更多共同好友
|
||||
];
|
||||
|
||||
this.setData({
|
||||
userInfo,
|
||||
feedList,
|
||||
mutualFriends
|
||||
});
|
||||
},
|
||||
|
||||
// 发送消息
|
||||
sendMessage() {
|
||||
wx.navigateTo({
|
||||
url: `/pages/message/chat/chat?userId=${this.data.userInfo.id}`
|
||||
});
|
||||
},
|
||||
|
||||
// 关注/取消关注
|
||||
toggleFollow() {
|
||||
const { isFollowing } = this.data;
|
||||
this.setData({
|
||||
isFollowing: !isFollowing
|
||||
});
|
||||
|
||||
wx.showToast({
|
||||
title: isFollowing ? '已取消关注' : '关注成功',
|
||||
icon: 'success'
|
||||
});
|
||||
},
|
||||
|
||||
// 查看帖子详情
|
||||
viewPostDetail(e) {
|
||||
const postId = e.currentTarget.dataset.id;
|
||||
wx.navigateTo({
|
||||
url: `/pages/profile/detail/detail?id=${postId}`
|
||||
});
|
||||
},
|
||||
|
||||
// 查看好友资料
|
||||
viewFriendProfile(e) {
|
||||
const userId = e.currentTarget.dataset.id;
|
||||
wx.navigateTo({
|
||||
url: `/pages/other/profile?userId=${userId}`
|
||||
});
|
||||
},
|
||||
|
||||
// 导航到全部帖子
|
||||
navigateToAllPosts() {
|
||||
wx.navigateTo({
|
||||
url: `/pages/profile/allposts/allposts?isOwnPosts = false`
|
||||
});
|
||||
},
|
||||
|
||||
// 其他导航方法...
|
||||
navigateToFollowers() {
|
||||
wx.navigateTo({
|
||||
url: `/pages/profile/followers/followers?tab=followers`
|
||||
});
|
||||
},
|
||||
|
||||
navigateToFollowing() {
|
||||
wx.navigateTo({
|
||||
url: `/pages/profile/followers/followers?tab=following`
|
||||
});
|
||||
}
|
||||
});
|
||||