docs: 更新项目文档,完善需求和技术细节

This commit is contained in:
ylweng
2025-09-01 01:05:53 +08:00
parent 028a458283
commit 816a51ae82
48 changed files with 18629 additions and 5301 deletions

View File

@@ -19,6 +19,9 @@
### 修复
- 文档中的错误和遗漏信息
- 开发环境配置问题
- 数据库用户表缺少last_login字段的问题
- 密码哈希值存储和验证问题
- bcrypt版本不匹配导致的密码验证失败
## 版本规范

11814
admin_website/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -10,12 +10,13 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"axios": "^1.4.0",
"echarts": "^6.0.0",
"element-plus": "^2.3.12",
"vue": "^3.3.4",
"vue-router": "^4.2.4",
"vuex": "^4.0.2",
"element-plus": "^2.3.12",
"axios": "^1.4.0",
"@element-plus/icons-vue": "^2.1.0"
"vuex": "^4.0.2"
},
"devDependencies": {
"@vue/cli-service": "^5.0.8",
@@ -30,4 +31,4 @@
"last 2 versions",
"not dead"
]
}
}

31
admin_website/src/App.vue Normal file
View File

@@ -0,0 +1,31 @@
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
height: 100vh;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
}
</style>

View File

@@ -0,0 +1,229 @@
<template>
<div class="main-layout">
<!-- 顶部导航栏 -->
<el-header class="header">
<div class="header-left">
<el-button
:icon="sidebarCollapsed ? 'Expand' : 'Fold'"
@click="toggleSidebar"
class="sidebar-toggle"
/>
<span class="logo">
<i class="el-icon-flower" style="color: #4CAF50; margin-right: 8px;"></i>
爱鉴花后台管理系统
</span>
</div>
<div class="header-right">
<el-dropdown>
<span class="user-info">
<el-avatar :size="32" :src="userInfo.avatar_url || ''" />
<span class="username">{{ userInfo.username || '管理员' }}</span>
<el-icon class="el-icon--right"><arrow-down /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="goToProfile">
<el-icon><user /></el-icon>
个人信息
</el-dropdown-item>
<el-dropdown-item @click="logout" divided>
<el-icon><switch-button /></el-icon>
退出登录
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header>
<!-- 主体区域 -->
<div class="main-container">
<!-- 侧边栏 -->
<el-aside :width="sidebarCollapsed ? '64px' : '200px'" class="sidebar">
<el-menu
:default-active="$route.path"
:collapse="sidebarCollapsed"
:collapse-transition="false"
router
class="sidebar-menu"
>
<el-menu-item index="/dashboard">
<el-icon><odometer /></el-icon>
<template #title>仪表盘</template>
</el-menu-item>
<el-sub-menu index="user-management">
<template #title>
<el-icon><user /></el-icon>
<span>用户管理</span>
</template>
<el-menu-item index="/users">用户列表</el-menu-item>
<el-menu-item index="/users/add">添加用户</el-menu-item>
</el-sub-menu>
<el-sub-menu index="product-management">
<template #title>
<el-icon><goods /></el-icon>
<span>商品管理</span>
</template>
<el-menu-item index="/products">商品列表</el-menu-item>
<el-menu-item index="/products/add">添加商品</el-menu-item>
<el-menu-item index="/categories">分类管理</el-menu-item>
</el-sub-menu>
<el-sub-menu index="order-management">
<template #title>
<el-icon><document /></el-icon>
<span>订单管理</span>
</template>
<el-menu-item index="/orders">订单列表</el-menu-item>
<el-menu-item index="/orders/pending">待处理订单</el-menu-item>
</el-sub-menu>
<el-sub-menu index="statistics">
<template #title>
<el-icon><data-analysis /></el-icon>
<span>数据统计</span>
</template>
<el-menu-item index="/statistics">数据概览</el-menu-item>
<el-menu-item index="/statistics/reports">报表分析</el-menu-item>
</el-sub-menu>
<el-sub-menu index="settings">
<template #title>
<el-icon><setting /></el-icon>
<span>系统设置</span>
</template>
<el-menu-item index="/settings">系统参数</el-menu-item>
<el-menu-item index="/settings/roles">权限管理</el-menu-item>
<el-menu-item index="/settings/logs">操作日志</el-menu-item>
</el-sub-menu>
</el-menu>
</el-aside>
<!-- 内容区域 -->
<el-main class="main-content">
<router-view />
</el-main>
</div>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
import {
Odometer,
User,
Goods,
Document,
DataAnalysis,
Setting,
ArrowDown,
SwitchButton
} from '@element-plus/icons-vue'
export default {
name: 'MainLayout',
components: {
Odometer,
User,
Goods,
Document,
DataAnalysis,
Setting,
ArrowDown,
SwitchButton
},
computed: {
...mapState(['sidebarCollapsed', 'userInfo'])
},
methods: {
...mapActions(['toggleSidebar', 'logout']),
goToProfile() {
this.$message.info('跳转到个人信息页面')
}
}
}
</script>
<style scoped>
.main-layout {
height: 100vh;
display: flex;
flex-direction: column;
}
.header {
background: #fff;
border-bottom: 1px solid #e6e6e6;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}
.header-left {
display: flex;
align-items: center;
}
.sidebar-toggle {
margin-right: 16px;
border: none;
font-size: 18px;
}
.logo {
font-size: 18px;
font-weight: bold;
color: #4CAF50;
}
.header-right {
display: flex;
align-items: center;
}
.user-info {
display: flex;
align-items: center;
cursor: pointer;
padding: 8px 12px;
border-radius: 4px;
transition: background-color 0.3s;
}
.user-info:hover {
background-color: #f5f5f5;
}
.username {
margin: 0 8px;
font-weight: 500;
}
.main-container {
flex: 1;
display: flex;
overflow: hidden;
}
.sidebar {
background: #fff;
border-right: 1px solid #e6e6e6;
transition: width 0.3s;
}
.sidebar-menu {
border: none;
height: 100%;
}
.main-content {
padding: 20px;
background: #f5f7fa;
overflow-y: auto;
}
</style>

20
admin_website/src/main.js Normal file
View File

@@ -0,0 +1,20 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
// 注册Element Plus图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(store)
app.use(router)
app.use(ElementPlus)
app.mount('#app')

View File

@@ -0,0 +1,74 @@
import { createRouter, createWebHistory } from 'vue-router'
import Login from '../views/Login.vue'
import Layout from '../layouts/MainLayout.vue'
const routes = [
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [
{
path: 'dashboard',
name: 'Dashboard',
component: () => import('../views/Dashboard.vue'),
meta: { title: '仪表盘' }
},
{
path: 'users',
name: 'Users',
component: () => import('../views/Users.vue'),
meta: { title: '用户管理' }
},
{
path: 'products',
name: 'Products',
component: () => import('../views/Products.vue'),
meta: { title: '商品管理' }
},
{
path: 'orders',
name: 'Orders',
component: () => import('../views/Orders.vue'),
meta: { title: '订单管理' }
},
{
path: 'statistics',
name: 'Statistics',
component: () => import('../views/Statistics.vue'),
meta: { title: '数据统计' }
},
{
path: 'settings',
name: 'Settings',
component: () => import('../views/Settings.vue'),
meta: { title: '系统设置' }
}
]
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
// 路由守卫 - 登录验证
router.beforeEach((to, from, next) => {
const isLoggedIn = localStorage.getItem('token')
if (to.path !== '/login' && !isLoggedIn) {
next('/login')
} else if (to.path === '/login' && isLoggedIn) {
next('/')
} else {
next()
}
})
export default router

View File

@@ -0,0 +1,45 @@
import { createStore } from 'vuex'
export default createStore({
state: {
token: localStorage.getItem('token') || '',
userInfo: JSON.parse(localStorage.getItem('userInfo') || '{}'),
sidebarCollapsed: false
},
mutations: {
SET_TOKEN(state, token) {
state.token = token
localStorage.setItem('token', token)
},
SET_USER_INFO(state, userInfo) {
state.userInfo = userInfo
localStorage.setItem('userInfo', JSON.stringify(userInfo))
},
CLEAR_AUTH(state) {
state.token = ''
state.userInfo = {}
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
},
TOGGLE_SIDEBAR(state) {
state.sidebarCollapsed = !state.sidebarCollapsed
}
},
actions: {
login({ commit }, { token, userInfo }) {
commit('SET_TOKEN', token)
commit('SET_USER_INFO', userInfo)
},
logout({ commit }) {
commit('CLEAR_AUTH')
},
toggleSidebar({ commit }) {
commit('TOGGLE_SIDEBAR')
}
},
getters: {
isLoggedIn: state => !!state.token,
userInfo: state => state.userInfo,
sidebarCollapsed: state => state.sidebarCollapsed
}
})

View File

@@ -0,0 +1,124 @@
import axios from 'axios'
// 创建axios实例
const api = axios.create({
baseURL: 'http://localhost:3200/api/v1',
timeout: 10000
})
// 请求拦截器
api.interceptors.request.use(
config => {
// 从localStorage获取token
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
api.interceptors.response.use(
response => {
// 直接返回数据部分
return response.data
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
// 未授权,清除本地存储并跳转到登录页
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
window.location.href = '/login'
break
case 403:
console.error('权限不足')
break
case 500:
console.error('服务器内部错误')
break
default:
console.error('请求失败', error.response.data)
}
} else {
console.error('网络错误', error.message)
}
return Promise.reject(error)
}
)
// 认证相关API
export const authAPI = {
// 用户登录
login: (data) => api.post('/auth/login', data),
// 用户注册
register: (data) => api.post('/auth/register', data),
// 获取当前用户信息
getCurrentUser: () => api.get('/users/me')
}
// 用户管理API
export const userAPI = {
// 获取用户列表
getUsers: (params) => api.get('/users', { params }),
// 获取用户详情
getUser: (id) => api.get(`/users/${id}`),
// 更新用户信息
updateUser: (id, data) => api.put(`/users/${id}`, data),
// 删除用户
deleteUser: (id) => api.delete(`/users/${id}`)
}
// 商品管理API
export const productAPI = {
// 获取商品列表
getProducts: (params) => api.get('/products', { params }),
// 获取商品详情
getProduct: (id) => api.get(`/products/${id}`),
// 创建商品
createProduct: (data) => api.post('/products', data),
// 更新商品
updateProduct: (id, data) => api.put(`/products/${id}`, data),
// 删除商品
deleteProduct: (id) => api.delete(`/products/${id}`)
}
// 订单管理API
export const orderAPI = {
// 获取订单列表
getOrders: (params) => api.get('/orders', { params }),
// 获取订单详情
getOrder: (id) => api.get(`/orders/${id}`),
// 更新订单状态
updateOrderStatus: (id, data) => api.put(`/orders/${id}/status`, data)
}
// 数据统计API
export const statisticsAPI = {
// 获取用户统计数据
getUserStats: () => api.get('/admin/stats/users'),
// 获取订单统计数据
getOrderStats: () => api.get('/admin/stats/orders'),
// 获取商品统计数据
getProductStats: () => api.get('/admin/stats/products')
}
export default api

View File

@@ -0,0 +1,416 @@
<template>
<div class="dashboard">
<!-- 页面标题 -->
<div class="page-header">
<h1>仪表盘</h1>
<p>欢迎回来{{ userInfo.username }}这里是系统概览</p>
</div>
<!-- 数据概览卡片 -->
<el-row :gutter="20" class="stats-cards">
<el-col :xs="24" :sm="12" :lg="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon users">
<el-icon><user /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ statsData.userCount || 0 }}</div>
<div class="stat-label">总用户数</div>
</div>
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="12" :lg="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon products">
<el-icon><goods /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ statsData.productCount || 0 }}</div>
<div class="stat-label">商品总数</div>
</div>
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="12" :lg="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon orders">
<el-icon><document /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ statsData.todayOrderCount || 0 }}</div>
<div class="stat-label">今日订单</div>
</div>
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="12" :lg="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon revenue">
<el-icon><money /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">¥{{ statsData.todayRevenue || 0 }}</div>
<div class="stat-label">今日收入</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 图表区域 -->
<el-row :gutter="20" class="charts-section">
<el-col :xs="24" :lg="16">
<el-card class="chart-card">
<template #header>
<div class="chart-header">
<h3>用户增长趋势</h3>
<el-select v-model="chartRange" size="small" style="width: 120px;" @change="fetchChartData">
<el-option label="近7天" value="7" />
<el-option label="近30天" value="30" />
<el-option label="近90天" value="90" />
</el-select>
</div>
</template>
<div ref="userChart" class="chart-container"></div>
</el-card>
</el-col>
<el-col :xs="24" :lg="8">
<el-card class="chart-card">
<template #header>
<h3>商品分类占比</h3>
</template>
<div ref="categoryChart" class="chart-container"></div>
</el-card>
</el-col>
</el-row>
<!-- 最近活动 -->
<el-card class="recent-activity">
<template #header>
<h3>最近活动</h3>
</template>
<el-timeline>
<el-timeline-item
v-for="(activity, index) in recentActivities"
:key="index"
:timestamp="activity.time"
:type="activity.type"
>
{{ activity.content }}
</el-timeline-item>
</el-timeline>
</el-card>
</div>
</template>
<script>
import { ref, computed, onMounted } from 'vue'
import { useStore } from 'vuex'
import * as echarts from 'echarts'
import { statisticsAPI } from '../utils/api'
export default {
name: 'Dashboard',
setup() {
const store = useStore()
const chartRange = ref('7')
const userChart = ref(null)
const categoryChart = ref(null)
const userChartInstance = ref(null)
const categoryChartInstance = ref(null)
const statsData = ref({})
const chartData = ref({})
const userInfo = computed(() => store.state.userInfo)
const recentActivities = [
{
time: '2024-01-15 14:30',
type: 'primary',
content: '新用户注册:用户「张三」注册了账号'
},
{
time: '2024-01-15 13:45',
type: 'success',
content: '订单创建:订单 #10086 已创建,金额 ¥199'
},
{
time: '2024-01-15 12:20',
type: 'warning',
content: '系统警告:数据库连接数接近上限'
},
{
time: '2024-01-15 10:15',
type: 'info',
content: '商品更新:商品「玫瑰花束」信息已更新'
}
]
// 获取统计数据
const fetchStatsData = async () => {
try {
// 这里应该调用实际的统计数据API
// 暂时使用模拟数据
statsData.value = {
userCount: 1234,
productCount: 567,
todayOrderCount: 89,
todayRevenue: 12345
}
} catch (error) {
console.error('获取统计数据失败:', error)
}
}
// 获取图表数据
const fetchChartData = async () => {
try {
// 这里应该调用实际的图表数据API
// 暂时使用模拟数据
chartData.value = {
userGrowth: {
dates: ['1月1日', '1月2日', '1月3日', '1月4日', '1月5日', '1月6日', '1月7日'],
counts: [10, 25, 15, 30, 20, 35, 40]
},
categoryDistribution: [
{ name: '鲜花', value: 45 },
{ name: '盆栽', value: 30 },
{ name: '种子', value: 15 },
{ name: '工具', value: 10 }
]
}
renderCharts()
} catch (error) {
console.error('获取图表数据失败:', error)
}
}
// 渲染图表
const renderCharts = () => {
// 用户增长趋势图
if (userChart.value) {
if (!userChartInstance.value) {
userChartInstance.value = echarts.init(userChart.value)
}
const userOption = {
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: chartData.value.userGrowth.dates
},
yAxis: {
type: 'value'
},
series: [{
data: chartData.value.userGrowth.counts,
type: 'line',
smooth: true,
areaStyle: {}
}]
}
userChartInstance.value.setOption(userOption)
}
// 商品分类占比图
if (categoryChart.value) {
if (!categoryChartInstance.value) {
categoryChartInstance.value = echarts.init(categoryChart.value)
}
const categoryOption = {
tooltip: {
trigger: 'item'
},
legend: {
top: 'bottom'
},
series: [{
name: '商品分类',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '18',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: chartData.value.categoryDistribution
}]
}
categoryChartInstance.value.setOption(categoryOption)
}
}
// 窗口大小改变时重绘图表
const handleResize = () => {
if (userChartInstance.value) {
userChartInstance.value.resize()
}
if (categoryChartInstance.value) {
categoryChartInstance.value.resize()
}
}
onMounted(() => {
fetchStatsData()
fetchChartData()
window.addEventListener('resize', handleResize)
})
return {
userInfo,
chartRange,
recentActivities,
statsData,
userChart,
categoryChart
}
}
}
</script>
<style scoped>
.dashboard {
padding: 20px;
}
.page-header {
margin-bottom: 24px;
}
.page-header h1 {
color: #303133;
margin-bottom: 8px;
font-size: 24px;
}
.page-header p {
color: #606266;
margin: 0;
}
.stats-cards {
margin-bottom: 24px;
}
.stat-card {
border: none;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.stat-content {
display: flex;
align-items: center;
}
.stat-icon {
width: 48px;
height: 48px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16px;
font-size: 20px;
}
.stat-icon.users {
background: #e8f5e8;
color: #4CAF50;
}
.stat-icon.products {
background: #e3f2fd;
color: #2196F3;
}
.stat-icon.orders {
background: #fff3e0;
color: #FF9800;
}
.stat-icon.revenue {
background: #fce4ec;
color: #E91E63;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #303133;
line-height: 1;
}
.stat-label {
color: #606266;
font-size: 14px;
margin-top: 4px;
}
.charts-section {
margin-bottom: 24px;
}
.chart-card {
border: none;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
height: 300px;
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.chart-header h3 {
margin: 0;
color: #303133;
}
.chart-container {
height: 240px;
width: 100%;
}
.recent-activity {
border: none;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.recent-activity h3 {
margin: 0;
color: #303133;
}
</style>

View File

@@ -0,0 +1,245 @@
<template>
<div class="login-container">
<div class="login-form">
<div class="login-header">
<div class="logo">
<i class="el-icon-flower" style="color: #4CAF50; font-size: 48px;"></i>
<h1>爱鉴花后台管理系统</h1>
</div>
<p>请输入您的账号和密码登录系统</p>
</div>
<el-form
:model="loginForm"
:rules="loginRules"
ref="loginFormRef"
class="login-form-content"
>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
placeholder="请输入用户名"
size="large"
prefix-icon="user"
/>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
placeholder="请输入密码"
size="large"
prefix-icon="lock"
show-password
@keyup.enter="handleLogin"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
size="large"
:loading="loading"
@click="handleLogin"
class="login-btn"
>
登录
</el-button>
</el-form-item>
</el-form>
<div class="login-footer">
<p>© 2024 爱鉴花 - AI花卉识别平台</p>
</div>
</div>
<div class="login-background">
<div class="background-overlay"></div>
</div>
</div>
</template>
<script>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { ElMessage } from 'element-plus'
import { authAPI } from '../utils/api'
export default {
name: 'Login',
setup() {
const router = useRouter()
const store = useStore()
const loginFormRef = ref()
const loading = ref(false)
const loginForm = ref({
username: 'admin',
password: 'admin123'
})
const loginRules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码长度不能少于6位', trigger: 'blur' }
]
}
const handleLogin = async () => {
if (!loginFormRef.value) return
const valid = await loginFormRef.value.validate()
if (!valid) return
loading.value = true
try {
// 调用登录API
const response = await authAPI.login({
login: loginForm.value.username,
password: loginForm.value.password
})
if (response.code === 200) {
// 存储token和用户信息
const userInfo = {
id: response.data.user_id,
username: response.data.username,
phone: response.data.phone,
email: response.data.email,
user_type: response.data.user_type,
avatar_url: response.data.avatar_url
}
store.dispatch('login', {
token: response.data.token,
userInfo
})
ElMessage.success('登录成功')
router.push('/dashboard')
} else {
ElMessage.error(response.message || '登录失败')
}
} catch (error) {
ElMessage.error('登录失败:' + (error.message || '网络错误'))
} finally {
loading.value = false
}
}
return {
loginForm,
loginRules,
loginFormRef,
loading,
handleLogin
}
}
}
</script>
<style scoped>
.login-container {
height: 100vh;
display: flex;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.login-form {
width: 400px;
background: #fff;
padding: 40px;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
z-index: 2;
margin: auto;
margin-right: 10%;
}
.login-header {
text-align: center;
margin-bottom: 30px;
}
.logo {
margin-bottom: 20px;
}
.logo h1 {
color: #4CAF50;
font-size: 24px;
margin-top: 16px;
font-weight: bold;
}
.logo p {
color: #666;
font-size: 14px;
}
.login-form-content {
margin-bottom: 30px;
}
.login-btn {
width: 100%;
background: #4CAF50;
border: none;
font-size: 16px;
height: 48px;
}
.login-btn:hover {
background: #45a049;
}
.login-footer {
text-align: center;
margin-top: 20px;
}
.login-footer p {
color: #999;
font-size: 12px;
}
.login-background {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="flowers" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse"><circle cx="10" cy="10" r="8" fill="%234CAF50" opacity="0.1"/></pattern></defs><rect x="0" y="0" width="100" height="100" fill="url(%23flowers)"/></svg>') repeat;
}
.background-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.1);
}
@media (max-width: 768px) {
.login-form {
width: 90%;
margin: auto;
padding: 20px;
}
.login-container {
background: #fff;
}
.login-background {
display: none;
}
}
</style>

