docs: 更新项目文档,完善需求和技术细节
This commit is contained in:
@@ -19,6 +19,9 @@
|
||||
### 修复
|
||||
- 文档中的错误和遗漏信息
|
||||
- 开发环境配置问题
|
||||
- 数据库用户表缺少last_login字段的问题
|
||||
- 密码哈希值存储和验证问题
|
||||
- bcrypt版本不匹配导致的密码验证失败
|
||||
|
||||
## 版本规范
|
||||
|
||||
|
||||
11814
admin_website/package-lock.json
generated
Normal file
11814
admin_website/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
31
admin_website/src/App.vue
Normal 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>
|
||||
229
admin_website/src/layouts/MainLayout.vue
Normal file
229
admin_website/src/layouts/MainLayout.vue
Normal 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
20
admin_website/src/main.js
Normal 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')
|
||||
74
admin_website/src/router/index.js
Normal file
74
admin_website/src/router/index.js
Normal 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
|
||||
45
admin_website/src/store/index.js
Normal file
45
admin_website/src/store/index.js
Normal 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
|
||||
}
|
||||
})
|
||||
124
admin_website/src/utils/api.js
Normal file
124
admin_website/src/utils/api.js
Normal 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
|
||||
416
admin_website/src/views/Dashboard.vue
Normal file
416
admin_website/src/views/Dashboard.vue
Normal 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>
|
||||
245
admin_website/src/views/Login.vue
Normal file
245
admin_website/src/views/Login.vue
Normal 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>
|
||||
382
admin_website/src/views/Orders.vue
Normal file
382
admin_website/src/views/Orders.vue
Normal 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>
|
||||
450
admin_website/src/views/Products.vue
Normal file
450
admin_website/src/views/Products.vue
Normal 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>
|
||||
1042
admin_website/src/views/Settings.vue
Normal file
1042
admin_website/src/views/Settings.vue
Normal file
File diff suppressed because it is too large
Load Diff
454
admin_website/src/views/Statistics.vue
Normal file
454
admin_website/src/views/Statistics.vue
Normal 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>
|
||||
365
admin_website/src/views/Users.vue
Normal file
365
admin_website/src/views/Users.vue
Normal 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>
|
||||
@@ -1,6 +1,6 @@
|
||||
# 应用配置
|
||||
NODE_ENV=development
|
||||
PORT=3000
|
||||
PORT=3200
|
||||
JWT_SECRET=your-super-secret-jwt-key-change-in-production
|
||||
|
||||
# 数据库配置
|
||||
|
||||
@@ -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配置
|
||||
|
||||
@@ -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配置
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## 基础信息
|
||||
|
||||
- **基础URL**: `http://localhost:3000/api/v1`
|
||||
- **基础URL**: `http://localhost:3200/api/v1`
|
||||
- **认证方式**: Bearer Token (JWT)
|
||||
- **数据格式**: JSON
|
||||
- **字符编码**: UTF-8
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
## 技术栈
|
||||
- Node.js
|
||||
- Express.js
|
||||
- MongoDB
|
||||
- SQLite(开发环境)
|
||||
- MySQL(生产环境)
|
||||
- Redis
|
||||
|
||||
## 文件结构
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
BIN
backend/database.sqlite
Normal file
Binary file not shown.
5309
backend/package-lock.json
generated
5309
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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
265
backend/routes/admin.js
Normal 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;
|
||||
@@ -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,
|
||||
|
||||
260
backend/scripts/initSQLite.js
Normal file
260
backend/scripts/initSQLite.js
Normal 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);
|
||||
});
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
278
backend/后台管理系统API文档.md
Normal file
278
backend/后台管理系统API文档.md
Normal 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*
|
||||
@@ -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>
|
||||
|
||||
@@ -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">© 2024 爱鉴花. 保留所有权利</p>
|
||||
<p class="text-muted mb-0">© 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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
72
official_website/image-requirements.md
Normal file
72
official_website/image-requirements.md
Normal 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生成的可商用肖像
|
||||
- 产品界面图片需体现现代科技感和用户体验
|
||||
- 植物花卉图片需真实准确,符合植物学特征
|
||||
- 团队形象图片需专业、友好、多样化
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -115,6 +115,13 @@
|
||||
- 识别记录: 3条
|
||||
- 收货地址: 3条
|
||||
|
||||
## 5. 数据库变更记录
|
||||
|
||||
### 2024-03-15 更新
|
||||
- 修复用户表缺少last_login字段的问题
|
||||
- 密码字段从password_hash改为password,使用bcrypt加密存储
|
||||
- 增加数据库初始化脚本,包含默认管理员账号(admin/admin123)
|
||||
|
||||
## 5. 数据库连接信息
|
||||
|
||||
**生产环境**:
|
||||
|
||||
4
部署指南.md
4
部署指南.md
@@ -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
|
||||
|
||||
1
需求文档.md
1
需求文档.md
@@ -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图片识别植物类型的微信小程序应用,为用户提供花卉相关信息、购买、配送等服务。
|
||||
|
||||
2
项目总览.md
2
项目总览.md
@@ -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、云服务器
|
||||
|
||||
|
||||
4
项目说明.md
4
项目说明.md
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user