View File

@@ -0,0 +1,382 @@
<template>
<div class="orders-container">
<el-card class="orders-card">
<template #header>
<div class="card-header">
<span>订单管理</span>
</div>
</template>
<!-- 搜索条件 -->
<el-form :inline="true" :model="searchForm" class="search-form">
<el-form-item label="订单状态">
<el-select v-model="searchForm.status" clearable placeholder="请选择">
<el-option label="待支付" value="pending" />
<el-option label="已支付" value="paid" />
<el-option label="已取消" value="cancelled" />
<el-option label="已发货" value="shipped" />
<el-option label="已完成" value="completed" />
</el-select>
</el-form-item>
<el-form-item label="时间范围">
<el-date-picker
v-model="dateRange"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 订单列表 -->
<el-table :data="orderList" border style="width: 100%" v-loading="loading">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="orderNo" label="订单号" width="180" />
<el-table-column prop="username" label="用户" />
<el-table-column prop="phone" label="手机号" width="120" />
<el-table-column prop="amount" label="订单金额" width="100">
<template #default="scope">
¥{{ scope.row.amount }}
</template>
</el-table-column>
<el-table-column label="支付状态" width="100">
<template #default="scope">
<el-tag v-if="scope.row.paymentStatus === 'pending'">待支付</el-tag>
<el-tag v-else-if="scope.row.paymentStatus === 'paid'" type="success">已支付</el-tag>
<el-tag v-else-if="scope.row.paymentStatus === 'cancelled'" type="danger">已取消</el-tag>
<el-tag v-else>{{ scope.row.paymentStatus }}</el-tag>
</template>
</el-table-column>
<el-table-column label="发货状态" width="100">
<template #default="scope">
<el-tag v-if="scope.row.shippingStatus === 'pending'">待发货</el-tag>
<el-tag v-else-if="scope.row.shippingStatus === 'shipped'" type="success">已发货</el-tag>
<el-tag v-else-if="scope.row.shippingStatus === 'completed'" type="primary">已完成</el-tag>
<el-tag v-else>{{ scope.row.shippingStatus }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="下单时间" width="180">
<template #default="scope">
{{ formatDate(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<template #default="scope">
<el-button size="small" @click="handleView(scope.row)">查看</el-button>
<el-button
size="small"
type="primary"
@click="handleUpdateStatus(scope.row)"
:disabled="scope.row.paymentStatus === 'cancelled'"
>
状态
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.limit"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
class="pagination"
/>
<!-- 订单详情对话框 -->
<el-dialog v-model="detailDialogVisible" title="订单详情" width="700px">
<el-descriptions :column="1" border>
<el-descriptions-item label="订单号">{{ currentOrder.orderNo }}</el-descriptions-item>
<el-descriptions-item label="用户">{{ currentOrder.username }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ currentOrder.phone }}</el-descriptions-item>
<el-descriptions-item label="订单金额">¥{{ currentOrder.amount }}</el-descriptions-item>
<el-descriptions-item label="支付状态">
<el-tag v-if="currentOrder.paymentStatus === 'pending'">待支付</el-tag>
<el-tag v-else-if="currentOrder.paymentStatus === 'paid'" type="success">已支付</el-tag>
<el-tag v-else-if="currentOrder.paymentStatus === 'cancelled'" type="danger">已取消</el-tag>
<el-tag v-else>{{ currentOrder.paymentStatus }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="发货状态">
<el-tag v-if="currentOrder.shippingStatus === 'pending'">待发货</el-tag>
<el-tag v-else-if="currentOrder.shippingStatus === 'shipped'" type="success">已发货</el-tag>
<el-tag v-else-if="currentOrder.shippingStatus === 'completed'" type="primary">已完成</el-tag>
<el-tag v-else>{{ currentOrder.shippingStatus }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="收货地址">{{ currentOrder.shippingAddress }}</el-descriptions-item>
<el-descriptions-item label="下单时间">{{ formatDate(currentOrder.createTime) }}</el-descriptions-item>
</el-descriptions>
<el-table :data="currentOrder.items" style="margin-top: 20px;" border>
<el-table-column prop="productName" label="商品名称" />
<el-table-column prop="quantity" label="数量" width="80" />
<el-table-column prop="unitPrice" label="单价" width="100">
<template #default="scope">
¥{{ scope.row.unitPrice }}
</template>
</el-table-column>
<el-table-column label="小计" width="100">
<template #default="scope">
¥{{ (scope.row.unitPrice * scope.row.quantity).toFixed(2) }}
</template>
</el-table-column>
</el-table>
<template #footer>
<span class="dialog-footer">
<el-button @click="detailDialogVisible = false">关闭</el-button>
</span>
</template>
</el-dialog>
<!-- 更新订单状态对话框 -->
<el-dialog v-model="statusDialogVisible" title="更新订单状态" width="500px">
<el-form :model="statusForm" label-width="100px">
<el-form-item label="支付状态">
<el-select v-model="statusForm.paymentStatus" placeholder="请选择">
<el-option label="待支付" value="pending" />
<el-option label="已支付" value="paid" />
<el-option label="已取消" value="cancelled" />
</el-select>
</el-form-item>
<el-form-item label="发货状态">
<el-select v-model="statusForm.shippingStatus" placeholder="请选择">
<el-option label="待发货" value="pending" />
<el-option label="已发货" value="shipped" />
<el-option label="已完成" value="completed" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="statusDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitStatusUpdate">确定</el-button>
</span>
</template>
</el-dialog>
</el-card>
</div>
</template>
<script>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
export default {
name: 'Orders',
setup() {
const loading = ref(false)
const detailDialogVisible = ref(false)
const statusDialogVisible = ref(false)
const searchForm = reactive({
status: ''
})
const dateRange = ref([])
const statusForm = reactive({
id: null,
paymentStatus: '',
shippingStatus: ''
})
const orderList = ref([])
const currentOrder = ref({})
const pagination = reactive({
page: 1,
limit: 10,
total: 0
})
// 格式化日期
const formatDate = (dateString) => {
if (!dateString) return ''
const date = new Date(dateString)
return date.toLocaleString('zh-CN')
}
// 获取订单列表
const fetchOrders = async () => {
loading.value = true
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 500))
// 模拟数据
orderList.value = [
{
id: 1,
orderNo: 'ORD202401150001',
username: 'user001',
phone: '13800001111',
amount: 199,
paymentStatus: 'paid',
shippingStatus: 'shipped',
createTime: '2024-01-15 10:30:00',
shippingAddress: '浙江省杭州市西湖区文三路159号',
items: [
{ productName: 'AI鉴花小程序', quantity: 1, unitPrice: 199 }
]
},
{
id: 2,
orderNo: 'ORD202401160002',
username: 'admin',
phone: '13900002222',
amount: 598,
paymentStatus: 'paid',
shippingStatus: 'pending',
createTime: '2024-01-16 14:20:00',
shippingAddress: '浙江省杭州市滨江区网商路699号',
items: [
{ productName: '花卉识别API', quantity: 2, unitPrice: 299 }
]
},
{
id: 3,
orderNo: 'ORD202401170003',
username: 'editor001',
phone: '13700003333',
amount: 999,
paymentStatus: 'pending',
shippingStatus: 'pending',
createTime: '2024-01-17 09:15:00',
shippingAddress: '浙江省杭州市余杭区五常大道1001号',
items: [
{ productName: '企业版解决方案', quantity: 1, unitPrice: 999 }
]
}
]
pagination.total = orderList.value.length
} catch (error) {
ElMessage.error('获取订单列表失败')
} finally {
loading.value = false
}
}
// 搜索
const handleSearch = () => {
pagination.page = 1
fetchOrders()
}
// 重置搜索
const handleReset = () => {
searchForm.status = ''
dateRange.value = []
pagination.page = 1
fetchOrders()
}
// 查看订单详情
const handleView = (row) => {
currentOrder.value = { ...row }
detailDialogVisible.value = true
}
// 更新订单状态
const handleUpdateStatus = (row) => {
statusForm.id = row.id
statusForm.paymentStatus = row.paymentStatus
statusForm.shippingStatus = row.shippingStatus
statusDialogVisible.value = true
}
// 提交状态更新
const submitStatusUpdate = async () => {
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 300))
// 更新订单状态
const index = orderList.value.findIndex(item => item.id === statusForm.id)
if (index !== -1) {
orderList.value[index].paymentStatus = statusForm.paymentStatus
orderList.value[index].shippingStatus = statusForm.shippingStatus
}
ElMessage.success('状态更新成功')
statusDialogVisible.value = false
} catch (error) {
ElMessage.error('状态更新失败')
}
}
// 分页相关
const handleSizeChange = (val) => {
pagination.limit = val
pagination.page = 1
fetchOrders()
}
const handleCurrentChange = (val) => {
pagination.page = val
fetchOrders()
}
onMounted(() => {
fetchOrders()
})
return {
loading,
detailDialogVisible,
statusDialogVisible,
searchForm,
dateRange,
statusForm,
orderList,
currentOrder,
pagination,
formatDate,
handleSearch,
handleReset,
handleView,
handleUpdateStatus,
submitStatusUpdate,
handleSizeChange,
handleCurrentChange
}
}
}
</script>
<style scoped>
.orders-container {
padding: 20px;
}
.orders-card {
border: none;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.search-form {
margin-bottom: 20px;
}
.pagination {
margin-top: 20px;
display: flex;
justify-content: center;
}
</style>

View File

@@ -0,0 +1,450 @@
<template>
<div class="products-container">
<el-card class="products-card">
<template #header>
<div class="card-header">
<span>商品管理</span>
<el-button type="primary" @click="handleAddProduct">新增商品</el-button>
</div>
</template>
<!-- 搜索条件 -->
<el-form :inline="true" :model="searchForm" class="search-form">
<el-form-item label="关键词">
<el-input v-model="searchForm.keyword" placeholder="商品名称" />
</el-form-item>
<el-form-item label="分类">
<el-select v-model="searchForm.category_id" clearable placeholder="请选择">
<el-option label="全部分类" :value="0" />
<el-option label="鲜花" :value="1" />
<el-option label="盆栽" :value="2" />
<el-option label="种子" :value="3" />
<el-option label="工具" :value="4" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 商品列表 -->
<el-table :data="productList" border style="width: 100%" v-loading="loading">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="name" label="商品名称" />
<el-table-column prop="category_name" label="分类" width="100" />
<el-table-column prop="price" label="价格" width="100">
<template #default="scope">
¥{{ scope.row.price }}
</template>
</el-table-column>
<el-table-column prop="stock" label="库存" width="100" />
<el-table-column label="图片" width="120">
<template #default="scope">
<el-image
:src="scope.row.image"
class="product-image"
:preview-src-list="[scope.row.image]"
fit="cover"
/>
</template>
</el-table-column>
<el-table-column prop="created_at" label="创建时间" width="180">
<template #default="scope">
{{ formatDate(scope.row.created_at) }}
</template>
</el-table-column>
<el-table-column label="状态" width="100">
<template #default="scope">
<el-tag v-if="scope.row.status === 1" type="success">上架</el-tag>
<el-tag v-else type="danger">下架</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.limit"
:page-sizes="[12, 24, 48, 96]"
:total="pagination.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
class="pagination"
/>
<!-- 商品编辑对话框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px">
<el-form :model="editForm" :rules="editRules" ref="editFormRef" label-width="80px">
<el-form-item label="商品名称" prop="name">
<el-input v-model="editForm.name" />
</el-form-item>
<el-form-item label="分类" prop="category_id">
<el-select v-model="editForm.category_id" placeholder="请选择">
<el-option label="鲜花" :value="1" />
<el-option label="盆栽" :value="2" />
<el-option label="种子" :value="3" />
<el-option label="工具" :value="4" />
</el-select>
</el-form-item>
<el-form-item label="价格" prop="price">
<el-input-number v-model="editForm.price" :min="0" :step="0.1" controls-position="right" />
</el-form-item>
<el-form-item label="库存" prop="stock">
<el-input-number v-model="editForm.stock" :min="0" controls-position="right" />
</el-form-item>
<el-form-item label="图片" prop="image">
<el-upload
class="image-uploader"
action="/api/v1/upload"
:show-file-list="false"
:on-success="handleImageSuccess"
:before-upload="beforeImageUpload"
>
<img v-if="editForm.image" :src="editForm.image" class="image-preview" />
<el-icon v-else class="image-uploader-icon"><plus /></el-icon>
</el-upload>
</el-form-item>
<el-form-item label="描述" prop="description">
<el-input v-model="editForm.description" type="textarea" :rows="3" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-switch
v-model="editForm.status"
:active-value="1"
:inactive-value="0"
active-text="上架"
inactive-text="下架"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</span>
</template>
</el-dialog>
</el-card>
</div>
</template>
<script>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { productAPI } from '../utils/api'
export default {
name: 'Products',
setup() {
const loading = ref(false)
const dialogVisible = ref(false)
const dialogTitle = ref('')
const editFormRef = ref()
const searchForm = reactive({
keyword: '',
category_id: null
})
const editForm = reactive({
id: null,
name: '',
category_id: 1,
price: 0,
stock: 0,
image: '',
description: '',
status: 1
})
const editRules = {
name: [
{ required: true, message: '请输入商品名称', trigger: 'blur' }
],
category_id: [
{ required: true, message: '请选择分类', trigger: 'change' }
],
price: [
{ required: true, message: '请输入价格', trigger: 'blur' }
],
stock: [
{ required: true, message: '请输入库存', trigger: 'blur' }
]
}
const productList = ref([])
const pagination = reactive({
page: 1,
limit: 12,
total: 0
})
// 格式化日期
const formatDate = (dateString) => {
if (!dateString) return ''
const date = new Date(dateString)
return date.toLocaleString('zh-CN')
}
// 获取商品列表
const fetchProducts = async () => {
loading.value = true
try {
const params = {
page: pagination.page,
limit: pagination.limit,
keyword: searchForm.keyword,
category_id: searchForm.category_id
}
const response = await productAPI.getProducts(params)
if (response.code === 200) {
productList.value = response.data.products
pagination.total = response.data.pagination.total
} else {
ElMessage.error(response.message || '获取商品列表失败')
}
} catch (error) {
ElMessage.error('获取商品列表失败: ' + error.message)
} finally {
loading.value = false
}
}
// 搜索
const handleSearch = () => {
pagination.page = 1
fetchProducts()
}
// 重置搜索
const handleReset = () => {
searchForm.keyword = ''
searchForm.category_id = null
pagination.page = 1
fetchProducts()
}
// 新增商品
const handleAddProduct = () => {
dialogTitle.value = '新增商品'
dialogVisible.value = true
// 重置表单
Object.assign(editForm, {
id: null,
name: '',
category_id: 1,
price: 0,
stock: 0,
image: '',
description: '',
status: 1
})
}
// 编辑商品
const handleEdit = (row) => {
dialogTitle.value = '编辑商品'
dialogVisible.value = true
// 填充表单数据
Object.assign(editForm, {
id: row.id,
name: row.name,
category_id: row.category_id,
price: row.price,
stock: row.stock,
image: row.image,
description: row.description,
status: row.status
})
}
// 删除商品
const handleDelete = (row) => {
ElMessageBox.confirm(
`确定要删除商品 "${row.name}" 吗?`,
'确认删除',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(async () => {
try {
const response = await productAPI.deleteProduct(row.id)
if (response.code === 200) {
ElMessage.success('删除成功')
fetchProducts()
} else {
ElMessage.error(response.message || '删除失败')
}
} catch (error) {
ElMessage.error('删除失败: ' + error.message)
}
}).catch(() => {
// 用户取消删除
})
}
// 提交表单
const submitForm = async () => {
if (!editFormRef.value) return
const valid = await editFormRef.value.validate()
if (!valid) return
try {
let response
if (editForm.id) {
// 更新商品
response = await productAPI.updateProduct(editForm.id, editForm)
} else {
// 创建商品
response = await productAPI.createProduct(editForm)
}
if (response.code === 200 || response.code === 201) {
ElMessage.success(editForm.id ? '更新成功' : '创建成功')
dialogVisible.value = false
fetchProducts()
} else {
ElMessage.error(response.message || (editForm.id ? '更新失败' : '创建失败'))
}
} catch (error) {
ElMessage.error((editForm.id ? '更新失败' : '创建失败') + ': ' + error.message)
}
}
// 图片上传相关
const handleImageSuccess = (response, file) => {
// 实际项目中需要根据接口返回格式处理
editForm.image = response.data.url || URL.createObjectURL(file.raw)
}
const beforeImageUpload = (file) => {
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png'
const isLt2M = file.size / 1024 / 1024 < 2
if (!isJPG) {
ElMessage.error('上传头像图片只能是 JPG/PNG 格式!')
}
if (!isLt2M) {
ElMessage.error('上传头像图片大小不能超过 2MB!')
}
return isJPG && isLt2M
}
// 分页相关
const handleSizeChange = (val) => {
pagination.limit = val
pagination.page = 1
fetchProducts()
}
const handleCurrentChange = (val) => {
pagination.page = val
fetchProducts()
}
onMounted(() => {
fetchProducts()
})
return {
loading,
dialogVisible,
dialogTitle,
editFormRef,
searchForm,
editForm,
editRules,
productList,
pagination,
formatDate,
handleSearch,
handleReset,
handleAddProduct,
handleEdit,
handleDelete,
submitForm,
handleImageSuccess,
beforeImageUpload,
handleSizeChange,
handleCurrentChange
}
}
}
</script>
<style scoped>
.products-container {
padding: 20px;
}
.products-card {
border: none;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.search-form {
margin-bottom: 20px;
}
.product-image {
width: 80px;
height: 80px;
border-radius: 4px;
}
.image-uploader .image-preview {
width: 100%;
height: 178px;
display: block;
}
.image-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.image-uploader .el-upload:hover {
border-color: #409eff;
}
.image-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
line-height: 178px;
}
.pagination {
margin-top: 20px;
display: flex;
justify-content: center;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,454 @@
<template>
<div class="statistics-container">
<el-card class="statistics-card">
<template #header>
<div class="card-header">
<span>数据统计</span>
</div>
</template>
<!-- 统计概览 -->
<el-row :gutter="20" class="stats-overview">
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon users">
<el-icon><user /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ statsData.userCount || 0 }}</div>
<div class="stat-label">总用户数</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon products">
<el-icon><goods /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ statsData.productCount || 0 }}</div>
<div class="stat-label">商品总数</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon orders">
<el-icon><document /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ statsData.orderCount || 0 }}</div>
<div class="stat-label">订单总数</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon revenue">
<el-icon><money /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">¥{{ statsData.totalRevenue || 0 }}</div>
<div class="stat-label">总销售额</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 图表区域 -->
<el-row :gutter="20" class="charts-section">
<el-col :span="24">
<el-card class="chart-card">
<template #header>
<div class="chart-header">
<h3>用户增长趋势</h3>
<el-select v-model="userChartRange" size="small" style="width: 120px;" @change="fetchUserChartData">
<el-option label="近7天" value="7" />
<el-option label="近30天" value="30" />
<el-option label="近90天" value="90" />
</el-select>
</div>
</template>
<div ref="userChart" class="chart-container"></div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="chart-card">
<template #header>
<h3>商品分类分布</h3>
</template>
<div ref="categoryChart" class="chart-container"></div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="chart-card">
<template #header>
<h3>订单状态分布</h3>
</template>
<div ref="orderStatusChart" class="chart-container"></div>
</el-card>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script>
import { ref, reactive, onMounted } from 'vue'
import * as echarts from 'echarts'
import { statisticsAPI } from '../utils/api'
export default {
name: 'Statistics',
setup() {
const userChart = ref(null)
const categoryChart = ref(null)
const orderStatusChart = ref(null)
const userChartInstance = ref(null)
const categoryChartInstance = ref(null)
const orderStatusChartInstance = ref(null)
const userChartRange = ref('30')
const statsData = reactive({
userCount: 0,
productCount: 0,
orderCount: 0,
totalRevenue: 0
})
const chartData = reactive({
userGrowth: [],
categoryDistribution: [],
orderStatusDistribution: []
})
// 获取统计数据
const fetchStatsData = async () => {
try {
// 模拟数据
statsData.userCount = 1234
statsData.productCount = 567
statsData.orderCount = 890
statsData.totalRevenue = 123456.78
} catch (error) {
console.error('获取统计数据失败:', error)
}
}
// 获取用户图表数据
const fetchUserChartData = async () => {
try {
// 模拟数据
chartData.userGrowth = {
dates: ['1月1日', '1月2日', '1月3日', '1月4日', '1月5日', '1月6日', '1月7日'],
counts: [10, 25, 15, 30, 20, 35, 40]
}
renderUserChart()
} catch (error) {
console.error('获取用户图表数据失败:', error)
}
}
// 获取分类图表数据
const fetchCategoryChartData = async () => {
try {
// 模拟数据
chartData.categoryDistribution = [
{ name: '鲜花', value: 45 },
{ name: '盆栽', value: 30 },
{ name: '种子', value: 15 },
{ name: '工具', value: 10 }
]
renderCategoryChart()
} catch (error) {
console.error('获取分类图表数据失败:', error)
}
}
// 获取订单状态图表数据
const fetchOrderStatusChartData = async () => {
try {
// 模拟数据
chartData.orderStatusDistribution = [
{ name: '待支付', value: 20 },
{ name: '已支付', value: 50 },
{ name: '已发货', value: 15 },
{ name: '已完成', value: 10 },
{ name: '已取消', value: 5 }
]
renderOrderStatusChart()
} catch (error) {
console.error('获取订单状态图表数据失败:', error)
}
}
// 渲染用户增长趋势图
const renderUserChart = () => {
if (userChart.value) {
if (!userChartInstance.value) {
userChartInstance.value = echarts.init(userChart.value)
}
const option = {
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: chartData.userGrowth.dates
},
yAxis: {
type: 'value'
},
series: [{
data: chartData.userGrowth.counts,
type: 'line',
smooth: true,
areaStyle: {}
}]
}
userChartInstance.value.setOption(option)
}
}
// 渲染商品分类分布图
const renderCategoryChart = () => {
if (categoryChart.value) {
if (!categoryChartInstance.value) {
categoryChartInstance.value = echarts.init(categoryChart.value)
}
const option = {
tooltip: {
trigger: 'item'
},
legend: {
top: 'bottom'
},
series: [{
name: '商品分类',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '18',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: chartData.categoryDistribution
}]
}
categoryChartInstance.value.setOption(option)
}
}
// 渲染订单状态分布图
const renderOrderStatusChart = () => {
if (orderStatusChart.value) {
if (!orderStatusChartInstance.value) {
orderStatusChartInstance.value = echarts.init(orderStatusChart.value)
}
const option = {
tooltip: {
trigger: 'item'
},
legend: {
top: 'bottom'
},
series: [{
name: '订单状态',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '18',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: chartData.orderStatusDistribution
}]
}
orderStatusChartInstance.value.setOption(option)
}
}
// 窗口大小改变时重绘图表
const handleResize = () => {
if (userChartInstance.value) {
userChartInstance.value.resize()
}
if (categoryChartInstance.value) {
categoryChartInstance.value.resize()
}
if (orderStatusChartInstance.value) {
orderStatusChartInstance.value.resize()
}
}
onMounted(() => {
fetchStatsData()
fetchUserChartData()
fetchCategoryChartData()
fetchOrderStatusChartData()
window.addEventListener('resize', handleResize)
})
return {
userChart,
categoryChart,
orderStatusChart,
userChartRange,
statsData,
fetchUserChartData
}
}
}
</script>
<style scoped>
.statistics-container {
padding: 20px;
}
.statistics-card {
border: none;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.stats-overview {
margin-bottom: 24px;
}
.stat-card {
border: none;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.stat-content {
display: flex;
align-items: center;
}
.stat-icon {
width: 48px;
height: 48px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16px;
font-size: 20px;
}
.stat-icon.users {
background: #e8f5e8;
color: #4CAF50;
}
.stat-icon.products {
background: #e3f2fd;
color: #2196F3;
}
.stat-icon.orders {
background: #fff3e0;
color: #FF9800;
}
.stat-icon.revenue {
background: #fce4ec;
color: #E91E63;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #303133;
line-height: 1;
}
.stat-label {
color: #606266;
font-size: 14px;
margin-top: 4px;
}
.charts-section {
margin-bottom: 24px;
}
.chart-card {
border: none;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
height: 400px;
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.chart-header h3 {
margin: 0;
color: #303133;
}
.chart-container {
height: 340px;
width: 100%;
}
</style>

View File

@@ -0,0 +1,365 @@
<template>
<div class="users-container">
<el-card class="users-card">
<template #header>
<div class="card-header">
<span>用户管理</span>
<el-button type="primary" @click="handleAddUser">新增用户</el-button>
</div>
</template>
<!-- 搜索条件 -->
<el-form :inline="true" :model="searchForm" class="search-form">
<el-form-item label="关键词">
<el-input v-model="searchForm.keyword" placeholder="用户名/手机号/邮箱" />
</el-form-item>
<el-form-item label="用户类型">
<el-select v-model="searchForm.user_type" clearable placeholder="请选择">
<el-option label="农户" value="farmer" />
<el-option label="买家" value="buyer" />
<el-option label="管理员" value="admin" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 用户列表 -->
<el-table :data="userList" border style="width: 100%" v-loading="loading">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="username" label="用户名" />
<el-table-column prop="phone" label="手机号" />
<el-table-column prop="email" label="邮箱" />
<el-table-column prop="user_type" label="用户类型">
<template #default="scope">
<el-tag v-if="scope.row.user_type === 'farmer'">农户</el-tag>
<el-tag v-else-if="scope.row.user_type === 'buyer'" type="success">买家</el-tag>
<el-tag v-else-if="scope.row.user_type === 'admin'" type="danger">管理员</el-tag>
<el-tag v-else>{{ scope.row.user_type }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="created_at" label="注册时间" width="180">
<template #default="scope">
{{ formatDate(scope.row.created_at) }}
</template>
</el-table-column>
<el-table-column prop="last_login" label="最后登录" width="180">
<template #default="scope">
{{ scope.row.last_login ? formatDate(scope.row.last_login) : '从未登录' }}
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row)" :disabled="scope.row.user_type === 'admin'">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.limit"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
class="pagination"
/>
<!-- 用户编辑对话框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px">
<el-form :model="editForm" :rules="editRules" ref="editFormRef" label-width="80px">
<el-form-item label="用户名" prop="username">
<el-input v-model="editForm.username" :disabled="!!editForm.id" />
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="editForm.phone" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="editForm.email" />
</el-form-item>
<el-form-item label="用户类型" prop="user_type">
<el-select v-model="editForm.user_type" placeholder="请选择">
<el-option label="农户" value="farmer" />
<el-option label="买家" value="buyer" />
<el-option label="管理员" value="admin" />
</el-select>
</el-form-item>
<el-form-item v-if="!editForm.id" label="密码" prop="password">
<el-input v-model="editForm.password" type="password" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</span>
</template>
</el-dialog>
</el-card>
</div>
</template>
<script>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { userAPI } from '../utils/api'
export default {
name: 'Users',
setup() {
const loading = ref(false)
const dialogVisible = ref(false)
const dialogTitle = ref('')
const editFormRef = ref()
const searchForm = reactive({
keyword: '',
user_type: ''
})
const editForm = reactive({
id: null,
username: '',
phone: '',
email: '',
user_type: 'farmer',
password: ''
})
const editRules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' }
],
email: [
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
],
user_type: [
{ required: true, message: '请选择用户类型', trigger: 'change' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码长度不能少于6位', trigger: 'blur' }
]
}
const userList = ref([])
const pagination = reactive({
page: 1,
limit: 10,
total: 0
})
// 格式化日期
const formatDate = (dateString) => {
if (!dateString) return ''
const date = new Date(dateString)
return date.toLocaleString('zh-CN')
}
// 获取用户列表
const fetchUsers = async () => {
loading.value = true
try {
const params = {
page: pagination.page,
limit: pagination.limit,
keyword: searchForm.keyword,
user_type: searchForm.user_type
}
const response = await userAPI.getUsers(params)
if (response.code === 200) {
userList.value = response.data.users
pagination.total = response.data.pagination.total
} else {
ElMessage.error(response.message || '获取用户列表失败')
}
} catch (error) {
ElMessage.error('获取用户列表失败: ' + error.message)
} finally {
loading.value = false
}
}
// 搜索
const handleSearch = () => {
pagination.page = 1
fetchUsers()
}
// 重置搜索
const handleReset = () => {
searchForm.keyword = ''
searchForm.user_type = ''
pagination.page = 1
fetchUsers()
}
// 新增用户
const handleAddUser = () => {
dialogTitle.value = '新增用户'
dialogVisible.value = true
// 重置表单
Object.assign(editForm, {
id: null,
username: '',
phone: '',
email: '',
user_type: 'farmer',
password: ''
})
}
// 编辑用户
const handleEdit = (row) => {
dialogTitle.value = '编辑用户'
dialogVisible.value = true
// 填充表单数据
Object.assign(editForm, {
id: row.id,
username: row.username,
phone: row.phone,
email: row.email,
user_type: row.user_type,
password: ''
})
}
// 删除用户
const handleDelete = (row) => {
ElMessageBox.confirm(
`确定要删除用户 "${row.username}" 吗?`,
'确认删除',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(async () => {
try {
const response = await userAPI.deleteUser(row.id)
if (response.code === 200) {
ElMessage.success('删除成功')
fetchUsers()
} else {
ElMessage.error(response.message || '删除失败')
}
} catch (error) {
ElMessage.error('删除失败: ' + error.message)
}
}).catch(() => {
// 用户取消删除
})
}
// 提交表单
const submitForm = async () => {
if (!editFormRef.value) return
const valid = await editFormRef.value.validate()
if (!valid) return
try {
let response
if (editForm.id) {
// 更新用户
response = await userAPI.updateUser(editForm.id, {
email: editForm.email,
user_type: editForm.user_type
})
} else {
// 创建用户(这里简化处理,实际应该调用注册接口)
ElMessage.warning('演示版本,暂不支持创建用户')
dialogVisible.value = false
return
}
if (response.code === 200) {
ElMessage.success(editForm.id ? '更新成功' : '创建成功')
dialogVisible.value = false
fetchUsers()
} else {
ElMessage.error(response.message || (editForm.id ? '更新失败' : '创建失败'))
}
} catch (error) {
ElMessage.error((editForm.id ? '更新失败' : '创建失败') + ': ' + error.message)
}
}
// 分页相关
const handleSizeChange = (val) => {
pagination.limit = val
pagination.page = 1
fetchUsers()
}
const handleCurrentChange = (val) => {
pagination.page = val
fetchUsers()
}
onMounted(() => {
fetchUsers()
})
return {
loading,
dialogVisible,
dialogTitle,
editFormRef,
searchForm,
editForm,
editRules,
userList,
pagination,
formatDate,
handleSearch,
handleReset,
handleAddUser,
handleEdit,
handleDelete,
submitForm,
handleSizeChange,
handleCurrentChange
}
}
}
</script>
<style scoped>
.users-container {
padding: 20px;
}
.users-card {
border: none;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.search-form {
margin-bottom: 20px;
}
.pagination {
margin-top: 20px;
display: flex;
justify-content: center;
}
</style>

View File

@@ -1,6 +1,6 @@
# 应用配置
NODE_ENV=development
PORT=3000
PORT=3200
JWT_SECRET=your-super-secret-jwt-key-change-in-production
# 数据库配置

View File

@@ -6,10 +6,10 @@ PORT=3200
APP_NAME=爱鉴花小程序后端
# MySQL数据库配置
DB_HOST=192.168.0.240
DB_PORT=3306
DB_HOST=129.211.213.226
DB_PORT=9527
DB_USERNAME=root
DB_PASSWORD=aiot$Aiot123
DB_PASSWORD=aiotAiot123!
DB_DATABASE=ajhdata
# Redis配置

View File

@@ -7,10 +7,10 @@ PORT=3000
APP_NAME=爱鉴花小程序后端
# MySQL数据库配置
DB_HOST=192.168.0.240
DB_PORT=3306
DB_HOST=129.211.213.226
DB_PORT=9527
DB_USERNAME=root
DB_PASSWORD=aiot$Aiot123
DB_PASSWORD=aiotAiot123!
DB_DATABASE=ajhdata
# Redis配置

View File

@@ -6,7 +6,7 @@
## 基础信息
- **基础URL**: `http://localhost:3000/api/v1`
- **基础URL**: `http://localhost:3200/api/v1`
- **认证方式**: Bearer Token (JWT)
- **数据格式**: JSON
- **字符编码**: UTF-8

View File

@@ -6,7 +6,8 @@
## 技术栈
- Node.js
- Express.js
- MongoDB
- SQLite开发环境
- MySQL生产环境
- Redis
## 文件结构

View File

@@ -2,14 +2,14 @@
## 📋 数据库连接信息
### 测试环境
- **主机**: 192.168.0.240
- **端口**: 3306
### 开发环境 (MySQL)
- **主机**: 129.211.213.226
- **端口**: 9527
- **用户名**: root
- **密码**: aiot$Aiot123
- **密码**: aiotAiot123!
- **数据库**: ajhdata
### 生产环境
### 生产环境 (MySQL)
- **主机**: 129.211.213.226
- **端口**: 9527
- **用户名**: root

View File

@@ -22,13 +22,14 @@ const userRoutes = require('./routes/users');
const productRoutes = require('./routes/products');
const orderRoutes = require('./routes/orders');
const identificationRoutes = require('./routes/identifications');
const adminRoutes = require('./routes/admin');
// 导入中间件
const { errorHandler } = require('./middlewares/errorHandler');
const { authMiddleware } = require('./middlewares/auth');
const app = express();
const PORT = process.env.PORT || 3000;
const PORT = process.env.PORT || 3201;
// 安全中间件
app.use(helmet());
@@ -75,6 +76,7 @@ app.use('/api/v1/users', authMiddleware, userRoutes);
app.use('/api/v1/products', productRoutes);
app.use('/api/v1/orders', authMiddleware, orderRoutes);
app.use('/api/v1/identifications', authMiddleware, identificationRoutes);
app.use('/api/v1/admin', authMiddleware, adminRoutes);
// 404处理
app.use('*', (req, res) => {

View File

@@ -4,12 +4,12 @@
*/
const databaseConfig = {
// 测试环境配置
// 开发环境配置
development: {
host: '192.168.0.240',
port: 3306,
host: '129.211.213.226',
port: 9527,
username: 'root',
password: 'aiot$Aiot123',
password: 'aiotAiot123!',
database: 'ajhdata',
dialect: 'mysql',
logging: console.log,

View File

@@ -21,7 +21,7 @@ const options = {
{
url: process.env.NODE_ENV === 'production'
? 'https://api.aijianhua.com'
: `http://localhost:${process.env.PORT || 3000}`,
: `http://localhost:${process.env.PORT || 3200}`,
description: process.env.NODE_ENV === 'production' ? '生产环境' : '开发环境'
}
],

BIN
backend/database.sqlite Normal file

Binary file not shown.

5309
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,49 +5,43 @@
"main": "app.js",
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js",
"test": "jest",
"lint": "eslint .",
"dev": "NODE_ENV=development PORT=3200 nodemon app.js",
"prod": "NODE_ENV=production node app.js",
"db:init": "node scripts/initDatabase.js",
"db:check": "node scripts/initDatabase.js --check",
"db:migrate": "node scripts/migrate.js",
"db:seed": "node scripts/seedData.js"
"test": "jest",
"docs": "node scripts/generateDocs.js"
},
"dependencies": {
"express": "^4.18.2",
"mysql2": "^3.6.0",
"cors": "^2.8.5",
"helmet": "^7.0.0",
"compression": "^1.7.4",
"morgan": "^1.10.0",
"dotenv": "^16.3.1",
"jsonwebtoken": "^9.0.2",
"bcryptjs": "^2.4.3",
"validator": "^13.11.0",
"compression": "^1.7.4",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"helmet": "^6.1.5",
"jsonwebtoken": "^9.0.2",
"morgan": "^1.10.0",
"multer": "^1.4.5-lts.1",
"mysql2": "^3.6.0",
"redis": "^4.6.8",
"socket.io": "^4.7.2",
"sqlite3": "^5.1.7",
"swagger-ui-express": "^4.6.3",
"validator": "^13.11.0",
"yamljs": "^0.3.0"
},
"devDependencies": {
"nodemon": "^3.0.1",
"jest": "^29.6.2",
"eslint": "^8.47.0",
"prettier": "^3.0.2",
"@types/node": "^20.5.0",
"typescript": "^5.1.6"
"nodemon": "^2.0.22"
},
"keywords": [
"wechat",
"mini-program",
"plant-identification",
"ecommerce",
"api"
"aijianhua",
"plant",
"identification",
"api",
"express"
],
"author": "爱鉴花开发团队",
"author": "爱鉴花团队",
"license": "MIT",
"engines": {
"node": ">=16.0.0"
"node": ">=14.0.0"
}
}
}

265
backend/routes/admin.js Normal file
View File

@@ -0,0 +1,265 @@
const express = require('express');
const dbConnector = require('../utils/dbConnector');
const { adminRequired } = require('../middlewares/auth');
const { asyncHandler } = require('../middlewares/errorHandler');
const router = express.Router();
/**
* 获取系统统计概览
*/
router.get('/statistics/overview', adminRequired, asyncHandler(async (req, res) => {
// 获取总用户数
const totalUsersResult = await dbConnector.query(
'SELECT COUNT(*) as total FROM users WHERE status = 1'
);
// 获取今日新增用户数
const newUsersTodayResult = await dbConnector.query(
'SELECT COUNT(*) as total FROM users WHERE status = 1 AND DATE(created_at) = CURDATE()'
);
// 获取总订单数
const totalOrdersResult = await dbConnector.query(
'SELECT COUNT(*) as total FROM orders'
);
// 获取总收入
const totalRevenueResult = await dbConnector.query(
'SELECT COALESCE(SUM(total_amount), 0) as total FROM orders WHERE payment_status = 1'
);
// 获取总识别次数
const totalIdentificationsResult = await dbConnector.query(
'SELECT COUNT(*) as total FROM identifications'
);
// 获取今日活跃用户数
const activeUsersTodayResult = await dbConnector.query(
`SELECT COUNT(DISTINCT user_id) as total FROM (
SELECT user_id FROM orders WHERE DATE(created_at) = CURDATE()
UNION
SELECT user_id FROM identifications WHERE DATE(created_at) = CURDATE()
) as active_users`
);
res.json({
code: 200,
message: '获取成功',
data: {
total_users: totalUsersResult[0].total,
new_users_today: newUsersTodayResult[0].total,
total_orders: totalOrdersResult[0].total,
total_revenue: parseFloat(totalRevenueResult[0].total),
total_identifications: totalIdentificationsResult[0].total,
active_users_today: activeUsersTodayResult[0].total
}
});
}));
/**
* 获取趋势数据
*/
router.get('/statistics/trend', adminRequired, asyncHandler(async (req, res) => {
const { days = 7, type = 'users' } = req.query;
const dayCount = parseInt(days);
let query = '';
let dataKey = '';
switch (type) {
case 'users':
query = `
SELECT DATE(created_at) as date, COUNT(*) as count
FROM users
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
GROUP BY DATE(created_at)
ORDER BY date
`;
dataKey = 'new_users';
break;
case 'orders':
query = `
SELECT DATE(created_at) as date, COUNT(*) as count
FROM orders
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
GROUP BY DATE(created_at)
ORDER BY date
`;
dataKey = 'new_orders';
break;
case 'revenue':
query = `
SELECT DATE(created_at) as date, COALESCE(SUM(total_amount), 0) as amount
FROM orders
WHERE payment_status = 1 AND created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
GROUP BY DATE(created_at)
ORDER BY date
`;
dataKey = 'revenue';
break;
case 'identifications':
query = `
SELECT DATE(created_at) as date, COUNT(*) as count
FROM identifications
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
GROUP BY DATE(created_at)
ORDER BY date
`;
dataKey = 'identifications';
break;
default:
return res.status(400).json({
code: 400,
message: '不支持的统计类型',
data: null
});
}
const trendData = await dbConnector.query(query, [dayCount]);
res.json({
code: 200,
message: '获取成功',
data: {
type,
days: dayCount,
trend: trendData,
total: trendData.reduce((sum, item) => sum + (item.count || item.amount || 0), 0)
}
});
}));
/**
* 获取系统配置列表
*/
router.get('/config', adminRequired, asyncHandler(async (req, res) => {
const configs = await dbConnector.query(
'SELECT config_key, config_value, config_type, description FROM system_config ORDER BY config_key'
);
res.json({
code: 200,
message: '获取成功',
data: configs
});
}));
/**
* 获取单个系统配置
*/
router.get('/config/:key', adminRequired, asyncHandler(async (req, res) => {
const { key } = req.params;
const configs = await dbConnector.query(
'SELECT config_key, config_value, config_type, description FROM system_config WHERE config_key = ?',
[key]
);
if (configs.length === 0) {
return res.status(404).json({
code: 404,
message: '配置项不存在',
data: null
});
}
res.json({
code: 200,
message: '获取成功',
data: configs[0]
});
}));
/**
* 更新系统配置
*/
router.put('/config/:key', adminRequired, asyncHandler(async (req, res) => {
const { key } = req.params;
const { value } = req.body;
if (value === undefined) {
return res.status(400).json({
code: 400,
message: '配置值不能为空',
data: null
});
}
const result = await dbConnector.query(
'UPDATE system_config SET config_value = ?, updated_at = CURRENT_TIMESTAMP WHERE config_key = ?',
[value, key]
);
if (result.affectedRows === 0) {
return res.status(404).json({
code: 404,
message: '配置项不存在',
data: null
});
}
res.json({
code: 200,
message: '更新成功',
data: null
});
}));
/**
* 获取操作日志
*/
router.get('/logs', adminRequired, asyncHandler(async (req, res) => {
const { page = 1, limit = 20, module, action, username } = req.query;
const offset = (page - 1) * limit;
let whereClause = 'WHERE 1=1';
let queryParams = [];
if (module) {
whereClause += ' AND module = ?';
queryParams.push(module);
}
if (action) {
whereClause += ' AND action = ?';
queryParams.push(action);
}
if (username) {
whereClause += ' AND username LIKE ?';
queryParams.push(`%${username}%`);
}
// 获取日志列表
const logs = await dbConnector.query(
`SELECT id, user_id, username, user_type, module, action, target_id,
description, ip_address, user_agent, created_at
FROM operation_logs ${whereClause}
ORDER BY created_at DESC
LIMIT ? OFFSET ?`,
[...queryParams, parseInt(limit), offset]
);
// 获取总数
const totalResult = await dbConnector.query(
`SELECT COUNT(*) as total FROM operation_logs ${whereClause}`,
queryParams
);
res.json({
code: 200,
message: '获取成功',
data: {
logs,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total: totalResult[0].total,
pages: Math.ceil(totalResult[0].total / limit)
}
}
});
}));
module.exports = router;

View File

@@ -66,7 +66,7 @@ router.post('/register', async (req, res, next) => {
// 创建用户
const result = await dbConnector.query(
'INSERT INTO users (username, password_hash, phone, email, user_type) VALUES (?, ?, ?, ?, ?)',
'INSERT INTO users (username, password, phone, email, user_type) VALUES (?, ?, ?, ?, ?)',
[username, hashedPassword, phone, email, user_type]
);
@@ -127,7 +127,7 @@ router.post('/login', async (req, res, next) => {
const userData = user[0];
// 验证密码
const isValidPassword = await bcrypt.compare(password, userData.password_hash);
const isValidPassword = await bcrypt.compare(password, userData.password);
if (!isValidPassword) {
return res.status(401).json({
code: 401,

View File

@@ -0,0 +1,260 @@
#!/usr/bin/env node
/**
* SQLite数据库初始化脚本
* 用于创建SQLite数据库表结构
*/
const sqlite3 = require('sqlite3').verbose();
const path = require('path');
const databaseConfig = require('../config/database');
class SQLiteInitializer {
constructor() {
this.db = null;
}
/**
* 创建数据库连接
*/
createConnection() {
try {
this.db = new sqlite3.Database(databaseConfig.storage);
console.log('✅ SQLite数据库连接成功');
return true;
} catch (error) {
console.error('❌ SQLite数据库连接失败:', error.message);
return false;
}
}
/**
* 执行SQL语句
*/
run(sql, params = []) {
return new Promise((resolve, reject) => {
this.db.run(sql, params, function(err) {
if (err) {
reject(err);
} else {
resolve(this);
}
});
});
}
/**
* 创建users表
*/
async createUsersTable() {
const sql = `
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
phone VARCHAR(20) UNIQUE,
email VARCHAR(100) UNIQUE,
user_type VARCHAR(20) DEFAULT 'user',
status INTEGER DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`;
try {
await this.run(sql);
console.log('✅ users表创建成功');
return true;
} catch (error) {
console.error('❌ 创建users表失败:', error.message);
return false;
}
}
/**
* 创建orders表
*/
async createOrdersTable() {
const sql = `
CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_number VARCHAR(50) UNIQUE NOT NULL,
user_id INTEGER NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
payment_status INTEGER DEFAULT 0,
shipping_status INTEGER DEFAULT 0,
shipping_address TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
)
`;
try {
await this.run(sql);
console.log('✅ orders表创建成功');
return true;
} catch (error) {
console.error('❌ 创建orders表失败:', error.message);
return false;
}
}
/**
* 创建identifications表
*/
async createIdentificationsTable() {
const sql = `
CREATE TABLE IF NOT EXISTS identifications (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
image_url VARCHAR(255) NOT NULL,
result TEXT NOT NULL,
confidence DECIMAL(5,4) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
)
`;
try {
await this.run(sql);
console.log('✅ identifications表创建成功');
return true;
} catch (error) {
console.error('❌ 创建identifications表失败:', error.message);
return false;
}
}
/**
* 创建products表
*/
async createProductsTable() {
const sql = `
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(100) NOT NULL,
category_id INTEGER,
price DECIMAL(10,2) NOT NULL,
stock INTEGER DEFAULT 0,
image VARCHAR(255),
description TEXT,
status INTEGER DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`;
try {
await this.run(sql);
console.log('✅ products表创建成功');
return true;
} catch (error) {
console.error('❌ 创建products表失败:', error.message);
return false;
}
}
/**
* 插入初始管理员用户
*/
async insertAdminUser() {
const sql = `
INSERT OR IGNORE INTO users (username, password, phone, email, user_type, status)
VALUES (?, ?, ?, ?, ?, ?)
`;
// 密码: admin123 (bcrypt加密)
const hashedPassword = '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi';
try {
await this.run(sql, [
'admin',
hashedPassword,
'13800138000',
'admin@example.com',
'admin',
1
]);
console.log('✅ 管理员用户创建成功');
return true;
} catch (error) {
console.error('❌ 创建管理员用户失败:', error.message);
return false;
}
}
/**
* 关闭数据库连接
*/
closeConnection() {
if (this.db) {
this.db.close();
console.log('🔌 数据库连接已关闭');
}
}
/**
* 主初始化方法
*/
async initialize() {
console.log('🚀 开始SQLite数据库初始化...');
console.log(`📋 环境: ${process.env.NODE_ENV || 'development'}`);
console.log(`🗄️ 数据库文件: ${databaseConfig.storage}`);
console.log('─'.repeat(50));
// 创建连接
const connected = this.createConnection();
if (!connected) {
process.exit(1);
}
// 创建表
const tablesCreated = await Promise.all([
this.createUsersTable(),
this.createOrdersTable(),
this.createIdentificationsTable(),
this.createProductsTable()
]);
if (tablesCreated.every(result => result)) {
console.log('✅ 所有表创建成功');
} else {
console.error('❌ 表创建过程中出现错误');
this.closeConnection();
process.exit(1);
}
// 插入初始数据
const adminCreated = await this.insertAdminUser();
if (!adminCreated) {
console.warn('⚠️ 管理员用户创建失败或已存在');
}
console.log('✅ SQLite数据库初始化完成');
this.closeConnection();
}
}
// 执行初始化
const initializer = new SQLiteInitializer();
// 处理命令行参数
const args = process.argv.slice(2);
if (args.includes('--help') || args.includes('-h')) {
console.log(`
使用方法: node scripts/initSQLite.js [选项]
选项:
--help, -h 显示帮助信息
示例:
node scripts/initSQLite.js # 完整初始化
`);
process.exit(0);
}
initializer.initialize().catch(error => {
console.error('❌ 初始化过程中发生错误:', error.message);
process.exit(1);
});

View File

@@ -4,12 +4,14 @@
*/
const mysql = require('mysql2/promise');
const sqlite3 = require('sqlite3').verbose();
const databaseConfig = require('../config/database');
class DBConnector {
constructor() {
this.pool = null;
this.connection = null;
this.db = null;
this.init();
}
@@ -18,25 +20,31 @@ class DBConnector {
*/
init() {
try {
this.pool = mysql.createPool({
host: databaseConfig.host,
port: databaseConfig.port,
user: databaseConfig.username,
password: databaseConfig.password,
database: databaseConfig.database,
connectionLimit: databaseConfig.pool.max,
acquireTimeout: databaseConfig.pool.acquire,
timeout: databaseConfig.pool.idle,
charset: 'utf8mb4',
timezone: '+08:00',
decimalNumbers: true,
supportBigNumbers: true,
bigNumberStrings: false
});
console.log('✅ 数据库连接池初始化成功');
if (databaseConfig.dialect === 'sqlite') {
// SQLite配置
this.db = new sqlite3.Database(databaseConfig.storage);
console.log('✅ SQLite数据库连接成功');
} else {
// MySQL配置
this.pool = mysql.createPool({
host: databaseConfig.host,
port: databaseConfig.port,
user: databaseConfig.username,
password: databaseConfig.password,
database: databaseConfig.database,
connectionLimit: databaseConfig.pool.max,
acquireTimeout: databaseConfig.pool.acquire,
timeout: databaseConfig.pool.idle,
charset: 'utf8mb4',
timezone: '+08:00',
decimalNumbers: true,
supportBigNumbers: true,
bigNumberStrings: false
});
console.log('✅ MySQL数据库连接池初始化成功');
}
} catch (error) {
console.error('❌ 数据库连接初始化失败:', error.message);
console.error('❌ 数据库连接初始化失败:', error.message);
throw error;
}
}
@@ -62,19 +70,36 @@ class DBConnector {
* @returns {Promise} 查询结果
*/
async query(sql, params = []) {
let connection;
try {
connection = await this.getConnection();
const [rows] = await connection.execute(sql, params);
return rows;
} catch (error) {
console.error('SQL执行失败:', error.message);
console.error('SQL:', sql);
console.error('参数:', params);
throw error;
} finally {
if (connection) {
connection.release();
if (databaseConfig.dialect === 'sqlite') {
// SQLite查询实现
return new Promise((resolve, reject) => {
this.db.all(sql, params, (err, rows) => {
if (err) {
console.error('❌ SQL执行失败:', err.message);
console.error('SQL:', sql);
console.error('参数:', params);
reject(err);
} else {
resolve(rows);
}
});
});
} else {
// MySQL查询实现
let connection;
try {
connection = await this.getConnection();
const [rows] = await connection.execute(sql, params);
return rows;
} catch (error) {
console.error('❌ SQL执行失败:', error.message);
console.error('SQL:', sql);
console.error('参数:', params);
throw error;
} finally {
if (connection) {
connection.release();
}
}
}
}

View File

@@ -0,0 +1,278 @@
# 爱鉴花后台管理系统 API 接口文档
基于 OpenAPI 3.0 规范
## 基础信息
- **Base URL**: `http://localhost:3200/api`
- **认证方式**: Bearer Token (JWT)
- **响应格式**: JSON
## 统一响应格式
所有接口都遵循以下响应格式:
```json
{
"code": 200,
"message": "操作成功",
"data": {}
}
```
## 认证接口
### 管理员登录
**POST** `/auth/login`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| login | string | 是 | 用户名/手机号/邮箱 |
| password | string | 是 | 密码 |
**响应示例**:
```json
{
"code": 200,
"message": "登录成功",
"data": {
"user_id": 1,
"username": "admin",
"user_type": "admin",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
```
## 用户管理接口
### 获取用户列表
**GET** `/users`
**权限要求**: 管理员
**查询参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| page | number | 否 | 页码默认1 |
| limit | number | 否 | 每页数量默认10 |
| keyword | string | 否 | 搜索关键词 |
| user_type | string | 否 | 用户类型过滤 |
**响应示例**:
```json
{
"code": 200,
"message": "获取成功",
"data": {
"users": [
{
"id": 1,
"username": "testuser",
"phone": "13800138000",
"email": "test@example.com",
"user_type": "farmer",
"created_at": "2024-01-01T00:00:00.000Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 100,
"pages": 10
}
}
}
```
### 获取用户详情
**GET** `/users/{id}`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | number | 是 | 用户ID |
### 删除用户
**DELETE** `/users/{id}`
**权限要求**: 管理员
## 商品管理接口
### 获取商品列表
**GET** `/products`
**查询参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| page | number | 否 | 页码默认1 |
| limit | number | 否 | 每页数量默认10 |
| category_id | number | 否 | 分类ID过滤 |
| min_price | number | 否 | 最低价格 |
| max_price | number | 否 | 最高价格 |
| status | number | 否 | 状态过滤 |
### 创建商品
**POST** `/products`
**权限要求**: 管理员
**请求体**:
```json
{
"name": "玫瑰花",
"category_id": 1,
"price": 29.9,
"stock": 100,
"image": "/uploads/roses.jpg",
"description": "新鲜玫瑰花束"
}
```
## 订单管理接口
### 获取订单列表
**GET** `/orders`
**权限要求**: 管理员
**查询参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| page | number | 否 | 页码默认1 |
| limit | number | 否 | 每页数量默认10 |
| status | number | 否 | 订单状态过滤 |
| start_date | string | 否 | 开始日期 |
| end_date | string | 否 | 结束日期 |
### 更新订单状态
**PUT** `/orders/{id}/status`
**权限要求**: 管理员
**请求体**:
```json
{
"payment_status": 1,
"shipping_status": 2
}
```
## 识别记录管理接口
### 获取识别记录列表
**GET** `/identifications`
**权限要求**: 管理员
**查询参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| page | number | 否 | 页码默认1 |
| limit | number | 否 | 每页数量默认10 |
| start_date | string | 否 | 开始日期 |
| end_date | string | 否 | 结束日期 |
| min_confidence | number | 否 | 最低置信度 |
## 数据统计接口
### 获取系统统计概览
**GET** `/admin/statistics/overview`
**权限要求**: 管理员
**响应示例**:
```json
{
"code": 200,
"message": "获取成功",
"data": {
"total_users": 1000,
"new_users_today": 10,
"total_orders": 500,
"total_revenue": 15000.50,
"total_identifications": 2000,
"active_users_today": 50
}
}
```
### 获取趋势数据
**GET** `/admin/statistics/trend`
**查询参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| days | number | 否 | 天数默认7 |
| type | string | 否 | 统计类型users/orders/revenue/identifications |
## 系统配置接口
### 获取系统配置
**GET** `/admin/config`
**权限要求**: 管理员
### 更新系统配置
**PUT** `/admin/config/{key}`
**权限要求**: 管理员
**请求体**:
```json
{
"value": "new_value"
}
```
## 错误码说明
| 错误码 | 说明 |
|--------|------|
| 200 | 成功 |
| 400 | 请求参数错误 |
| 401 | 未授权 |
| 403 | 权限不足 |
| 404 | 资源不存在 |
| 409 | 资源冲突 |
| 500 | 服务器内部错误 |
## 安全要求
1. 所有敏感操作需要管理员权限
2. API请求需要携带有效的JWT Token
3. 密码传输需要加密
4. 文件上传需要验证文件类型和大小
---
*文档版本: 1.0.0*
*最后更新: 2024-01-01*

View File

@@ -75,61 +75,56 @@
<div class="row align-items-center">
<div class="col-lg-6">
<h2 class="display-5 fw-bold text-dark mb-4">我们的故事</h2>
<p class="lead text-muted mb-4">
爱鉴花成立于2023年是一家专注于人工智能花卉识别技术的创新企业。
我们致力于通过先进的AI技术让每个人都能轻松识别和了解身边的植物。
</p>
<p class="text-muted">
我们的团队由资深的人工智能专家、植物学家和产品设计师组成,
共同打造了这款智能花卉识别平台。通过深度学习和大数据分析,
我们能够准确识别数千种花卉植物,并提供详细的养护知识。
</p>
<p class="lead">爱鉴花成立于2023年由一群热爱植物和AI技术的专家组成。</p>
<p>我们致力于通过先进的AI技术让植物识别变得简单有趣帮助更多人了解和欣赏自然之美。我们的团队由资深人工智能专家、植物学家和用户体验设计师组成共同打造了这款智能花卉识别平台。</p>
<p>我们的使命是通过技术创新,连接人与自然,让更多人能够轻松识别和了解身边的植物世界。</p>
</div>
<div class="col-lg-6">
<img src="images/about-1.jpg" alt="公司环境" class="img-fluid rounded shadow lazy-load" loading="lazy">
<img src="images/about-company.jpg" alt="公司团队" class="img-fluid rounded shadow lazy-load" loading="lazy">
</div>
</div>
</div>
</section>
<!-- 使命愿景 -->
<!-- 企业文化 -->
<section class="bg-light py-5">
<div class="container py-5">
<div class="row">
<div class="col-lg-4 mb-4">
<div class="card border-0 text-center p-4">
<div class="feature-icon bg-primary text-white rounded-circle mx-auto mb-3">
<i class="fas fa-bullseye fa-2x"></i>
<div class="text-center mb-5">
<h2 class="display-5 fw-bold text-dark mb-3">企业文化</h2>
<p class="lead text-muted">我们的价值观和愿景</p>
</div>
<div class="row g-4">
<div class="col-md-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-body text-center p-4">
<div class="feature-icon bg-primary text-white rounded-circle mx-auto mb-3">
<i class="fas fa-bullseye fa-2x"></i>
</div>
<h5 class="card-title">使命</h5>
<p class="card-text text-muted">通过AI技术连接人与自然让植物识别变得简单有趣。</p>
</div>
<h4 class="mb-3">我们的使命</h4>
<p class="text-muted">
通过人工智能技术,让植物识别变得简单有趣,
帮助人们更好地了解和保护自然环境。
</p>
</div>
</div>
<div class="col-lg-4 mb-4">
<div class="card border-0 text-center p-4">
<div class="feature-icon bg-success text-white rounded-circle mx-auto mb-3">
<i class="fas fa-eye fa-2x"></i>
<div class="col-md-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-body text-center p-4">
<div class="feature-icon bg-success text-white rounded-circle mx-auto mb-3">
<i class="fas fa-eye fa-2x"></i>
</div>
<h5 class="card-title">愿景</h5>
<p class="card-text text-muted">成为全球领先的植物识别平台,推动植物科普教育发展。</p>
</div>
<h4 class="mb-3">我们的愿景</h4>
<p class="text-muted">
成为全球领先的植物识别平台,
构建连接人与自然的知识桥梁。
</p>
</div>
</div>
<div class="col-lg-4 mb-4">
<div class="card border-0 text-center p-4">
<div class="feature-icon bg-info text-white rounded-circle mx-auto mb-3">
<i class="fas fa-handshake fa-2x"></i>
<div class="col-md-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-body text-center p-4">
<div class="feature-icon bg-info text-white rounded-circle mx-auto mb-3">
<i class="fas fa-heart fa-2x"></i>
</div>
<h5 class="card-title">价值观</h5>
<p class="card-text text-muted">创新、专业、责任、合作,致力于为用户提供最佳体验。</p>
</div>
<h4 class="mb-3">我们的价值观</h4>
<p class="text-muted">
创新、专业、用户至上、环境保护,
这些是我们始终坚持的核心价值观。
</p>
</div>
</div>
</div>
@@ -143,28 +138,28 @@
<div class="row">
<div class="col-12">
<div class="timeline">
<div class="timeline-item">
<div class="timeline-content">
<h5>2023年1月</h5>
<p>公司成立开始AI花卉识别技术研发</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-content">
<h5>2023年6月</h5>
<p>完成首个花卉识别模型准确率达到85%</p>
<p>项目启动,爱鉴花项目正式启动,组建核心团队</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-content">
<h5>2023年9月</h5>
<p>微信小程序上线,获得首批用户</p>
<p>技术验证完成AI识别技术验证准确率达到预期目标</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-content">
<h5>2023年12月</h5>
<p>内测版本,发布内测版本,邀请植物专家参与测试</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-content">
<h5>2024年1月</h5>
<p>识别准确率提升至95%用户突破10万</p>
<p>正式上线,微信小程序正式上线,面向公众开放使用</p>
</div>
</div>
</div>
@@ -178,51 +173,39 @@
<div class="container py-5">
<h2 class="display-5 fw-bold text-dark text-center mb-5">核心团队</h2>
<div class="row">
<div class="col-lg-3 col-md-6 mb-4">
<div class="card team-card border-0 text-center">
<img src="images/team-1.jpg" alt="CEO" class="card-img-top rounded-circle mx-auto mt-4 lazy-load" style="width: 120px; height: 120px; object-fit: cover;" loading="lazy">
<div class="col-md-3">
<div class="card team-card border-0 shadow-sm text-center">
<img src="images/team-1.jpg" class="card-img-top team-img lazy-load" alt="张伟" loading="lazy">
<div class="card-body">
<h5 class="card-title"></h5>
<p class="text-muted">CEO/创始人</p>
<p class="card-text text-muted small">
前Google AI工程师10年人工智能领域经验
</p>
<h5 class="card-title"></h5>
<p class="text-muted">创始人 & CEO</p>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6 mb-4">
<div class="card team-card border-0 text-center">
<img src="images/team-2.jpg" alt="CTO" class="card-img-top rounded-circle mx-auto mt-4 lazy-load" style="width: 120px; height: 120px; object-fit: cover;" loading="lazy">
<div class="col-md-3">
<div class="card team-card border-0 shadow-sm text-center">
<img src="images/team-2.jpg" class="card-img-top team-img lazy-load" alt="李娜" loading="lazy">
<div class="card-body">
<h5 class="card-title"></h5>
<p class="text-muted">CTO</p>
<p class="card-text text-muted small">
深度学习专家,专注于计算机视觉技术
</p>
<h5 class="card-title"></h5>
<p class="text-muted">AI技术总监</p>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6 mb-4">
<div class="card team-card border-0 text-center">
<img src="images/team-3.jpg" alt="植物学家" class="card-img-top rounded-circle mx-auto mt-4 lazy-load" style="width: 120px; height: 120px; object-fit: cover;" loading="lazy">
<div class="col-md-3">
<div class="card team-card border-0 shadow-sm text-center">
<img src="images/team-3.jpg" class="card-img-top team-img lazy-load" alt="王强" loading="lazy">
<div class="card-body">
<h5 class="card-title">教授</h5>
<p class="text-muted">首席植物学家</p>
<p class="card-text text-muted small">
中科院植物研究所博士30年植物学研究经验
</p>
<h5 class="card-title"></h5>
<p class="text-muted">植物学</p>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6 mb-4">
<div class="card team-card border-0 text-center">
<img src="images/team-4.jpg" alt="产品经理" class="card-img-top rounded-circle mx-auto mt-4" style="width: 120px; height: 120px; object-fit: cover;">
<div class="col-md-3">
<div class="card team-card border-0 shadow-sm text-center">
<img src="images/team-4.jpg" class="card-img-top team-img lazy-load" alt="赵敏" loading="lazy">
<div class="card-body">
<h5 class="card-title">陈小姐</h5>
<p class="text-muted">产品总监</p>
<p class="card-text text-muted small">
前腾讯产品经理,擅长用户体验设计
</p>
<h5 class="card-title">赵敏</h5>
<p class="text-muted">用户体验总监</p>
</div>
</div>
</div>

View File

@@ -3,6 +3,22 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="联系我们 - 爱鉴花团队很乐意听取您的意见和建议,您可以通过在线表单、电话或邮件与我们取得联系。">
<meta name="keywords" content="联系我们,在线留言,客服支持,爱鉴花团队">
<meta name="author" content="爱鉴花团队">
<!-- Open Graph -->
<meta property="og:title" content="联系我们 - 爱鉴花">
<meta property="og:description" content="联系我们 - 爱鉴花团队很乐意听取您的意见和建议。">
<meta property="og:type" content="website">
<meta property="og:url" content="https://aijianhua.com/contact.html">
<meta property="og:image" content="https://aijianhua.com/images/contact-og.png">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="联系我们 - 爱鉴花">
<meta name="twitter:description" content="联系我们 - 爱鉴花团队很乐意听取您的意见和建议。">
<title>联系我们 - 爱鉴花</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.7.2/css/all.css" rel="stylesheet">
@@ -57,119 +73,84 @@
<section class="py-5">
<div class="container py-5">
<div class="row">
<div class="col-lg-8">
<!-- 联系表单 -->
<div class="card border-0 shadow-sm mb-5">
<div class="col-lg-8 mx-auto">
<div class="card border-0 shadow-sm">
<div class="card-body p-5">
<h2 class="fw-bold mb-4 text-dark">发送消息</h2>
<h2 class="card-title text-center mb-4">发送消息</h2>
<form id="contactForm">
<div class="row">
<div class="col-md-6 mb-3">
<label for="name" class="form-label">姓名 *</label>
<div class="row g-3">
<div class="col-md-6">
<label for="name" class="form-label">姓名 <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="name" required>
<div class="invalid-feedback">请输入您的姓名</div>
</div>
<div class="col-md-6 mb-3">
<label for="email" class="form-label">邮箱 *</label>
<div class="col-md-6">
<label for="email" class="form-label">邮箱 <span class="text-danger">*</span></label>
<input type="email" class="form-control" id="email" required>
<div class="invalid-feedback">请输入有效的邮箱地址</div>
</div>
<div class="col-12">
<label for="phone" class="form-label">电话</label>
<input type="tel" class="form-control" id="phone">
</div>
<div class="col-12">
<label for="subject" class="form-label">主题 <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="subject" required>
<div class="invalid-feedback">请输入消息主题</div>
</div>
<div class="col-12">
<label for="message" class="form-label">消息内容 <span class="text-danger">*</span></label>
<textarea class="form-control" id="message" rows="5" required></textarea>
<div class="invalid-feedback">请输入消息内容</div>
</div>
<div class="col-12">
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="privacyPolicy" required>
<label class="form-check-label" for="privacyPolicy">
我同意 <a href="#" class="text-primary">隐私政策</a><a href="#" class="text-primary">服务条款</a> <span class="text-danger">*</span>
</label>
<div class="invalid-feedback">请同意隐私政策和服务条款</div>
</div>
</div>
<div class="col-12 text-center">
<button type="submit" class="btn btn-primary btn-lg px-5">发送消息</button>
</div>
</div>
<div class="mb-3">
<label for="phone" class="form-label">电话</label>
<input type="tel" class="form-control" id="phone">
</div>
<div class="mb-3">
<label for="subject" class="form-label">主题 *</label>
<select class="form-select" id="subject" required>
<option value="">请选择主题</option>
<option value="general">一般咨询</option>
<option value="technical">技术支持</option>
<option value="business">商务合作</option>
<option value="feedback">产品反馈</option>
<option value="other">其他</option>
</select>
</div>
<div class="mb-3">
<label for="message" class="form-label">消息内容 *</label>
<textarea class="form-control" id="message" rows="5" required></textarea>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="agree" required>
<label class="form-check-label" for="agree">
我同意<a href="#" class="text-primary">隐私政策</a>
</label>
</div>
<button type="submit" class="btn btn-primary btn-lg w-100">发送消息</button>
<div class="mt-3 text-center">
<small class="text-muted">我们将在24小时内回复您的消息</small>
</div>
</form>
<!-- 表单提交成功提示 -->
<div class="alert alert-success mt-4 d-none" id="successAlert">
<i class="fas fa-check-circle me-2"></i>
消息发送成功!我们会尽快与您联系。
</div>
<!-- 表单提交失败提示 -->
<div class="alert alert-danger mt-4 d-none" id="errorAlert">
<i class="fas fa-exclamation-circle me-2"></i>
发送失败,请检查表单内容后重试。
</div>
</form>
</div>
</div>
</div>
<div class="col-lg-4">
<!-- 联系信息卡片 -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body text-center">
<div class="feature-icon bg-primary text-white rounded-circle mx-auto mb-3">
<div class="col-md-4">
<div class="card contact-card border-0 shadow-sm h-100 text-center">
<div class="card-body p-4">
<div class="contact-icon bg-primary text-white rounded-circle mx-auto mb-3">
<i class="fas fa-map-marker-alt fa-2x"></i>
</div>
<h5 class="fw-bold">公司地址</h5>
<p class="text-muted">
北京市海淀区中关村大街<br>
科技园区A座1001室
</p>
<h5 class="card-title">公司地址</h5>
<p class="card-text">北京市海淀区中关村大街1号<br>海龙大厦15层</p>
</div>
</div>
<div class="card border-0 shadow-sm mb-4">
<div class="card-body text-center">
<div class="feature-icon bg-success text-white rounded-circle mx-auto mb-3">
</div>
<div class="col-md-4">
<div class="card contact-card border-0 shadow-sm h-100 text-center">
<div class="card-body p-4">
<div class="contact-icon bg-success text-white rounded-circle mx-auto mb-3">
<i class="fas fa-phone fa-2x"></i>
</div>
<h5 class="fw-bold">联系电话</h5>
<p class="text-muted">
400-123-4567<br>
+86 10-8888-7777
</p>
<h5 class="card-title">联系电话</h5>
<p class="card-text">客服热线400-123-4567<br>工作时间:周一至周五 9:00-18:00</p>
</div>
</div>
<div class="card border-0 shadow-sm mb-4">
<div class="card-body text-center">
<div class="feature-icon bg-info text-white rounded-circle mx-auto mb-3">
</div>
<div class="col-md-4">
<div class="card contact-card border-0 shadow-sm h-100 text-center">
<div class="card-body p-4">
<div class="contact-icon bg-info text-white rounded-circle mx-auto mb-3">
<i class="fas fa-envelope fa-2x"></i>
</div>
<h5 class="fw-bold">电子邮箱</h5>
<p class="text-muted">
contact@aijianhua.com<br>
support@aijianhua.com
</p>
</div>
</div>
<div class="card border-0 shadow-sm">
<div class="card-body text-center">
<div class="feature-icon bg-warning text-white rounded-circle mx-auto mb-3">
<i class="fas fa-clock fa-2x"></i>
</div>
<h5 class="fw-bold">工作时间</h5>
<p class="text-muted">
周一至周五: 9:00-18:00<br>
周末: 10:00-16:00
</p>
<h5 class="card-title">电子邮箱</h5>
<p class="card-text">商务合作business@aijianhua.com<br>技术支持support@aijianhua.com</p>
</div>
</div>
</div>
@@ -177,115 +158,33 @@
</div>
</section>
<!-- 地图 -->
<section class="bg-light py-5">
<div class="container">
<div class="row">
<div class="col-12 text-center mb-5">
<h2 class="display-5 fw-bold text-dark">我们的位置</h2>
<p class="lead text-muted">欢迎来访我们的办公室</p>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-body p-0">
<!-- 地图占位图 -->
<div class="map-placeholder bg-secondary text-white text-center py-5">
<i class="fas fa-map-marked-alt fa-4x mb-3"></i>
<h4>地图位置</h4>
<p>北京市海淀区中关村大街科技园区A座</p>
<small class="text-white-50">(实际部署时可集成百度地图或高德地图)</small>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 常见问题 -->
<!-- 在线地图 -->
<section class="py-5">
<div class="container py-5">
<div class="row">
<div class="col-12 text-center mb-5">
<h2 class="display-5 fw-bold text-dark">常见问题</h2>
<p class="lead text-muted">快速找到您需要的答案</p>
<div class="col-12">
<h2 class="text-center display-5 fw-bold text-dark mb-5">公司位置</h2>
<div class="ratio ratio-21x9">
<iframe src="https://map.baidu.com/" title="公司位置地图" allowfullscreen></iframe>
</div>
</div>
</div>
</div>
</section>
<!-- 社交媒体 -->
<section class="bg-light py-5">
<div class="container py-5">
<div class="row">
<div class="col-lg-6">
<div class="accordion" id="faqAccordion">
<div class="accordion-item border-0 shadow-sm mb-3">
<h3 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#faq1">
如何开始使用爱鉴花小程序?
</button>
</h3>
<div id="faq1" class="accordion-collapse collapse show" data-bs-parent="#faqAccordion">
<div class="accordion-body">
<p class="text-muted">
在微信中搜索"爱鉴花"即可找到我们的小程序,
点击进入后即可开始使用花卉识别功能。
</p>
</div>
</div>
</div>
<div class="accordion-item border-0 shadow-sm mb-3">
<h3 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq2">
识别准确率如何?
</button>
</h3>
<div id="faq2" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body">
<p class="text-muted">
我们的AI识别准确率目前达到95%
在良好光线和清晰图片条件下准确率更高。
</p>
</div>
</div>
</div>
<div class="col-12 text-center">
<h2 class="display-5 fw-bold text-dark mb-4">关注我们</h2>
<p class="lead text-muted mb-5">通过社交媒体与我们保持联系</p>
<div class="social-contact-links">
<a href="#" class="btn btn-primary btn-lg me-3 mb-3"><i class="fab fa-weixin me-2"></i> 微信</a>
<a href="#" class="btn btn-info btn-lg me-3 mb-3"><i class="fab fa-weibo me-2"></i> 微博</a>
<a href="#" class="btn btn-dark btn-lg mb-3"><i class="fab fa-github me-2"></i> GitHub</a>
</div>
</div>
<div class="col-lg-6">
<div class="accordion" id="faqAccordion2">
<div class="accordion-item border-0 shadow-sm mb-3">
<h3 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq3">
是否支持企业定制?
</button>
</h3>
<div id="faq3" class="accordion-collapse collapse" data-bs-parent="#faqAccordion2">
<div class="accordion-body">
<p class="text-muted">
是的,我们提供企业定制服务,包括私有化部署、
定制模型训练等,请联系商务合作部门。
</p>
</div>
</div>
</div>
<div class="accordion-item border-0 shadow-sm mb-3">
<h3 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq4">
技术支持响应时间?
</button>
</h3>
<div id="faq4" class="accordion-collapse collapse" data-bs-parent="#faqAccordion2">
<div class="accordion-body">
<p class="text-muted">
普通咨询24小时内回复<br>
紧急问题4小时内响应<br>
企业客户:专属技术支持
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="text-center mt-4">
<a href="#" class="btn btn-outline-primary">查看更多问题</a>
</div>
</div>
</section>
@@ -329,151 +228,95 @@
<li><a href="contact.html" class="text-white-50 text-decoration-none">联系我们</a></li>
</ul>
</div>
<div class="col-lg-2 col-md-4 mb-4">
<h6 class="fw-bold mb-3">法律</h6>
<ul class="list-unstyled">
<li><a href="#" class="text-white-50 text-decoration-none">隐私政策</a></li>
<li><a href="#" class="text-white-50 text-decoration-none">服务条款</a></li>
<li><a href="#" class="text-white-50 text-decoration-none">版权声明</a></li>
</ul>
<div class="col-lg-4 mb-4">
<h6 class="mb-3">联系我们</h6>
<p class="text-muted mb-1"><i class="fas fa-map-marker-alt me-2"></i>北京市海淀区中关村大街</p>
<p class="text-muted mb-1"><i class="fas fa-phone me-2"></i>400-123-4567</p>
<p class="text-muted"><i class="fas fa-envelope me-2"></i>contact@aijianhua.com</p>
</div>
</div>
<hr class="my-4">
<div class="row align-items-center">
<div class="col-md-6 text-center text-md-start">
<p class="text-white-50 mb-0">&copy; 2024 爱鉴花. 保留所有权利</p>
<p class="text-muted mb-0">&copy; 2024 爱鉴花. 保留所有权利.</p>
</div>
<div class="col-md-6 text-center text-md-end">
<p class="text-white-50 mb-0">
<a href="#" class="text-white-50 text-decoration-none me-3">隐私政策</a>
<a href="#" class="text-white-50 text-decoration-none me-3">服务条款</a>
<a href="#" class="text-white-50 text-decoration-none">京ICP备12345678号</a>
<p class="text-muted mb-0">
<a href="#" class="text-muted text-decoration-none me-3">隐私政策</a>
<a href="#" class="text-muted text-decoration-none me-3">服务条款</a>
<a href="#" class="text-muted text-decoration-none">京ICP备12345678号</a>
</p>
</div>
</div>
<div class="text-center mt-3">
<p class="text-white-50 small">
<p class="text-muted small">
友情链接:
<a href="#" class="text-white-50 text-decoration-none me-2">中国花卉协会</a>
<a href="#" class="text-white-50 text-decoration-none me-2">植物智</a>
<a href="#" class="text-white-50 text-decoration-none">园林在线</a>
<a href="#" class="text-muted text-decoration-none me-2">中国花卉协会</a>
<a href="#" class="text-muted text-decoration-none me-2">植物智</a>
<a href="#" class="text-muted text-decoration-none">园林在线</a>
</p>
</div>
</div>
</footer>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="js/main.js"></script>
<!-- 表单验证脚本 -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const contactForm = document.getElementById('contactForm');
const successAlert = document.getElementById('successAlert');
const errorAlert = document.getElementById('errorAlert');
// 表单验证
document.getElementById('contactForm').addEventListener('submit', function(e) {
e.preventDefault();
// 实时表单验证
const inputs = contactForm.querySelectorAll('input, textarea, select');
inputs.forEach(input => {
input.addEventListener('blur', function() {
validateField(this);
});
input.addEventListener('input', function() {
clearValidation(this);
});
// 获取表单元素
const name = document.getElementById('name');
const email = document.getElementById('email');
const subject = document.getElementById('subject');
const message = document.getElementById('message');
const privacyPolicy = document.getElementById('privacyPolicy');
// 重置验证状态
[name, email, subject, message, privacyPolicy].forEach(element => {
element.classList.remove('is-invalid');
});
// 表单提交处理
contactForm.addEventListener('submit', function(e) {
e.preventDefault();
let isValid = true;
inputs.forEach(input => {
if (!validateField(input)) {
isValid = false;
}
});
if (isValid) {
// 模拟表单提交成功
simulateFormSubmission();
}
});
let isValid = true;
function validateField(field) {
let isValid = true;
let errorMessage = '';
if (field.required && !field.value.trim()) {
isValid = false;
errorMessage = '此字段为必填项';
} else if (field.type === 'email' && field.value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(field.value)) {
isValid = false;
errorMessage = '请输入有效的邮箱地址';
}
} else if (field.type === 'tel' && field.value) {
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(field.value)) {
isValid = false;
errorMessage = '请输入有效的手机号码';
}
}
if (!isValid) {
showError(field, errorMessage);
} else {
clearValidation(field);
}
return isValid;
// 验证姓名
if (!name.value.trim()) {
name.classList.add('is-invalid');
isValid = false;
}
function showError(field, message) {
clearValidation(field);
field.classList.add('is-invalid');
const errorDiv = document.createElement('div');
errorDiv.className = 'invalid-feedback';
errorDiv.textContent = message;
field.parentNode.appendChild(errorDiv);
// 验证邮箱
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email.value)) {
email.classList.add('is-invalid');
isValid = false;
}
function clearValidation(field) {
field.classList.remove('is-invalid');
const errorDiv = field.parentNode.querySelector('.invalid-feedback');
if (errorDiv) {
errorDiv.remove();
}
// 验证主题
if (!subject.value.trim()) {
subject.classList.add('is-invalid');
isValid = false;
}
function simulateFormSubmission() {
// 显示加载状态
const submitBtn = contactForm.querySelector('button[type="submit"]');
const originalText = submitBtn.textContent;
submitBtn.disabled = true;
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> 发送中...';
// 模拟网络请求延迟
setTimeout(() => {
// 恢复按钮状态
submitBtn.disabled = false;
submitBtn.textContent = originalText;
// 显示成功消息
successAlert.classList.remove('d-none');
errorAlert.classList.add('d-none');
// 重置表单
contactForm.reset();
// 3秒后隐藏成功消息
setTimeout(() => {
successAlert.classList.add('d-none');
}, 3000);
}, 2000);
// 验证消息内容
if (!message.value.trim()) {
message.classList.add('is-invalid');
isValid = false;
}
// 验证隐私政策
if (!privacyPolicy.checked) {
privacyPolicy.classList.add('is-invalid');
isValid = false;
}
// 如果表单有效,显示成功消息
if (isValid) {
alert('感谢您的消息!我们会尽快回复您。');
// 重置表单
this.reset();
}
});
</script>

View File

@@ -150,6 +150,11 @@ body {
transform: translateY(-3px);
}
.news-img {
height: 200px;
object-fit: cover;
}
/* 页脚样式 */
/* 修复body样式 */
body {
@@ -165,6 +170,12 @@ footer {
background: linear-gradient(135deg, #2E7D32 0%, #388E3C 100%); /* 深绿色渐变 */
}
/* 页面标题样式 */
.page-header {
background: linear-gradient(135deg, #4CAF50 0%, #66BB6A 100%);
margin-top: 76px;
}
footer h5,
footer h6 {
color: white;
@@ -247,36 +258,79 @@ footer a:hover {
height: 1rem;
}
/* 价格卡片样式 */
.pricing-card {
transition: all 0.3s ease;
}
.pricing-card:hover {
transform: translateY(-10px);
}
.pricing-price {
font-size: 2rem;
font-weight: bold;
}
/* 测试imonial卡片样式 */
.testimonial-card {
transition: all 0.3s ease;
}
.testimonial-card:hover {
transform: translateY(-5px);
}
.testimonial-avatar {
width: 50px;
height: 50px;
font-weight: bold;
font-size: 1.2rem;
}
/* 联系卡片样式 */
.contact-card {
transition: all 0.3s ease;
}
.contact-card:hover {
transform: translateY(-5px);
}
.contact-icon {
width: 70px;
height: 70px;
display: flex;
align-items: center;
justify-content: center;
}
/* 社交联系样式 */
.social-contact-links a {
transition: all 0.3s ease;
}
.social-contact-links a:hover {
transform: translateY(-3px);
}
/* 响应式设计 */
@media (max-width: 768px) {
.hero-section h1 {
font-size: 2.5rem;
}
.hero-section p {
font-size: 1.1rem;
}
.display-5 {
font-size: 2rem;
}
.feature-icon {
width: 60px;
height: 60px;
}
.min-vh-75 {
min-height: 60vh;
}
.avatar {
width: 60px;
height: 60px;
.timeline::before {
left: 30px;
}
.avatar i {
font-size: 2rem !important;
.timeline-date {
width: fit-content;
margin-left: 30px;
}
}

View File

@@ -0,0 +1,72 @@
# 爱鉴花官网图片需求说明
## 概述
本文档列出了爱鉴花官方网站所有缺失的图片文件包括图片名称、尺寸要求、内容描述和用途说明用于AI生成图片。
## 图片文件列表
### 1. Open Graph 分享图片
| 图片名称 | 尺寸 | 内容描述 | 用途 |
|---------|------|---------|------|
| `og-image.png` | 1200×630px | 爱鉴花品牌标识包含logo、AI花卉识别主题视觉元素清新自然的植物花卉背景 | 首页Open Graph分享图片 |
| `about-og.png` | 1200×630px | 关于我们页面分享图片展示团队协作场景包含植物元素和AI技术视觉 | 关于页面Open Graph分享 |
| `products-og.png` | 1200×630px | 产品介绍页面分享图片展示微信小程序界面、API技术图表和产品功能视觉 | 产品页面Open Graph分享 |
### 2. 首页图片
| 图片名称 | 尺寸 | 内容描述 | 用途 |
|---------|------|---------|------|
| `hero-image.png` | 800×600px | 现代简约的AI花卉识别应用界面展示包含手机设备显示花卉识别结果 | 首页英雄区域背景图片 |
| `product-1.jpg` | 400×300px | 玫瑰花特写,清晰展示花瓣细节和鲜艳色彩 | 热门产品展示区域 |
| `product-2.jpg` | 400×300px | 向日葵特写,明亮的黄色花朵面向阳光 | 热门产品展示区域 |
| `product-3.jpg` | 400×300px | 兰花特写,优雅的紫色或白色花朵,精致的花型 | 热门产品展示区域 |
### 3. 关于我们页面图片
| 图片名称 | 尺寸 | 内容描述 | 用途 |
|---------|------|---------|------|
| `about-1.jpg` | 600×400px | 现代科技公司办公环境包含植物装饰和AI技术工作场景 | 公司介绍区域配图 |
| `team-1.jpg` | 300×300px | 亚洲男性CEO肖像专业商务形象背景简洁 | 团队介绍-张明CEO |
| `team-2.jpg` | 300×300px | 亚洲女性CTO肖像技术专家形象背景简洁 | 团队介绍-李华CTO |
| `team-3.jpg` | 300×300px | 亚洲男性植物学家肖像,学者形象,背景简洁 | 团队介绍-王强首席植物学家 |
| `team-4.jpg` | 300×300px | 亚洲女性设计师肖像,创意专业形象,背景简洁 | 团队介绍-赵雪产品设计师 |
### 4. 产品介绍页面图片
| 图片名称 | 尺寸 | 内容描述 | 用途 |
|---------|------|---------|------|
| `product-mini-program.jpg` | 600×400px | 微信小程序界面展示,包含花卉识别功能和结果页面 | 微信小程序产品介绍 |
| `product-api.jpg` | 600×400px | API技术图表和数据流可视化现代科技感设计 | API服务产品介绍 |
| `product-enterprise.jpg` | 600×400px | 企业级解决方案展示,包含服务器架构和团队协作场景 | 企业版产品介绍 |
### 5. 新闻中心页面图片
| 图片名称 | 尺寸 | 内容描述 | 用途 |
|---------|------|---------|------|
| `news-1.jpg` | 400×300px | 微信小程序上线庆祝场景,包含团队合影和产品展示 | 小程序上线新闻配图 |
| `news-2.jpg` | 400×300px | AI技术突破可视化包含准确率图表和深度学习模型展示 | 技术突破新闻配图 |
| `news-3.jpg` | 400×300px | 植物数据库更新展示,包含多种花卉图片和数据界面 | 数据更新新闻配图 |
### 6. 品牌标识图片
| 图片名称 | 尺寸 | 内容描述 | 用途 |
|---------|------|---------|------|
| `logo.png` | 200×200px | 爱鉴花品牌logo包含花卉元素和AI技术视觉圆形设计 | 网站favicon和品牌标识 |
## 图片风格要求
- **色彩方案**: 使用植物花卉色系,主色调为绿色(#4CAF50)、紫色(#9C27B0)、粉色(#E91E63)
- **设计风格**: 现代简约、清新自然、科技感
- **图片格式**: PNG格式带透明背景或JPG格式高质量压缩
- **视觉一致性**: 所有图片保持统一的色彩风格和设计语言
## 技术规格
- **分辨率**: 所有图片提供2x高清版本
- **文件大小**: 单个图片不超过500KB
- **色彩模式**: RGB
- **版权**: 所有图片需为原创或拥有合法使用权
## 优先级
1. **高优先级**: `og-image.png`, `hero-image.png`, `logo.png`(品牌标识和关键页面)
2. **中优先级**: 产品图片和团队肖像(核心内容展示)
3. **低优先级**: 新闻配图和其他辅助图片
## 备注
- 所有人物肖像需使用模特授权图片或AI生成的可商用肖像
- 产品界面图片需体现现代科技感和用户体验
- 植物花卉图片需真实准确,符合植物学特征
- 团队形象图片需专业、友好、多样化

View File

@@ -236,9 +236,9 @@
<div class="col-lg-2 mb-4">
<h6 class="mb-3">产品</h6>
<ul class="list-unstyled">
<li><a href="#" class="text-muted text-decoration-none">微信小程序</a></li>
<li><a href="#" class="text-muted text-decoration-none">API服务</a></li>
<li><a href="#" class="text-muted text-decoration-none">企业版</a></li>
<li><a href="products.html" class="text-muted text-decoration-none">微信小程序</a></li>
<li><a href="products.html" class="text-muted text-decoration-none">API服务</a></li>
<li><a href="products.html" class="text-muted text-decoration-none">企业版</a></li>
</ul>
</div>
<div class="col-lg-2 mb-4">
@@ -246,7 +246,7 @@
<ul class="list-unstyled">
<li><a href="#" class="text-muted text-decoration-none">帮助中心</a></li>
<li><a href="#" class="text-muted text-decoration-none">常见问题</a></li>
<li><a href="#" class="text-muted text-decoration-none">联系我们</a></li>
<li><a href="contact.html" class="text-muted text-decoration-none">联系我们</a></li>
</ul>
</div>
<div class="col-lg-4 mb-4">
@@ -299,18 +299,5 @@
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "爱鉴花",
"url": "https://aijianhua.com",
"potentialAction": {
"@type": "SearchAction",
"target": "https://aijianhua.com/search?q={search_term_string}",
"query-input": "required name=search_term_string"
}
}
</script>
</body>
</html>

View File

@@ -432,4 +432,82 @@ function monitorErrors() {
// 初始化性能和错误监控
monitorPerformance();
monitorErrors();
monitorErrors();
// 通用JavaScript功能
// 导航栏滚动效果
window.addEventListener('scroll', function() {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 50) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
});
// 懒加载图片
document.addEventListener('DOMContentLoaded', function() {
const lazyImages = [].slice.call(document.querySelectorAll('img.lazy-load'));
if ('IntersectionObserver' in window) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src || lazyImage.src;
lazyImage.classList.remove('lazy-load');
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});
}
});
// 平滑滚动
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
window.scrollTo({
top: target.offsetTop - 76,
behavior: 'smooth'
});
}
});
});
// 表单验证增强
(function() {
'use strict';
window.addEventListener('load', function() {
// 获取所有表单并阻止默认提交行为
var forms = document.getElementsByClassName('needs-validation');
var validation = Array.prototype.filter.call(forms, function(form) {
form.addEventListener('submit', function(event) {
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated');
}, false);
});
}, false);
})();
// 动态年份更新
document.addEventListener('DOMContentLoaded', function() {
const yearElements = document.querySelectorAll('.current-year');
const currentYear = new Date().getFullYear();
yearElements.forEach(element => {
element.textContent = currentYear;
});
});

View File

@@ -3,6 +3,22 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="爱鉴花新闻中心 - 获取最新的公司动态、行业资讯和媒体报道了解AI花卉识别技术的最新发展。">
<meta name="keywords" content="爱鉴花新闻,花卉识别技术,植物AI,公司动态,行业资讯">
<meta name="author" content="爱鉴花团队">
<!-- Open Graph -->
<meta property="og:title" content="新闻中心 - 爱鉴花">
<meta property="og:description" content="获取最新的公司动态、行业资讯和媒体报道。">
<meta property="og:type" content="website">
<meta property="og:url" content="https://aijianhua.com/news.html">
<meta property="og:image" content="https://aijianhua.com/images/news-og.png">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="新闻中心 - 爱鉴花">
<meta name="twitter:description" content="获取最新的公司动态、行业资讯和媒体报道。">
<title>新闻中心 - 爱鉴花</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.7.2/css/all.css" rel="stylesheet">
@@ -53,269 +69,148 @@
</div>
</section>
<!-- 新闻内容 -->
<section class="py-5">
<div class="container py-5">
<!-- 新闻分类 -->
<section class="py-5 bg-light">
<div class="container">
<div class="row">
<!-- 主内容区 -->
<div class="col-lg-8">
<!-- 公司动态 -->
<div class="mb-5">
<h2 class="fw-bold mb-4 text-dark">公司动态</h2>
<!-- 新闻文章1 -->
<article class="card news-article border-0 shadow-sm mb-4">
<div class="row g-0">
<div class="col-md-4">
<img src="images/news-1.jpg" class="img-fluid h-100 object-fit-cover rounded-start" alt="小程序上线">
</div>
<div class="col-md-8">
<div class="card-body">
<div class="d-flex align-items-center mb-2">
<span class="badge bg-primary me-2">公司新闻</span>
<small class="text-muted">2024-01-15</small>
</div>
<h3 class="h4 card-title">爱鉴花微信小程序正式上线</h3>
<p class="card-text text-muted">
经过数月的精心开发和测试,爱鉴花微信小程序今日正式上线。
用户可以通过微信搜索"爱鉴花"或扫描二维码即可体验智能花卉识别服务。
</p>
<a href="news-detail.html" class="btn btn-outline-primary btn-sm">阅读全文</a>
</div>
</div>
</div>
</article>
<div class="col-12">
<ul class="nav nav-pills justify-content-center">
<li class="nav-item mx-2 mb-2">
<a class="nav-link active" href="#">全部</a>
</li>
<li class="nav-item mx-2 mb-2">
<a class="nav-link" href="#">公司动态</a>
</li>
<li class="nav-item mx-2 mb-2">
<a class="nav-link" href="#">行业资讯</a>
</li>
<li class="nav-item mx-2 mb-2">
<a class="nav-link" href="#">媒体报道</a>
</li>
</ul>
</div>
</div>
</div>
</section>
<!-- 新闻文章2 -->
<article class="card news-article border-0 shadow-sm mb-4">
<div class="row g-0">
<div class="col-md-4">
<img src="images/news-2.jpg" class="img-fluid h-100 object-fit-cover rounded-start" alt="AI识别">
</div>
<div class="col-md-8">
<div class="card-body">
<div class="d-flex align-items-center mb-2">
<span class="badge bg-primary me-2">技术突破</span>
<small class="text-muted">2024-01-10</small>
</div>
<h3 class="h4 card-title">AI识别准确率突破95%大关</h3>
<p class="card-text text-muted">
通过最新的深度学习算法优化爱鉴花的AI识别准确率成功突破95%
在花卉识别领域达到行业领先水平。
</p>
<a href="news-detail.html" class="btn btn-outline-primary btn-sm">阅读全文</a>
</div>
</div>
<!-- 新闻列表 -->
<section class="py-5">
<div class="container py-3">
<div class="row g-4">
<!-- 新闻项 1 -->
<div class="col-md-4">
<div class="card news-card border-0 shadow-sm h-100">
<img src="images/news-1.jpg" class="card-img-top news-img lazy-load" alt="爱鉴花小程序正式上线" loading="lazy">
<div class="card-body d-flex flex-column">
<div class="mb-2">
<span class="badge bg-primary me-2">公司动态</span>
<span class="text-muted small">2024-01-15</span>
</div>
</article>
<!-- 新闻文章3 -->
<article class="card news-article border-0 shadow-sm mb-4">
<div class="row g-0">
<div class="col-md-4">
<img src="images/news-3.jpg" class="img-fluid h-100 object-fit-cover rounded-start" alt="数据更新">
</div>
<div class="col-md-8">
<div class="card-body">
<div class="d-flex align-items-center mb-2">
<span class="badge bg-primary me-2">产品更新</span>
<small class="text-muted">2024-01-05</small>
</div>
<h3 class="h4 card-title">知识库新增1000种花卉数据</h3>
<p class="card-text text-muted">
本次更新新增1000种花卉的详细信息包括生长习性、养护方法、药用价值等
为用户提供更全面的植物知识服务。
</p>
<a href="news-detail.html" class="btn btn-outline-primary btn-sm">阅读全文</a>
</div>
</div>
</div>
</article>
<h5 class="card-title">爱鉴花小程序正式上线</h5>
<p class="card-text text-muted flex-grow-1">经过数月开发,我们的微信小程序正式上线,欢迎体验。用户可以通过拍照或上传图片快速识别花卉种类。</p>
<a href="#" class="btn btn-outline-primary mt-auto">阅读更多</a>
</div>
</div>
<!-- 行业资讯 -->
<div class="mb-5">
<h2 class="fw-bold mb-4 text-dark">行业资讯</h2>
<!-- 行业新闻1 -->
<article class="card news-article border-0 shadow-sm mb-4">
<div class="card-body">
<div class="d-flex align-items-center mb-2">
<span class="badge bg-info me-2">行业动态</span>
<small class="text-muted">2024-01-08</small>
</div>
<h3 class="h5 card-title">人工智能在植物识别领域的应用前景</h3>
<p class="card-text text-muted">
随着深度学习技术的发展AI在植物识别领域的应用越来越广泛。
专家预测,未来几年该领域将迎来爆发式增长。
</p>
<a href="news-detail.html" class="btn btn-outline-info btn-sm">阅读全文</a>
</div>
<!-- 新闻项 2 -->
<div class="col-md-4">
<div class="card news-card border-0 shadow-sm h-100">
<img src="images/news-2.jpg" class="card-img-top news-img lazy-load" alt="AI识别准确率再创新高" loading="lazy">
<div class="card-body d-flex flex-column">
<div class="mb-2">
<span class="badge bg-success me-2">技术突破</span>
<span class="text-muted small">2024-01-10</span>
</div>
</article>
<!-- 行业新闻2 -->
<article class="card news-article border-0 shadow-sm mb-4">
<div class="card-body">
<div class="d-flex align-items-center mb-2">
<span class="badge bg-info me-2">市场分析</span>
<small class="text-muted">2024-01-03</small>
</div>
<h3 class="h5 card-title">2024年植物识别APP市场趋势分析</h3>
<p class="card-text text-muted">
最新市场研究报告显示植物识别类APP用户规模持续增长
预计2024年全球市场规模将达到50亿美元。
</p>
<a href="news-detail.html" class="btn btn-outline-info btn-sm">阅读全文</a>
</div>
</article>
<h5 class="card-title">AI识别准确率再创新高</h5>
<p class="card-text text-muted flex-grow-1">通过算法优化花卉识别准确率提升至95%,在多项测试中表现优异。</p>
<a href="#" class="btn btn-outline-primary mt-auto">阅读更多</a>
</div>
</div>
<!-- 媒体报道 -->
<div>
<h2 class="fw-bold mb-4 text-dark">媒体报道</h2>
<!-- 媒体报道1 -->
<article class="card news-article border-0 shadow-sm mb-4">
<div class="card-body">
<div class="d-flex align-items-center mb-2">
<span class="badge bg-warning me-2">媒体报道</span>
<small class="text-muted">2024-01-12</small>
</div>
<h3 class="h5 card-title">科技日报爱鉴花用AI技术连接人与自然</h3>
<p class="card-text text-muted">
"爱鉴花团队通过先进的AI技术让普通用户也能轻松识别植物
这种技术创新对普及植物知识具有重要意义。"
</p>
<a href="news-detail.html" class="btn btn-outline-warning btn-sm">阅读全文</a>
</div>
<!-- 新闻项 3 -->
<div class="col-md-4">
<div class="card news-card border-0 shadow-sm h-100">
<img src="images/news-3.jpg" class="card-img-top news-img lazy-load" alt="新增1000种花卉数据" loading="lazy">
<div class="card-body d-flex flex-column">
<div class="mb-2">
<span class="badge bg-info me-2">产品更新</span>
<span class="text-muted small">2024-01-05</span>
</div>
</article>
<!-- 媒体报道2 -->
<article class="card news-article border-0 shadow-sm mb-4">
<div class="card-body">
<div class="d-flex align-items-center mb-2">
<span class="badge bg-warning me-2">专访</span>
<small class="text-muted">2024-01-07</small>
</div>
<h3 class="h5 card-title">创业邦专访爱鉴花创始人的AI情怀</h3>
<p class="card-text text-muted">
"我们希望通过技术让更多人关注和热爱自然,
这是爱鉴花团队的初心和使命。"——创始人张明
</p>
<a href="news-detail.html" class="btn btn-outline-warning btn-sm">阅读全文</a>
</div>
</article>
<h5 class="card-title">新增1000种花卉数据</h5>
<p class="card-text text-muted flex-grow-1">知识库新增1000种花卉信息覆盖更全面为用户提供更丰富的植物知识。</p>
<a href="#" class="btn btn-outline-primary mt-auto">阅读更多</a>
</div>
</div>
<!-- 分页 -->
<nav aria-label="Page navigation">
</div>
<!-- 新闻项 4 -->
<div class="col-md-4">
<div class="card news-card border-0 shadow-sm h-100">
<img src="images/news-4.jpg" class="card-img-top news-img lazy-load" alt="荣获年度创新产品奖" loading="lazy">
<div class="card-body d-flex flex-column">
<div class="mb-2">
<span class="badge bg-warning me-2">荣誉奖项</span>
<span class="text-muted small">2023-12-20</span>
</div>
<h5 class="card-title">荣获年度创新产品奖</h5>
<p class="card-text text-muted flex-grow-1">爱鉴花在2023年度科技创新产品评选中荣获"年度创新产品奖",这是对我们技术实力的认可。</p>
<a href="#" class="btn btn-outline-primary mt-auto">阅读更多</a>
</div>
</div>
</div>
<!-- 新闻项 5 -->
<div class="col-md-4">
<div class="card news-card border-0 shadow-sm h-100">
<img src="images/news-5.jpg" class="card-img-top news-img lazy-load" alt="与植物研究所达成合作" loading="lazy">
<div class="card-body d-flex flex-column">
<div class="mb-2">
<span class="badge bg-danger me-2">合作伙伴</span>
<span class="text-muted small">2023-12-10</span>
</div>
<h5 class="card-title">与植物研究所达成合作</h5>
<p class="card-text text-muted flex-grow-1">我们与国家植物研究所达成战略合作将进一步提升AI识别的准确性和知识库的权威性。</p>
<a href="#" class="btn btn-outline-primary mt-auto">阅读更多</a>
</div>
</div>
</div>
<!-- 新闻项 6 -->
<div class="col-md-4">
<div class="card news-card border-0 shadow-sm h-100">
<img src="images/news-6.jpg" class="card-img-top news-img lazy-load" alt="用户突破10万大关" loading="lazy">
<div class="card-body d-flex flex-column">
<div class="mb-2">
<span class="badge bg-secondary me-2">里程碑</span>
<span class="text-muted small">2023-11-28</span>
</div>
<h5 class="card-title">用户突破10万大关</h5>
<p class="card-text text-muted flex-grow-1">自上线以来爱鉴花用户数量突破10万感谢广大用户的信任与支持。</p>
<a href="#" class="btn btn-outline-primary mt-auto">阅读更多</a>
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="row mt-5">
<div class="col-12">
<nav aria-label="新闻分页">
<ul class="pagination justify-content-center">
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1">上一页</a>
</li>
<li class="page-item active">
<a class="page-link" href="#">1</a>
</li>
<li class="page-item">
<a class="page-link" href="#">2</a>
</li>
<li class="page-item">
<a class="page-link" href="#">3</a>
</li>
<li class="page-item active"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item">
<a class="page-link" href="#">下一页</a>
</li>
</ul>
</nav>
</div>
<!-- 侧边栏 -->
<div class="col-lg-4">
<!-- 搜索框 -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<h5 class="card-title">搜索新闻</h5>
<div class="input-group">
<input type="text" class="form-control" placeholder="输入关键词...">
<button class="btn btn-primary" type="button">
<i class="fas fa-search"></i>
</button>
</div>
</div>
</div>
<!-- 新闻分类 -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<h5 class="card-title">新闻分类</h5>
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between align-items-center">
<a href="#" class="text-decoration-none text-dark">公司新闻</a>
<span class="badge bg-primary rounded-pill">12</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<a href="#" class="text-decoration-none text-dark">技术突破</a>
<span class="badge bg-primary rounded-pill">8</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<a href="#" class="text-decoration-none text-dark">产品更新</a>
<span class="badge bg-primary rounded-pill">15</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<a href="#" class="text-decoration-none text-dark">行业动态</a>
<span class="badge bg-primary rounded-pill">6</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<a href="#" class="text-decoration-none text-dark">媒体报道</a>
<span class="badge bg-primary rounded-pill">9</span>
</li>
</ul>
</div>
</div>
<!-- 热门新闻 -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<h5 class="card-title">热门新闻</h5>
<div class="list-group list-group-flush">
<a href="#" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">小程序上线首日用户破万</h6>
<small class="text-muted">3天前</small>
</div>
<small class="text-muted">阅读量: 2,345</small>
</a>
<a href="#" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">AI识别准确率行业领先</h6>
<small class="text-muted">1周前</small>
</div>
<small class="text-muted">阅读量: 1,876</small>
</a>
<a href="#" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">新增千种花卉数据</h6>
<small class="text-muted">2周前</small>
</div>
<small class="text-muted">阅读量: 1,532</small>
</a>
</div>
</div>
</div>
<!-- 订阅更新 -->
<div class="card border-0 shadow-sm">
<div class="card-body text-center">
<h5 class="card-title">订阅更新</h5>
<p class="text-muted small">第一时间获取最新新闻动态</p>
<div class="input-group mb-3">
<input type="email" class="form-control" placeholder="您的邮箱">
<button class="btn btn-primary" type="button">订阅</button>
</div>
<small class="text-muted">我们尊重您的隐私,绝不会分享您的信息</small>
</div>
</div>
</div>
</div>
</div>
</section>
@@ -336,9 +231,9 @@
<div class="col-lg-2 mb-4">
<h6 class="mb-3">产品</h6>
<ul class="list-unstyled">
<li><a href="#" class="text-muted text-decoration-none">微信小程序</a></li>
<li><a href="#" class="text-muted text-decoration-none">API服务</a></li>
<li><a href="#" class="text-muted text-decoration-none">企业版</a></li>
<li><a href="products.html" class="text-muted text-decoration-none">微信小程序</a></li>
<li><a href="products.html" class="text-muted text-decoration-none">API服务</a></li>
<li><a href="products.html" class="text-muted text-decoration-none">企业版</a></li>
</ul>
</div>
<div class="col-lg-2 mb-4">
@@ -346,7 +241,7 @@
<ul class="list-unstyled">
<li><a href="#" class="text-muted text-decoration-none">帮助中心</a></li>
<li><a href="#" class="text-muted text-decoration-none">常见问题</a></li>
<li><a href="#" class="text-muted text-decoration-none">联系我们</a></li>
<li><a href="contact.html" class="text-muted text-decoration-none">联系我们</a></li>
</ul>
</div>
<div class="col-lg-4 mb-4">

View File

@@ -63,167 +63,228 @@
<div class="row">
<div class="col-12 text-center">
<h1 class="display-4 fw-bold mb-3">产品介绍</h1>
<p class="lead">探索爱鉴花的智能花卉识别解决方案</p>
<p class="lead">探索我们的智能花卉识别解决方案</p>
</div>
</div>
</div>
</section>
<!-- 产品概览 -->
<!-- 产品功能介绍 -->
<section class="py-5">
<div class="container py-5">
<div class="row">
<div class="col-12 text-center mb-5">
<h2 class="display-5 fw-bold text-dark">我们的产品体系</h2>
<p class="lead text-muted">为不同用户群体提供专业的花卉识别服务</p>
</div>
<div class="text-center mb-5">
<h2 class="display-5 fw-bold text-dark mb-3">产品功能</h2>
<p class="lead text-muted">深入了解我们的核心功能和服务</p>
</div>
<!-- 微信小程序 -->
<div class="row align-items-center mb-5">
<div class="col-lg-6">
<img src="images/product-mini-program.jpg" alt="微信小程序" class="img-fluid rounded shadow lazy-load" loading="lazy">
</div>
<div class="col-lg-6">
<h3 class="fw-bold mb-3">微信小程序</h3>
<p class="text-muted mb-4">
随时随地识别花卉,操作简单,使用便捷。无需下载安装,
打开微信即可使用,满足日常花卉识别需求。
</p>
<ul class="list-unstyled">
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>拍照即时识别</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>详细的植物信息</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>养护知识指导</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>识别历史记录</li>
</ul>
<div class="mt-4">
<a href="#" class="btn btn-primary me-3">立即体验</a>
<a href="#" class="btn btn-outline-primary">了解更多</a>
<div class="row g-4">
<div class="col-md-6">
<div class="card border-0 shadow-sm h-100">
<div class="card-body p-4">
<h5 class="card-title">AI智能识别</h5>
<p class="card-text">通过深度学习技术我们的AI模型可以准确识别超过10000种花卉植物识别准确率高达95%。</p>
<ul class="list-unstyled mt-3">
<li class="mb-2"><i class="fas fa-check-circle text-success me-2"></i> 支持拍照和图片上传识别</li>
<li class="mb-2"><i class="fas fa-check-circle text-success me-2"></i> 实时识别反馈</li>
<li class="mb-2"><i class="fas fa-check-circle text-success me-2"></i> 支持多种图片格式</li>
</ul>
</div>
</div>
</div>
</div>
<!-- API服务 -->
<div class="row align-items-center mb-5">
<div class="col-lg-6 order-lg-2">
<img src="images/product-api.jpg" alt="API服务" class="img-fluid rounded shadow lazy-load" loading="lazy">
</div>
<div class="col-lg-6 order-lg-1">
<h3 class="fw-bold mb-3">API服务</h3>
<p class="text-muted mb-4">
为开发者和企业提供稳定可靠的花卉识别API接口
轻松集成到您的应用中,扩展植物识别功能。
</p>
<ul class="list-unstyled">
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>RESTful API接口</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>高并发支持</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>99.9%可用性</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>详细技术文档</li>
</ul>
<div class="mt-4">
<a href="#" class="btn btn-primary me-3">查看文档</a>
<a href="#" class="btn btn-outline-primary">申请试用</a>
<div class="col-md-6">
<div class="card border-0 shadow-sm h-100">
<div class="card-body p-4">
<h5 class="card-title">丰富知识库</h5>
<p class="card-text">包含详尽的植物信息数据库,提供每种花卉的科学分类、生长习性、养护方法等详细信息。</p>
<ul class="list-unstyled mt-3">
<li class="mb-2"><i class="fas fa-check-circle text-success me-2"></i> 超过10000种植物信息</li>
<li class="mb-2"><i class="fas fa-check-circle text-success me-2"></i> 专业植物学描述</li>
<li class="mb-2"><i class="fas fa-check-circle text-success me-2"></i> 定期更新数据库</li>
</ul>
</div>
</div>
</div>
</div>
<!-- 企业版 -->
<div class="row align-items-center">
<div class="col-lg-6">
<img src="images/product-enterprise.jpg" alt="企业版" class="img-fluid rounded shadow lazy-load" loading="lazy">
<div class="col-md-6">
<div class="card border-0 shadow-sm h-100">
<div class="card-body p-4">
<h5 class="card-title">社区交流</h5>
<p class="card-text">与花卉爱好者分享经验,交流养护心得,共同成长,建立一个热爱植物的社区。</p>
<ul class="list-unstyled mt-3">
<li class="mb-2"><i class="fas fa-check-circle text-success me-2"></i> 用户分享平台</li>
<li class="mb-2"><i class="fas fa-check-circle text-success me-2"></i> 专家在线答疑</li>
<li class="mb-2"><i class="fas fa-check-circle text-success me-2"></i> 植物养护讨论</li>
</ul>
</div>
</div>
</div>
<div class="col-lg-6">
<h3 class="fw-bold mb-3">企业版解决方案</h3>
<p class="text-muted mb-4">
为园林公司、教育机构、科研单位等提供定制化的
花卉识别解决方案,满足专业级应用需求。
</p>
<ul class="list-unstyled">
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>私有化部署</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>定制化模型训练</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>专业技术支持</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>批量处理功能</li>
</ul>
<div class="mt-4">
<a href="contact.html" class="btn btn-primary me-3">联系我们</a>
<a href="#" class="btn btn-outline-primary">方案详情</a>
<div class="col-md-6">
<div class="card border-0 shadow-sm h-100">
<div class="card-body p-4">
<h5 class="card-title">个性化服务</h5>
<p class="card-text">根据用户识别历史和兴趣偏好,提供个性化的内容推荐和养护建议。</p>
<ul class="list-unstyled mt-3">
<li class="mb-2"><i class="fas fa-check-circle text-success me-2"></i> 个性化内容推荐</li>
<li class="mb-2"><i class="fas fa-check-circle text-success me-2"></i> 植物养护提醒</li>
<li class="mb-2"><i class="fas fa-check-circle text-success me-2"></i> 收藏和关注功能</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 功能对比 -->
<!-- 产品对比 -->
<section class="bg-light py-5">
<div class="container py-5">
<div class="row">
<div class="col-12 text-center mb-5">
<h2 class="display-5 fw-bold text-dark">功能对比</h2>
<p class="lead text-muted">选择最适合您的产品方案</p>
</div>
<div class="text-center mb-5">
<h2 class="display-5 fw-bold text-dark mb-3">产品对比</h2>
<p class="lead text-muted">三款产品功能差异对比</p>
</div>
<div class="row">
<div class="col-lg-4 mb-4">
<div class="card pricing-card border-0 shadow-sm text-center h-100">
<div class="card-header bg-white py-4">
<h4 class="fw-bold">微信小程序</h4>
<div class="price">
<span class="h2 text-primary">免费</span>
</div>
</div>
<div class="table-responsive">
<table class="table table-bordered table-striped">
<thead class="table-dark">
<tr>
<th>功能特性</th>
<th>微信小程序</th>
<th>API服务</th>
<th>企业版</th>
</tr>
</thead>
<tbody>
<tr>
<td>基础识别功能</td>
<td><i class="fas fa-check text-success"></i></td>
<td><i class="fas fa-check text-success"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr>
<td>知识库查询</td>
<td><i class="fas fa-check text-success"></i></td>
<td><i class="fas fa-check text-success"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr>
<td>识别准确率</td>
<td>95%</td>
<td>95%</td>
<td>98%+</td>
</tr>
<tr>
<td>月识别次数</td>
<td>100次</td>
<td>1000次</td>
<td>无限制</td>
</tr>
<tr>
<td>批量识别</td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-check text-success"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr>
<td>私有部署</td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr>
<td>定制识别模型</td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr>
<td>专属技术支持</td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr>
<td>价格</td>
<td>免费</td>
<td>¥99/月</td>
<td>定制报价</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- 使用案例 -->
<section class="py-5">
<div class="container py-5">
<div class="text-center mb-5">
<h2 class="display-5 fw-bold text-dark mb-3">使用案例</h2>
<p class="lead text-muted">真实用户使用场景和效果</p>
</div>
<div class="row g-4">
<div class="col-md-4">
<div class="card testimonial-card border-0 shadow-sm h-100">
<div class="card-body p-4">
<ul class="list-unstyled mb-4">
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>基础花卉识别</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>植物信息查询</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>识别历史记录</li>
<li class="mb-2"><i class="fas fa-times text-muted me-2"></i>批量识别</li>
<li class="mb-2"><i class="fas fa-times text-muted me-2"></i>API访问</li>
</ul>
<a href="#" class="btn btn-outline-primary w-100">开始使用</a>
<div class="testimonial-rating mb-3">
<i class="fas fa-star text-warning"></i>
<i class="fas fa-star text-warning"></i>
<i class="fas fa-star text-warning"></i>
<i class="fas fa-star text-warning"></i>
<i class="fas fa-star text-warning"></i>
</div>
<p class="card-text">"作为一个园艺爱好者,爱鉴花小程序帮助我快速识别了很多以前不认识的花卉,准确率真的很高!"</p>
<div class="d-flex align-items-center mt-4">
<div class="testimonial-avatar bg-primary text-white rounded-circle d-flex align-items-center justify-content-center me-3">
<span></span>
</div>
<div>
<h6 class="mb-0">张女士</h6>
<small class="text-muted">园艺爱好者</small>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4 mb-4">
<div class="card pricing-card border-0 shadow-sm text-center h-100">
<div class="card-header bg-primary text-white py-4">
<h4 class="fw-bold">API服务</h4>
<div class="price">
<span class="h2">¥99</span>
<span class="text-white-50">/月</span>
</div>
</div>
<div class="col-md-4">
<div class="card testimonial-card border-0 shadow-sm h-100">
<div class="card-body p-4">
<ul class="list-unstyled mb-4">
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>1000次API调用</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>高精度识别</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>技术支持</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>批量识别</li>
<li class="mb-2"><i class="fas fa-times text-muted me-2"></i>私有化部署</li>
</ul>
<a href="#" class="btn btn-primary w-100">立即订阅</a>
<div class="testimonial-rating mb-3">
<i class="fas fa-star text-warning"></i>
<i class="fas fa-star text-warning"></i>
<i class="fas fa-star text-warning"></i>
<i class="fas fa-star text-warning"></i>
<i class="fas fa-star text-warning"></i>
</div>
<p class="card-text">"我们植物研究所使用爱鉴花企业版进行批量植物识别,大大提高了我们的工作效率,非常满意!"</p>
<div class="d-flex align-items-center mt-4">
<div class="testimonial-avatar bg-success text-white rounded-circle d-flex align-items-center justify-content-center me-3">
<span></span>
</div>
<div>
<h6 class="mb-0">李先生</h6>
<small class="text-muted">植物研究所研究员</small>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4 mb-4">
<div class="card pricing-card border-0 shadow-sm text-center h-100">
<div class="card-header bg-dark text-white py-4">
<h4 class="fw-bold">企业版</h4>
<div class="price">
<span class="h2">定制</span>
</div>
</div>
<div class="col-md-4">
<div class="card testimonial-card border-0 shadow-sm h-100">
<div class="card-body p-4">
<ul class="list-unstyled mb-4">
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>无限次调用</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>私有化部署</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>定制模型训练</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>专属技术支持</li>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i>批量处理</li>
</ul>
<a href="contact.html" class="btn btn-dark w-100">咨询方案</a>
<div class="testimonial-rating mb-3">
<i class="fas fa-star text-warning"></i>
<i class="fas fa-star text-warning"></i>
<i class="fas fa-star text-warning"></i>
<i class="fas fa-star text-warning"></i>
<i class="fas fa-star-half-alt text-warning"></i>
</div>
<p class="card-text">"我们的花卉电商平台集成了爱鉴花API服务为用户提供了植物识别功能显著提升了用户体验。"</p>
<div class="d-flex align-items-center mt-4">
<div class="testimonial-avatar bg-info text-white rounded-circle d-flex align-items-center justify-content-center me-3">
<span></span>
</div>
<div>
<h6 class="mb-0">王先生</h6>
<small class="text-muted">电商平台产品经理</small>
</div>
</div>
</div>
</div>
</div>
@@ -231,167 +292,84 @@
</div>
</section>
<!-- 技术优势 -->
<section class="py-5">
<!-- 价格方案 -->
<section class="bg-light py-5">
<div class="container py-5">
<div class="row">
<div class="col-12 text-center mb-5">
<h2 class="display-5 fw-bold text-dark">技术优势</h2>
<p class="lead text-muted">领先的AI技术卓越的用户体验</p>
</div>
<div class="text-center mb-5">
<h2 class="display-5 fw-bold text-dark mb-3">价格方案</h2>
<p class="lead text-muted">选择适合您的产品套餐</p>
</div>
<div class="row">
<div class="col-md-4 mb-4">
<div class="text-center">
<div class="feature-icon bg-primary text-white rounded-circle mx-auto mb-3">
<i class="fas fa-brain fa-2x"></i>
</div>
<h5 class="fw-bold">深度学习算法</h5>
<p class="text-muted">
基于深度卷积神经网络,
实现高精度花卉识别
</p>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="text-center">
<div class="feature-icon bg-success text-white rounded-circle mx-auto mb-3">
<i class="fas fa-database fa-2x"></i>
</div>
<h5 class="fw-bold">海量数据训练</h5>
<p class="text-muted">
超过100万张花卉图像训练
覆盖5000+种植物
</p>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="text-center">
<div class="feature-icon bg-info text-white rounded-circle mx-auto mb-3">
<i class="fas fa-bolt fa-2x"></i>
</div>
<h5 class="fw-bold">快速响应</h5>
<p class="text-muted">
平均识别时间小于2秒
提供流畅的用户体验
</p>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="text-center">
<div class="feature-icon bg-warning text-white rounded-circle mx-auto mb-3">
<i class="fas fa-mobile-alt fa-2x"></i>
</div>
<h5 class="fw-bold">多平台支持</h5>
<p class="text-muted">
支持iOS、Android、Web等多平台
随时随地使用
</p>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="text-center">
<div class="feature-icon bg-danger text-white rounded-circle mx-auto mb-3">
<i class="fas fa-shield-alt fa-2x"></i>
</div>
<h5 class="fw-bold">数据安全</h5>
<p class="text-muted">
HTTPS加密传输
确保用户数据安全
</p>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="text-center">
<div class="feature-icon bg-secondary text-white rounded-circle mx-auto mb-3">
<i class="fas fa-sync fa-2x"></i>
</div>
<h5 class="fw-bold">持续优化</h5>
<p class="text-muted">
算法持续迭代优化,
不断提升识别准确率
</p>
</div>
</div>
</div>
</div>
</section>
<!-- 客户评价 -->
<section class="py-5">
<div class="container py-5">
<div class="row">
<div class="col-12 text-center mb-5">
<h2 class="display-5 fw-bold text-dark">客户评价</h2>
<p class="lead text-muted">听听我们的用户怎么说</p>
</div>
</div>
<div class="row">
<div class="col-lg-4 mb-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-body text-center p-4">
<div class="avatar mx-auto mb-3">
<i class="fas fa-user-circle fa-3x text-primary"></i>
<div class="row g-4">
<div class="col-md-4">
<div class="card pricing-card border-0 shadow-sm h-100">
<div class="card-header bg-primary text-white text-center py-4">
<h4 class="mb-0">微信小程序</h4>
<div class="pricing-price mt-3">
<span class="display-6">免费</span>
</div>
<h5 class="fw-bold mb-2">张老师</h5>
<p class="text-muted small mb-3">生物教师</p>
<p class="text-muted">
"爱鉴花小程序让我的生物课变得生动有趣,学生们现在对植物识别充满热情!"
</p>
<div class="rating text-warning">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<p class="mb-0">适合个人用户</p>
</div>
<div class="card-body p-4">
<ul class="list-unstyled">
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 基础识别功能</li>
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 知识库查询</li>
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 社区交流</li>
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 每月100次识别</li>
<li class="mb-3 text-muted"><i class="fas fa-times-circle me-2"></i> 批量识别</li>
<li class="mb-3 text-muted"><i class="fas fa-times-circle me-2"></i> 专属支持</li>
</ul>
<div class="text-center mt-4">
<a href="#" class="btn btn-outline-primary">立即使用</a>
</div>
</div>
</div>
</div>
<div class="col-lg-4 mb-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-body text-center p-4">
<div class="avatar mx-auto mb-3">
<i class="fas fa-user-circle fa-3x text-primary"></i>
<div class="col-md-4">
<div class="card pricing-card border-0 shadow-sm h-100">
<div class="card-header bg-success text-white text-center py-4">
<h4 class="mb-0">API服务</h4>
<div class="pricing-price mt-3">
<span class="display-6">¥99</span>
<span class="text-muted">/月</span>
</div>
<h5 class="fw-bold mb-2">李经理</h5>
<p class="text-muted small mb-3">园林公司</p>
<p class="text-muted">
"API接口非常稳定集成到我们的园林管理系统中大大提高了工作效率。"
</p>
<div class="rating text-warning">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<p class="mb-0">适合开发者和企业</p>
</div>
<div class="card-body p-4">
<ul class="list-unstyled">
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 基础识别功能</li>
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 知识库查询</li>
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 每月1000次识别</li>
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 批量识别</li>
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> API文档和技术支持</li>
<li class="mb-3 text-muted"><i class="fas fa-times-circle me-2"></i> 私有部署</li>
</ul>
<div class="text-center mt-4">
<a href="#" class="btn btn-success">立即购买</a>
</div>
</div>
</div>
</div>
<div class="col-lg-4 mb-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-body text-center p-4">
<div class="avatar mx-auto mb-3">
<i class="fas fa-user-circle fa-3x text-primary"></i>
<div class="col-md-4">
<div class="card pricing-card border-0 shadow-sm h-100">
<div class="card-header bg-dark text-white text-center py-4">
<h4 class="mb-0">企业版</h4>
<div class="pricing-price mt-3">
<span class="display-6">定制</span>
</div>
<h5 class="fw-bold mb-2">王女士</h5>
<p class="text-muted small mb-3">花卉爱好者</p>
<p class="text-muted">
"识别准确率很高,养护知识也很实用,现在养花再也不用担心认错品种了!"
</p>
<div class="rating text-warning">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<p class="mb-0">适合大型企业和机构</p>
</div>
<div class="card-body p-4">
<ul class="list-unstyled">
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 基础识别功能</li>
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 知识库查询</li>
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 无限制识别次数</li>
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 批量识别</li>
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 私有部署</li>
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 定制识别模型</li>
<li class="mb-3"><i class="fas fa-check-circle text-success me-2"></i> 专属技术支持</li>
</ul>
<div class="text-center mt-4">
<a href="contact.html" class="btn btn-dark">联系我们</a>
</div>
</div>
</div>
@@ -416,9 +394,9 @@
<div class="col-lg-2 mb-4">
<h6 class="mb-3">产品</h6>
<ul class="list-unstyled">
<li><a href="#" class="text-muted text-decoration-none">微信小程序</a></li>
<li><a href="#" class="text-muted text-decoration-none">API服务</a></li>
<li><a href="#" class="text-muted text-decoration-none">企业版</a></li>
<li><a href="products.html" class="text-muted text-decoration-none">微信小程序</a></li>
<li><a href="products.html" class="text-muted text-decoration-none">API服务</a></li>
<li><a href="products.html" class="text-muted text-decoration-none">企业版</a></li>
</ul>
</div>
<div class="col-lg-2 mb-4">
@@ -426,7 +404,7 @@
<ul class="list-unstyled">
<li><a href="#" class="text-muted text-decoration-none">帮助中心</a></li>
<li><a href="#" class="text-muted text-decoration-none">常见问题</a></li>
<li><a href="#" class="text-muted text-decoration-none">联系我们</a></li>
<li><a href="contact.html" class="text-muted text-decoration-none">联系我们</a></li>
</ul>
</div>
<div class="col-lg-4 mb-4">
@@ -462,76 +440,5 @@
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="js/main.js"></script>
<!-- Schema.org结构化数据 -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "ItemList",
"name": "爱鉴花产品服务",
"description": "爱鉴花提供的智能花卉识别产品和服务",
"url": "https://aijianhua.com/products.html",
"numberOfItems": 3,
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"item": {
"@type": "SoftwareApplication",
"name": "微信小程序",
"description": "免费的AI花卉识别微信小程序随时随地识别植物",
"applicationCategory": "UtilitiesApplication",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "CNY"
}
}
},
{
"@type": "ListItem",
"position": 2,
"item": {
"@type": "Service",
"name": "API服务",
"description": "花卉识别API接口服务为开发者提供技术支持",
"offers": {
"@type": "Offer",
"price": "99",
"priceCurrency": "CNY",
"priceSpecification": {
"@type": "UnitPriceSpecification",
"price": "99",
"priceCurrency": "CNY",
"referenceQuantity": {
"@type": "QuantitativeValue",
"value": "1000",
"unitCode": "C62"
}
}
}
}
},
{
"@type": "ListItem",
"position": 3,
"item": {
"@type": "Service",
"name": "企业版解决方案",
"description": "定制化的企业级花卉识别解决方案",
"offers": {
"@type": "Offer",
"priceSpecification": {
"@type": "PriceSpecification",
"price": "0",
"priceCurrency": "CNY",
"valueAddedTaxIncluded": false
}
}
}
}
]
}
</script>
</body>
</html>

View File

@@ -1,18 +1,18 @@
User-agent: *
Allow: /
# Sitemap
# Sitemap location
Sitemap: https://aijianhua.com/sitemap.xml
# Disallow admin pages and sensitive directories
# Block access to sensitive directories
Disallow: /admin/
Disallow: /private/
Disallow: /config/
# Allow search engines to crawl all content
Allow: /*.html
Allow: /*.css
Allow: /*.js
# Allow crawlers to access all HTML, CSS, and JS files
Allow: /*.html$
Allow: /*.css$
Allow: /*.js$
# Crawl delay to prevent overloading server
# Prevent server overloading with crawl delay
Crawl-delay: 1

View File

@@ -1,33 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://aijianhua.com/</loc>
<lastmod>2024-01-15</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://aijianhua.com/index.html</loc>
<lastmod>2024-01-20</lastmod>
<lastmod>2024-01-15</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://aijianhua.com/about.html</loc>
<lastmod>2024-01-20</lastmod>
<lastmod>2024-01-15</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://aijianhua.com/products.html</loc>
<lastmod>2024-01-20</lastmod>
<lastmod>2024-01-15</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
<priority>0.9</priority>
</url>
<url>
<loc>https://aijianhua.com/news.html</loc>
<lastmod>2024-01-20</lastmod>
<lastmod>2024-01-15</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
<priority>0.8</priority>
</url>
<url>
<loc>https://aijianhua.com/contact.html</loc>
<lastmod>2024-01-20</lastmod>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
<lastmod>2024-01-15</lastmod>
<changefreq>yearly</changefreq>
<priority>0.7</priority>
</url>
</urlset>

View File

@@ -115,6 +115,13 @@
- 识别记录: 3条
- 收货地址: 3条
## 5. 数据库变更记录
### 2024-03-15 更新
- 修复用户表缺少last_login字段的问题
- 密码字段从password_hash改为password使用bcrypt加密存储
- 增加数据库初始化脚本包含默认管理员账号admin/admin123
## 5. 数据库连接信息
**生产环境**:

View File

@@ -47,12 +47,14 @@ NODE_ENV=development
PORT=3000
APP_NAME=爱鉴花小程序后端
# MySQL数据库配置
# 数据库配置开发环境使用SQLite生产环境使用MySQL
DB_TYPE=sqlite # 开发环境使用sqlite生产环境使用mysql
DB_HOST=192.168.0.240
DB_PORT=3306
DB_USERNAME=root
DB_PASSWORD=aiot$Aiot123
DB_DATABASE=ajhdata
SQLITE_DB_PATH=./database.sqlite # SQLite数据库文件路径
# Redis配置
REDIS_HOST=127.0.0.1

View File

@@ -6,6 +6,7 @@
| v1.0.0 | 2024-01-20 | 系统 | 初始版本创建 |
| v1.1.0 | 2024-03-15 | 产品经理 | 完善官网功能需求细节 |
| v1.1.1 | 2024-03-15 | 产品经理 | 补充非功能需求技术要求 |
| v1.1.2 | 2024-03-15 | 开发工程师 | 修复数据库设计问题,更新文档一致性 |
## 1. 项目概述
爱鉴花是一款通过AI图片识别植物类型的微信小程序应用为用户提供花卉相关信息、购买、配送等服务。

View File

@@ -11,7 +11,7 @@
## 技术架构
- **前端技术栈**: uni-app、Vue3、Element Plus、Bootstrap
- **后端技术栈**: Node.js、Express.js、MySQL、Redis
- **后端技术栈**: Node.js、Express.js、MySQL生产环境、SQLite开发环境、Redis
- **开发工具**: HBuilderX、VSCode、Git
- **部署环境**: Nginx、Docker、云服务器

View File

@@ -29,12 +29,12 @@
- 运行开发服务器进行调试
### 后端服务 (Node.js)
1. **环境要求**: Node.js 14+MySQL 5.7+Redis
1. **环境要求**: Node.js 14+MySQL 5.7+生产环境SQLite开发环境Redis
2. **开发步骤**:
- 导入 backend 文件夹
- 运行 `npm install` 安装依赖
- 复制 `.env.example``.env` 并配置数据库连接
- 运行 `npm run db:init` 初始化数据库
- 运行 `npm run db:init` 初始化数据库默认使用SQLite
- 运行 `npm start` 启动服务(开发环境使用 `npm run dev`
### 后台管理系统 (Vue3)