diff --git a/02.企业服务/04.Apache.md b/02.企业服务/04.Apache.md
index 2335433..eb6c8bf 100644
--- a/02.企业服务/04.Apache.md
+++ b/02.企业服务/04.Apache.md
@@ -271,9 +271,10 @@ TIME-WAIT 0 0 [::ffff:127.0.0.1]:80 [::ffff:127.0.0.1]:33330
- 使用ab进行压力测试,目前的apache默认的mpm模块是event支持上万并发,效果不明显
```bash
-[root@localhost ~]# ab -c 1000 -n 10000 http://127.0.0.1/
+[root@localhost ~]# ab -k -c 1000 -n 10000 http://127.0.0.1/
# -c 即concurrency,用于指定的并发数
# -n 即requests,用于指定压力测试总共的执行次数
+# -k 支持keepalive场景
```
# 4. 虚拟主机
diff --git a/02.企业服务/05.Nginx.md b/02.企业服务/05.Nginx.md
index 8017306..13ecf60 100644
--- a/02.企业服务/05.Nginx.md
+++ b/02.企业服务/05.Nginx.md
@@ -314,7 +314,7 @@ nginx: configuration file /etc/nginx/nginx.conf test is successful
# 测试验证
[root@localhost ~]# curl 172.16.175.129:8001
Hello PC Website!
-[root@localhost ~]# curl -H 'Hostname: p.test.com' 172.16.175.129
+[root@localhost ~]# curl -H 'Hostname: m.test.com' 172.16.175.129
Hello Moblie Website!
[root@localhost ~]# curl 172.16.175.129:8003
curl: (7) Failed to connect to 172.16.175.129 port 8003: Connection refused
@@ -658,7 +658,7 @@ server {
# 重新编译nginx,以前的配置文件不会丢失
[root@localhost ~]# cd /usr/local/src
[root@localhost src]# wget https://github.com/openresty/echo-nginx-module/archive/refs/heads/master.zip
-[root@localhost src]# unzip echo-nginx-module-master.zip
+
[root@localhost src]# mv echo-nginx-module-master echo-nginx-module
[root@localhost src]# cd nginx-1.22.0
[root@localhost nginx-1.22.0]# ./configure --prefix=/apps/nginx \
diff --git a/02.企业服务/07.负载均衡.md b/02.企业服务/07.负载均衡.md
index 8f30391..db97ee5 100644
--- a/02.企业服务/07.负载均衡.md
+++ b/02.企业服务/07.负载均衡.md
@@ -815,6 +815,7 @@ vrrp_instance VI_1 {
priority 150 #优先级
advert_int 1 #监测间隔时间
#use_vmac #使用虚拟mac地址,因为路由问题,可能导致原本的IP不可用
+ vmac_xmit_base
authentication { #认证
auth_type PASS #认证方式
auth_pass 1111 #认证密码
diff --git a/03.数据库/01.Mysql.md b/03.数据库/01.Mysql.md
index 8f8207b..b19be6e 100644
--- a/03.数据库/01.Mysql.md
+++ b/03.数据库/01.Mysql.md
@@ -174,7 +174,7 @@ useradd mysql -s /sbin/nologin -M
-DWITH_DEBUG=0
-[root@localhost mysql-5.6.40]# c #查看上条命令是否运行正确,0表示正确
+[root@localhost mysql-5.6.40]# echo $? #查看上条命令是否运行正确,0表示正确
[root@localhost mysql-5.6.40]# make -j 2
[root@localhost mysql-5.6.40]# make install
```
diff --git a/03.数据库/03.数据库开发项目代码示例/01.笔记网站/.codebuddy/models.json b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/.codebuddy/models.json
new file mode 100644
index 0000000..2e3cc30
--- /dev/null
+++ b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/.codebuddy/models.json
@@ -0,0 +1,47 @@
+{
+ "models": [
+ {
+ "id": "kimi-for-coding",
+ "name": "kimi-for-coding",
+ "vendor": "user",
+ "url": "https://api.kimi.com/coding/v1",
+ "apiKey": "sk-kimi-OLoO20UJ8D3hPtywZqitU6zQO6edvWXhrRcWynoQFWBsmf38slzCG3ZbhvhLZOeS",
+ "supportsToolCall": true,
+ "supportsImages": true,
+ "supportsReasoning": false,
+ "temperature": 1
+ },
+ {
+ "id": "kimicode",
+ "name": "kimicode",
+ "vendor": "user",
+ "url": "https://api.kimi.com/coding/v1",
+ "apiKey": "sk-kimi-1sl2dMlwHcDAM5epuy4EHXhlehH38tcZxzqyOXAK7Og81HofBAcmYnW86KOd9Hdr",
+ "supportsToolCall": true,
+ "supportsImages": true,
+ "supportsReasoning": true,
+ "temperature": 1
+ },
+ {
+ "id": "mimo-v2.5-pro",
+ "name": "mimo-v2.5-pro",
+ "vendor": "user",
+ "url": "https://token-plan-cn.xiaomimimo.com/v1",
+ "apiKey": "tpqqgst8uxezjxw070kyv57vo4ayjm97go9jqk7fh",
+ "supportsToolCall": true,
+ "supportsImages": true,
+ "supportsReasoning": false
+ },
+ {
+ "id": "deepseek-v4-pro",
+ "name": "DeepSeek-V4 Pro",
+ "vendor": "user",
+ "url": "https://api.deepseek.com/chat/completions",
+ "apiKey": "sk-4456a067934f4a2a9582d0f579c7b231",
+ "supportsToolCall": true,
+ "supportsImages": false,
+ "supportsReasoning": true
+ }
+ ]
+}
+
diff --git a/03.数据库/03.数据库开发项目代码示例/01.笔记网站/admin.html b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/admin.html
new file mode 100644
index 0000000..68e4dd5
--- /dev/null
+++ b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/admin.html
@@ -0,0 +1,249 @@
+
+
+
+
+
+后台管理 - 笔记小站
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/03.数据库/03.数据库开发项目代码示例/01.笔记网站/css/style.css b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/css/style.css
new file mode 100644
index 0000000..68e900e
--- /dev/null
+++ b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/css/style.css
@@ -0,0 +1,761 @@
+/* ==================== CSS Variables & Reset ==================== */
+:root {
+ --primary: #6366f1;
+ --primary-hover: #4f46e5;
+ --primary-light: #eef2ff;
+ --primary-soft: #e0e7ff;
+ --success: #10b981;
+ --warning: #f59e0b;
+ --danger: #ef4444;
+ --danger-hover: #dc2626;
+ --bg: #f8fafc;
+ --bg-card: #ffffff;
+ --bg-hover: #f1f5f9;
+ --text: #0f172a;
+ --text-secondary: #64748b;
+ --text-muted: #94a3b8;
+ --border: #e2e8f0;
+ --border-light: #f1f5f9;
+ --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
+ --shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);
+ --shadow-md: 0 4px 6px rgba(0,0,0,0.07), 0 2px 4px rgba(0,0,0,0.06);
+ --shadow-lg: 0 10px 15px rgba(0,0,0,0.1), 0 4px 6px rgba(0,0,0,0.05);
+ --shadow-xl: 0 20px 25px rgba(0,0,0,0.1), 0 10px 10px rgba(0,0,0,0.04);
+ --radius-sm: 8px;
+ --radius: 12px;
+ --radius-lg: 16px;
+ --radius-xl: 20px;
+ --transition: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ --transition-slow: 0.35s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+* { margin: 0; padding: 0; box-sizing: border-box; }
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
+ background: var(--bg);
+ color: var(--text);
+ line-height: 1.6;
+ min-height: 100vh;
+ -webkit-font-smoothing: antialiased;
+}
+
+a { text-decoration: none; color: inherit; }
+button { cursor: pointer; font-family: inherit; border: none; outline: none; }
+input, textarea, select { font-family: inherit; outline: none; }
+
+/* ==================== Animations ==================== */
+@keyframes fadeInUp {
+ from { opacity: 0; transform: translateY(24px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+@keyframes fadeInScale {
+ from { opacity: 0; transform: scale(0.95); }
+ to { opacity: 1; transform: scale(1); }
+}
+@keyframes slideInLeft {
+ from { opacity: 0; transform: translateX(-20px); }
+ to { opacity: 1; transform: translateX(0); }
+}
+@keyframes slideInRight {
+ from { opacity: 0; transform: translateX(20px); }
+ to { opacity: 1; transform: translateX(0); }
+}
+@keyframes float {
+ 0%, 100% { transform: translateY(0); }
+ 50% { transform: translateY(-8px); }
+}
+@keyframes pulse {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.6; }
+}
+
+.anim-fade-up { animation: fadeInUp 0.6s cubic-bezier(0.4,0,0.2,1) both; }
+.anim-fade-up-d1 { animation-delay: 0.05s; }
+.anim-fade-up-d2 { animation-delay: 0.1s; }
+.anim-fade-up-d3 { animation-delay: 0.15s; }
+.anim-fade-up-d4 { animation-delay: 0.2s; }
+.anim-fade-up-d5 { animation-delay: 0.25s; }
+.anim-fade-up-d6 { animation-delay: 0.3s; }
+.anim-scale-in { animation: fadeInScale 0.3s cubic-bezier(0.4,0,0.2,1) both; }
+
+/* ==================== Navbar ==================== */
+.navbar {
+ position: sticky; top: 0; z-index: 100;
+ background: rgba(255,255,255,0.85);
+ backdrop-filter: blur(20px);
+ -webkit-backdrop-filter: blur(20px);
+ border-bottom: 1px solid var(--border);
+ padding: 0 2rem;
+ height: 64px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.navbar-brand {
+ font-size: 1.5rem; font-weight: 800;
+ background: linear-gradient(135deg, var(--primary), #8b5cf6);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ display: flex; align-items: center; gap: 0.5rem;
+}
+
+.logo-icon {
+ width: 36px; height: 36px;
+ background: linear-gradient(135deg, var(--primary), #8b5cf6);
+ border-radius: var(--radius-sm);
+ display: flex; align-items: center; justify-content: center;
+ font-size: 1.2rem;
+ -webkit-text-fill-color: white;
+}
+
+.navbar-actions {
+ display: flex; align-items: center; gap: 0.75rem;
+}
+
+/* ==================== Buttons ==================== */
+.btn {
+ padding: 0.5rem 1.25rem; border-radius: var(--radius-sm);
+ font-size: 0.9rem; font-weight: 600;
+ transition: all var(--transition);
+ display: inline-flex; align-items: center; gap: 0.4rem;
+}
+
+.btn-primary {
+ background: linear-gradient(135deg, var(--primary), #8b5cf6);
+ color: white;
+ box-shadow: 0 2px 8px rgba(99,102,241,0.3);
+}
+.btn-primary:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 14px rgba(99,102,241,0.4);
+}
+
+.btn-outline {
+ background: transparent; color: var(--primary);
+ border: 2px solid var(--primary-soft);
+}
+.btn-outline:hover {
+ background: var(--primary-light); border-color: var(--primary);
+}
+
+.btn-ghost {
+ background: transparent; color: var(--text-secondary);
+}
+.btn-ghost:hover { background: var(--bg-hover); color: var(--text); }
+
+.btn-sm { padding: 0.35rem 0.85rem; font-size: 0.8rem; }
+.btn-danger { background: var(--danger); color: white; }
+.btn-danger:hover { background: var(--danger-hover); transform: translateY(-1px); }
+.btn-success { background: var(--success); color: white; }
+.btn-success:hover { opacity: 0.9; transform: translateY(-1px); }
+.btn-warning { background: var(--warning); color: white; }
+
+/* ==================== User Menu ==================== */
+.user-avatar {
+ width: 36px; height: 36px; border-radius: 50%;
+ background: linear-gradient(135deg, var(--primary), #8b5cf6);
+ color: white;
+ display: flex; align-items: center; justify-content: center;
+ font-weight: 700; font-size: 0.9rem;
+}
+
+.user-menu {
+ display: flex; align-items: center; gap: 0.5rem;
+ padding: 0.3rem 0.85rem; border-radius: 100px;
+ cursor: pointer; transition: var(--transition); position: relative;
+}
+.user-menu:hover { background: var(--bg-hover); }
+
+.user-dropdown {
+ position: absolute; top: calc(100% + 8px); right: 0;
+ background: var(--bg-card); border-radius: var(--radius);
+ box-shadow: var(--shadow-xl); border: 1px solid var(--border);
+ min-width: 180px; padding: 0.5rem;
+ animation: fadeInScale 0.2s ease both;
+ display: none; z-index: 50;
+}
+.user-dropdown.show { display: block; }
+.user-dropdown a {
+ display: flex; align-items: center; gap: 0.5rem;
+ padding: 0.6rem 0.85rem; border-radius: var(--radius-sm);
+ font-size: 0.9rem; transition: var(--transition);
+}
+.user-dropdown a:hover { background: var(--primary-light); color: var(--primary); }
+.user-dropdown .divider { height: 1px; background: var(--border); margin: 0.25rem 0; }
+
+/* ==================== Layout ==================== */
+.container { max-width: 1280px; margin: 0 auto; padding: 2rem; }
+.main-content { display: flex; gap: 2rem; }
+
+/* ==================== Sidebar ==================== */
+.sidebar {
+ width: 260px; flex-shrink: 0;
+ position: sticky; top: 88px; height: fit-content;
+ animation: slideInLeft 0.5s ease both;
+}
+
+.category-panel {
+ background: var(--bg-card); border-radius: var(--radius-lg);
+ box-shadow: var(--shadow); border: 1px solid var(--border);
+ overflow: hidden;
+}
+
+.category-panel-header {
+ padding: 1.25rem 1.25rem 0.75rem;
+ font-weight: 700; font-size: 1rem;
+ display: flex; align-items: center; gap: 0.5rem;
+}
+
+.category-list {
+ list-style: none; padding: 0 0.5rem 0.75rem;
+}
+
+.category-item { margin: 0.15rem 0; }
+
+.category-item a {
+ display: flex; align-items: center; justify-content: space-between;
+ padding: 0.65rem 0.85rem; border-radius: var(--radius-sm);
+ font-size: 0.9rem; transition: all var(--transition);
+ color: var(--text-secondary); cursor: pointer;
+}
+.category-item a:hover,
+.category-item a.active {
+ background: var(--primary-light); color: var(--primary); font-weight: 600;
+}
+
+.category-item .count {
+ background: var(--border-light); padding: 0.15rem 0.55rem;
+ border-radius: 100px; font-size: 0.75rem; font-weight: 600;
+ color: var(--text-muted);
+}
+.category-item a.active .count {
+ background: var(--primary-soft); color: var(--primary);
+}
+
+/* ==================== Content Area ==================== */
+.content-area { flex: 1; min-width: 0; }
+
+.content-header {
+ display: flex; align-items: center; justify-content: space-between;
+ margin-bottom: 1.5rem;
+ animation: fadeInUp 0.5s ease both;
+}
+.content-header h2 { font-size: 1.5rem; font-weight: 700; }
+
+.search-box { position: relative; }
+.search-box input {
+ padding: 0.55rem 1rem 0.55rem 2.5rem;
+ border: 2px solid var(--border); border-radius: 100px;
+ font-size: 0.9rem; width: 260px;
+ transition: all var(--transition); background: var(--bg-card);
+}
+.search-box input:focus {
+ border-color: var(--primary); box-shadow: 0 0 0 3px rgba(99,102,241,0.1);
+ width: 300px;
+}
+.search-box .search-icon {
+ position: absolute; left: 0.85rem; top: 50%;
+ transform: translateY(-50%);
+ color: var(--text-muted); font-size: 0.9rem;
+}
+
+/* ==================== Notes Grid ==================== */
+.notes-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
+ gap: 1.25rem;
+}
+
+.note-card {
+ display: block;
+ background: var(--bg-card); border-radius: var(--radius-lg);
+ border: 1px solid var(--border); padding: 1.5rem;
+ transition: all var(--transition-slow);
+ cursor: pointer; position: relative; overflow: hidden;
+ animation: fadeInUp 0.5s ease both;
+ color: inherit;
+ text-decoration: none;
+}
+.note-card:hover {
+ transform: translateY(-4px);
+ box-shadow: var(--shadow-xl);
+ border-color: var(--primary-soft);
+}
+.note-card::before {
+ content: ''; position: absolute; top: 0; left: 0; right: 0;
+ height: 3px;
+ background: linear-gradient(90deg, var(--primary), #8b5cf6, #a78bfa);
+ transform: scaleX(0); transform-origin: left;
+ transition: transform var(--transition-slow);
+}
+.note-card:hover::before { transform: scaleX(1); }
+
+.note-category {
+ display: inline-flex; align-items: center; gap: 0.3rem;
+ padding: 0.2rem 0.7rem; border-radius: 100px;
+ font-size: 0.75rem; font-weight: 600; margin-bottom: 0.75rem;
+}
+
+.note-title {
+ font-size: 1.1rem; font-weight: 700; margin-bottom: 0.5rem;
+ line-height: 1.4;
+ display: -webkit-box;
+ -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
+}
+
+.note-excerpt {
+ color: var(--text-secondary); font-size: 0.88rem;
+ line-height: 1.6; margin-bottom: 1rem;
+ display: -webkit-box;
+ -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
+}
+
+.note-meta {
+ display: flex; align-items: center; justify-content: space-between;
+ font-size: 0.8rem; color: var(--text-muted);
+ padding-top: 0.75rem; border-top: 1px solid var(--border-light);
+}
+.note-meta span { display: flex; align-items: center; gap: 0.3rem; }
+
+/* ==================== Note Detail Modal ==================== */
+.note-detail-overlay {
+ position: fixed; inset: 0;
+ background: rgba(15,23,42,0.5); backdrop-filter: blur(4px);
+ z-index: 200;
+ display: flex; align-items: center; justify-content: center;
+ animation: fadeIn 0.2s ease both;
+}
+
+.note-detail {
+ background: var(--bg-card); border-radius: var(--radius-xl);
+ width: 90%; max-width: 750px; max-height: 90vh; overflow-y: auto;
+ box-shadow: var(--shadow-xl);
+ animation: fadeInScale 0.3s ease both;
+}
+
+.note-detail-header {
+ padding: 1.5rem 2rem; border-bottom: 1px solid var(--border);
+ display: flex; align-items: center; justify-content: space-between;
+ position: sticky; top: 0; background: var(--bg-card); z-index: 1;
+ border-radius: var(--radius-xl) var(--radius-xl) 0 0;
+}
+.note-detail-header h3 { font-size: 1.3rem; }
+
+.note-detail-close {
+ width: 36px; height: 36px; border-radius: 50%;
+ display: flex; align-items: center; justify-content: center;
+ background: var(--bg-hover); font-size: 1.2rem;
+ transition: var(--transition); cursor: pointer;
+}
+.note-detail-close:hover { background: var(--border); }
+
+.note-detail-body { padding: 2rem; }
+.note-full-content {
+ font-size: 1rem; line-height: 1.8; white-space: pre-wrap;
+}
+.note-detail-info {
+ display: flex; gap: 1.5rem; margin: 1.25rem 0;
+ padding: 1rem; background: var(--bg);
+ border-radius: var(--radius); font-size: 0.85rem;
+ color: var(--text-secondary);
+}
+
+/* ==================== Comments ==================== */
+.comments-section { padding: 0 2rem 2rem; }
+.comments-section h4 {
+ font-size: 1.1rem; margin-bottom: 1rem;
+ display: flex; align-items: center; gap: 0.5rem;
+}
+
+.comment-form {
+ display: flex; gap: 0.75rem; margin-bottom: 1.5rem;
+}
+.comment-form input {
+ flex: 1; padding: 0.65rem 1rem;
+ border: 2px solid var(--border); border-radius: 100px;
+ font-size: 0.9rem; transition: var(--transition);
+}
+.comment-form input:focus {
+ border-color: var(--primary);
+ box-shadow: 0 0 0 3px rgba(99,102,241,0.1);
+}
+.comment-form button {
+ padding: 0.6rem 1.5rem; border-radius: 100px;
+ background: var(--primary); color: white; font-weight: 600;
+ transition: var(--transition);
+}
+.comment-form button:hover { background: var(--primary-hover); }
+
+.comment-list { list-style: none; display: flex; flex-direction: column; gap: 0.75rem; }
+
+.comment-item {
+ background: var(--bg); padding: 1rem 1.25rem;
+ border-radius: var(--radius);
+ animation: fadeInUp 0.4s ease both;
+ display: flex; gap: 0.75rem;
+}
+
+.comment-avatar {
+ width: 36px; height: 36px; border-radius: 50%;
+ background: linear-gradient(135deg, #a78bfa, #6366f1);
+ color: white;
+ display: flex; align-items: center; justify-content: center;
+ font-weight: 700; font-size: 0.8rem; flex-shrink: 0;
+}
+
+.comment-body { flex: 1; }
+.comment-author { font-weight: 600; font-size: 0.9rem; }
+.comment-time { font-size: 0.75rem; color: var(--text-muted); margin-left: 0.5rem; }
+.comment-text { font-size: 0.88rem; color: var(--text-secondary); margin-top: 0.25rem; }
+
+.comment-actions { display: flex; gap: 0.5rem; margin-top: 0.4rem; }
+.comment-actions button {
+ font-size: 0.75rem; color: var(--text-muted); background: none;
+ padding: 0.2rem 0.5rem; border-radius: 4px; transition: var(--transition);
+}
+.comment-actions button:hover { color: var(--danger); background: #fef2f2; }
+
+/* ==================== Modals ==================== */
+.modal-overlay {
+ position: fixed; inset: 0;
+ background: rgba(15,23,42,0.5); backdrop-filter: blur(4px);
+ z-index: 300;
+ display: flex; align-items: center; justify-content: center;
+ animation: fadeIn 0.2s ease both;
+}
+
+.login-modal {
+ background: var(--bg-card); border-radius: var(--radius-xl);
+ padding: 2.5rem; width: 90%; max-width: 420px;
+ box-shadow: var(--shadow-xl);
+ animation: fadeInScale 0.3s ease both;
+}
+.login-modal h2 { font-size: 1.5rem; text-align: center; margin-bottom: 0.5rem; }
+.login-modal .subtitle {
+ text-align: center; color: var(--text-secondary);
+ font-size: 0.9rem; margin-bottom: 2rem;
+}
+
+.form-group { margin-bottom: 1.25rem; }
+.form-group label {
+ display: block; font-size: 0.85rem; font-weight: 600;
+ margin-bottom: 0.4rem; color: var(--text);
+}
+.form-group input,
+.form-group select,
+.form-group textarea {
+ width: 100%; padding: 0.7rem 1rem;
+ border: 2px solid var(--border); border-radius: var(--radius-sm);
+ font-size: 0.9rem; transition: all var(--transition);
+ background: var(--bg);
+}
+.form-group input:focus,
+.form-group select:focus,
+.form-group textarea:focus {
+ border-color: var(--primary);
+ box-shadow: 0 0 0 3px rgba(99,102,241,0.1);
+}
+.form-group textarea { resize: vertical; min-height: 100px; }
+
+.login-modal .btn-primary { width: 100%; justify-content: center; padding: 0.75rem; }
+.login-hint {
+ text-align: center; margin-top: 1rem;
+ font-size: 0.8rem; color: var(--text-muted);
+}
+
+.form-modal {
+ background: var(--bg-card); border-radius: var(--radius-xl);
+ padding: 2rem; width: 90%; max-width: 520px;
+ box-shadow: var(--shadow-xl);
+ animation: fadeInScale 0.3s ease both;
+ max-height: 85vh; overflow-y: auto;
+}
+.form-modal h3 { margin-bottom: 1.5rem; font-size: 1.2rem; }
+.form-actions { display: flex; gap: 0.75rem; justify-content: flex-end; margin-top: 1.5rem; }
+
+/* ==================== Admin ==================== */
+.admin-layout { display: flex; min-height: calc(100vh - 64px); }
+
+.admin-sidebar {
+ width: 240px; background: var(--bg-card);
+ border-right: 1px solid var(--border);
+ padding: 1.5rem 0;
+ display: flex; flex-direction: column;
+ animation: slideInLeft 0.4s ease both;
+}
+
+.admin-nav-item {
+ display: flex; align-items: center; gap: 0.75rem;
+ padding: 0.75rem 1.5rem; font-size: 0.9rem; font-weight: 500;
+ color: var(--text-secondary); transition: var(--transition);
+ border-left: 3px solid transparent; cursor: pointer;
+}
+.admin-nav-item:hover { background: var(--bg-hover); color: var(--text); }
+.admin-nav-item.active {
+ color: var(--primary); background: var(--primary-light);
+ border-left-color: var(--primary); font-weight: 600;
+}
+
+.admin-main {
+ flex: 1; padding: 2rem; overflow-y: auto;
+ animation: fadeInUp 0.5s ease both;
+}
+
+.admin-panel {
+ background: var(--bg-card); border-radius: var(--radius-lg);
+ box-shadow: var(--shadow); border: 1px solid var(--border);
+ overflow: hidden;
+}
+
+.admin-panel-header {
+ padding: 1.25rem 1.5rem; border-bottom: 1px solid var(--border);
+ display: flex; align-items: center; justify-content: space-between;
+}
+.admin-panel-header h3 { font-size: 1.1rem; }
+.admin-panel-body { padding: 1.5rem; }
+
+.admin-table { width: 100%; border-collapse: collapse; }
+.admin-table th {
+ text-align: left; padding: 0.75rem 1rem;
+ font-size: 0.8rem; font-weight: 700; color: var(--text-muted);
+ text-transform: uppercase; letter-spacing: 0.05em;
+ background: var(--bg); border-bottom: 2px solid var(--border);
+}
+.admin-table td {
+ padding: 0.85rem 1rem; font-size: 0.9rem;
+ border-bottom: 1px solid var(--border-light);
+}
+.admin-table tbody tr { transition: var(--transition); }
+.admin-table tbody tr:hover { background: var(--bg-hover); }
+.admin-table .actions { display: flex; gap: 0.4rem; }
+
+.badge {
+ display: inline-flex; padding: 0.2rem 0.6rem;
+ border-radius: 100px; font-size: 0.75rem; font-weight: 600;
+}
+.badge-admin { background: #fef3c7; color: #d97706; }
+.badge-user { background: #e0e7ff; color: #4f46e5; }
+.badge-category { background: #d1fae5; color: #059669; }
+
+/* ==================== Admin Tabs ==================== */
+.admin-header {
+ text-align: center; padding: 2rem 0 1.5rem;
+ animation: fadeInUp 0.5s ease both;
+}
+.admin-header h1 { font-size: 1.8rem; font-weight: 800; }
+
+.admin-tabs {
+ display: flex; gap: 0.5rem; margin-bottom: 2rem;
+ background: var(--bg-card); padding: 0.4rem;
+ border-radius: var(--radius); border: 1px solid var(--border);
+ animation: fadeInUp 0.5s 0.05s ease both;
+}
+
+.admin-tab {
+ flex: 1; padding: 0.7rem 1rem; border-radius: var(--radius-sm);
+ font-size: 0.88rem; font-weight: 600; color: var(--text-secondary);
+ background: transparent; transition: all var(--transition);
+ cursor: pointer; border: none; text-align: center;
+}
+.admin-tab:hover { color: var(--text); background: var(--bg-hover); }
+.admin-tab.active {
+ color: white;
+ background: linear-gradient(135deg, var(--primary), #8b5cf6);
+ box-shadow: var(--shadow-sm);
+}
+
+/* ==================== Admin Section ==================== */
+.section-header {
+ display: flex; align-items: center; justify-content: space-between;
+ margin-bottom: 1.25rem;
+}
+.section-header h2 { font-size: 1.2rem; font-weight: 700; }
+
+.data-table {
+ background: var(--bg-card); border-radius: var(--radius-lg);
+ box-shadow: var(--shadow); border: 1px solid var(--border);
+ overflow: hidden;
+ animation: fadeInUp 0.4s ease both;
+}
+.data-table table { width: 100%; border-collapse: collapse; }
+.data-table thead { background: var(--bg); }
+.data-table th {
+ text-align: left; padding: 0.75rem 1rem;
+ font-size: 0.8rem; font-weight: 700; color: var(--text-muted);
+ text-transform: uppercase; letter-spacing: 0.05em;
+ border-bottom: 2px solid var(--border);
+}
+.data-table td {
+ padding: 0.85rem 1rem; font-size: 0.9rem;
+ border-bottom: 1px solid var(--border-light);
+ color: var(--text);
+}
+.data-table tbody tr { transition: var(--transition); }
+.data-table tbody tr:hover { background: var(--bg-hover); }
+.data-table tbody tr:last-child td { border-bottom: none; }
+.data-table .btn { white-space: nowrap; }
+
+.color-dot {
+ display: inline-block; width: 14px; height: 14px;
+ border-radius: 50%; vertical-align: middle; margin-right: 4px;
+ border: 2px solid rgba(0,0,0,0.05);
+}
+
+/* ==================== Back Link ==================== */
+.back-link {
+ display: inline-flex; align-items: center; gap: 0.3rem;
+ color: var(--primary); font-weight: 600; font-size: 0.9rem;
+ margin-bottom: 1.5rem; transition: var(--transition);
+}
+.back-link:hover { color: var(--primary-hover); transform: translateX(-3px); }
+
+/* ==================== Note Detail Page ==================== */
+.note-detail-header {
+ background: var(--bg-card); border-radius: var(--radius-lg);
+ padding: 2rem; border: 1px solid var(--border);
+ animation: fadeInUp 0.5s ease both;
+}
+.note-detail-header h1 { font-size: 1.6rem; font-weight: 800; margin-bottom: 1rem; }
+
+.note-detail-meta {
+ display: flex; flex-wrap: wrap; gap: 1rem; align-items: center;
+ font-size: 0.85rem; color: var(--text-secondary);
+}
+.note-detail-meta span {
+ display: flex; align-items: center; gap: 0.3rem;
+}
+
+.note-detail-body {
+ background: var(--bg-card); border-radius: var(--radius-lg);
+ padding: 2rem; margin-top: 1rem;
+ border: 1px solid var(--border);
+ animation: fadeInUp 0.5s 0.1s ease both;
+}
+.note-detail-body p {
+ font-size: 1rem; line-height: 1.9; white-space: pre-wrap;
+ color: var(--text);
+}
+
+/* ==================== Comment Section (Page) ==================== */
+.comment-section {
+ margin-top: 1.5rem;
+ background: var(--bg-card); border-radius: var(--radius-lg);
+ padding: 2rem; border: 1px solid var(--border);
+ animation: fadeInUp 0.5s 0.15s ease both;
+}
+.comment-section h3 {
+ font-size: 1.1rem; margin-bottom: 1rem;
+ display: flex; align-items: center; gap: 0.5rem;
+}
+.comment-section .comment-item {
+ background: var(--bg); margin-bottom: 0.75rem;
+}
+.comment-section .comment-item:last-child { margin-bottom: 0; }
+
+.comment-meta { margin-bottom: 0.2rem; }
+.comment-meta strong { font-size: 0.9rem; margin-right: 0.5rem; }
+.comment-meta span { font-size: 0.75rem; color: var(--text-muted); }
+
+.comment-form-wrapper {
+ margin-top: 1.5rem; padding-top: 1.5rem;
+ border-top: 1px solid var(--border);
+}
+.comment-form-wrapper h4 {
+ font-size: 1rem; margin-bottom: 0.75rem;
+ display: flex; align-items: center; gap: 0.4rem;
+}
+.comment-form-wrapper .comment-form {
+ display: flex; flex-direction: column; gap: 0.75rem;
+}
+.comment-form-wrapper .form-group { margin-bottom: 0; }
+.comment-form-wrapper .form-group input,
+.comment-form-wrapper .form-group textarea {
+ width: 100%; padding: 0.7rem 1rem;
+ border: 2px solid var(--border); border-radius: var(--radius-sm);
+ font-size: 0.9rem; transition: all var(--transition);
+ background: var(--bg);
+}
+.comment-form-wrapper .form-group input:focus,
+.comment-form-wrapper .form-group textarea:focus {
+ border-color: var(--primary);
+ box-shadow: 0 0 0 3px rgba(99,102,241,0.1);
+}
+.comment-form-wrapper .btn-primary { align-self: flex-start; }
+
+/* ==================== Toast ==================== */
+.toast-container {
+ position: fixed; top: 1.5rem; right: 1.5rem; z-index: 9999;
+ display: flex; flex-direction: column; gap: 0.5rem;
+}
+
+.toast {
+ padding: 0.85rem 1.25rem; background: var(--bg-card);
+ border-radius: var(--radius); box-shadow: var(--shadow-lg);
+ font-size: 0.9rem; font-weight: 500;
+ animation: slideInRight 0.3s ease both;
+ display: flex; align-items: center; gap: 0.5rem;
+ border-left: 4px solid;
+}
+.toast-success { border-left-color: var(--success); }
+.toast-error { border-left-color: var(--danger); }
+.toast-info { border-left-color: var(--primary); }
+
+/* ==================== Empty State ==================== */
+.empty-state {
+ text-align: center; padding: 3rem 2rem; color: var(--text-muted);
+}
+.empty-state .empty-icon { font-size: 3.5rem; margin-bottom: 1rem; animation: float 3s ease-in-out infinite; }
+.empty-state p { font-size: 0.95rem; }
+
+/* ==================== Hero Section ==================== */
+.hero {
+ text-align: center; padding: 3rem 2rem;
+ background: linear-gradient(135deg, #eef2ff 0%, #faf5ff 50%, #f0f9ff 100%);
+ border-radius: var(--radius-xl); margin-bottom: 2rem;
+ animation: fadeInUp 0.6s ease both;
+}
+.hero h1 {
+ font-size: 2.2rem; font-weight: 800;
+ background: linear-gradient(135deg, var(--primary), #8b5cf6);
+ -webkit-background-clip: text; -webkit-text-fill-color: transparent;
+ margin-bottom: 0.75rem;
+}
+.hero p { color: var(--text-secondary); font-size: 1.05rem; max-width: 500px; margin: 0 auto; }
+
+/* ==================== Responsive ==================== */
+@media (max-width: 768px) {
+ .main-content { flex-direction: column; }
+ .sidebar { width: 100%; position: static; }
+ .category-list { display: flex; flex-wrap: wrap; gap: 0.5rem; }
+ .category-item a { padding: 0.4rem 0.7rem; font-size: 0.8rem; }
+ .notes-grid { grid-template-columns: 1fr; }
+ .admin-layout { flex-direction: column; }
+ .admin-sidebar {
+ width: 100%; flex-direction: row; overflow-x: auto;
+ padding: 0.75rem; border-right: none; border-bottom: 1px solid var(--border);
+ }
+ .admin-nav-item {
+ border-left: none; border-bottom: 2px solid transparent;
+ white-space: nowrap; padding: 0.5rem 1rem;
+ }
+ .admin-nav-item.active { border-left-color: transparent; border-bottom-color: var(--primary); }
+ .admin-main { padding: 1rem; }
+ .navbar { padding: 0 1rem; }
+ .container { padding: 1rem; }
+ .hero { padding: 2rem 1rem; }
+ .hero h1 { font-size: 1.6rem; }
+ .search-box input { width: 180px; }
+ .search-box input:focus { width: 220px; }
+}
+
+/* Scrollbar */
+::-webkit-scrollbar { width: 6px; }
+::-webkit-scrollbar-track { background: transparent; }
+::-webkit-scrollbar-thumb { background: var(--border); border-radius: 100px; }
+::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
diff --git a/03.数据库/03.数据库开发项目代码示例/01.笔记网站/function/db.php b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/function/db.php
new file mode 100644
index 0000000..18cfed2
--- /dev/null
+++ b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/function/db.php
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/03.数据库/03.数据库开发项目代码示例/01.笔记网站/function/user.php b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/function/user.php
new file mode 100644
index 0000000..465e23f
--- /dev/null
+++ b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/function/user.php
@@ -0,0 +1,57 @@
+ 10004, 'status' => 'error', 'message' => '数据库连接失败']);
+ exit;
+}
+
+// 测试用户名是否已存在
+function testUsername(mysqli $conn, string $username): bool
+{
+ $sql = "select * from users where username = '$username'"; // sql注入风险
+ $result = mysqli_query($conn, $sql);
+ if (mysqli_num_rows($result) > 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// 如果action是register,则注册用户
+if ($action == 'register') {
+ $username = $data['username'];
+ $password = $data['password'];
+ $email = $data['email'];
+ if (testUsername($conn, $username)) {
+ echo json_encode(['code' => 10001, 'status' => 'error', 'message' => '用户名已存在']);
+ } else {
+ $sql = "insert into users (username, password, email) values ('$username', '$password', '$email')";
+ $result = mysqli_query($conn, $sql);
+ if ($result) {
+ echo json_encode(['code' => 0, 'status' => 'success', 'message' => '注册成功']);
+ } else {
+ echo json_encode(['code' => 10002, 'status' => 'error', 'message' => '注册失败']);
+ }
+ }
+}
+
+// login的逻辑
+if ($action == 'login') {
+ $username = $data['username'];
+ $password = $data['password'];
+ $sql = "select * from users where username = '$username' and password = '$password'";
+ $result = mysqli_query($conn, $sql);
+ if (mysqli_num_rows($result) > 0) {
+ echo json_encode(['code' => 0, 'status' => 'success', 'message' => '登录成功']);
+ } else {
+ echo json_encode(['code' => 10003, 'status' => 'error', 'message' => '用户名或密码错误']);
+ }
+}
diff --git a/03.数据库/03.数据库开发项目代码示例/01.笔记网站/index.html b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/index.html
new file mode 100644
index 0000000..8fe568d
--- /dev/null
+++ b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/index.html
@@ -0,0 +1,126 @@
+
+
+
+
+
+笔记小站 - 发现精彩内容
+
+
+
+
+
+
+
+
+
+
+
📝 发现精彩内容
+
分享技术、记录生活、沉淀思考——这里有你想看的一切
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/03.数据库/03.数据库开发项目代码示例/01.笔记网站/js/common.js b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/js/common.js
new file mode 100644
index 0000000..1c5fbd5
--- /dev/null
+++ b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/js/common.js
@@ -0,0 +1,61 @@
+/* ==================== Shared Utilities ==================== */
+
+/* URL parameter helper */
+function getUrlParam(name) {
+ const url = new URL(window.location.href);
+ return url.searchParams.get(name);
+}
+
+/* Toast notification */
+function showToast(msg, type) {
+ type = type || 'info';
+ var icons = { success: '✅', error: '❌', info: 'ℹ️' };
+ var container = document.getElementById('toastContainer');
+ if (!container) { container = document.createElement('div'); container.id = 'toastContainer'; container.className = 'toast-container'; document.body.appendChild(container); }
+ var t = document.createElement('div');
+ t.className = 'toast toast-' + type;
+ t.innerHTML = '' + (icons[type] || '') + ' ' + msg;
+ container.appendChild(t);
+ setTimeout(function () { t.remove(); }, 3000);
+}
+
+/* Render shared navbar */
+function renderNavbar() {
+ var nav = document.getElementById('navbarActions');
+ if (!nav) return;
+ nav.innerHTML =
+ '🏠 首页' +
+ '⚙️ 后台管理' +
+ '✍️ 注册' +
+ '🔑 登录';
+}
+
+/* Category badge helper */
+function categoryBadge(cat) {
+ if (!cat) return '';
+ return '' + cat.icon + ' ' + cat.name + '';
+}
+
+/* Empty state helper */
+function emptyState(icon, text) {
+ return '';
+}
+
+/* Modal helpers */
+function renderModal(title, content) {
+ var div = document.createElement('div');
+ div.className = 'modal-overlay';
+ div.onclick = function (e) { if (e.target === div) closeModal(); };
+ div.innerHTML = '' + title + '
' + content + '';
+ document.body.appendChild(div);
+}
+
+function closeModal() {
+ var o = document.querySelector('.modal-overlay');
+ if (o) o.remove();
+}
+
+/* ==================== Bootstrap on every page ==================== */
+document.addEventListener('DOMContentLoaded', function () {
+ renderNavbar();
+});
diff --git a/03.数据库/03.数据库开发项目代码示例/01.笔记网站/js/data.js b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/js/data.js
new file mode 100644
index 0000000..75dd23a
--- /dev/null
+++ b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/js/data.js
@@ -0,0 +1,37 @@
+/* ==================== Mock Data Store ==================== */
+const DATA = {
+ users: [
+ { id: 1, username: 'admin', password: 'admin123', nickname: '管理员', role: 'admin', avatar: '👑', createdAt: '2024-01-01' },
+ { id: 2, username: 'xiaoming', password: '123456', nickname: '小明', role: 'user', avatar: '😊', createdAt: '2024-03-15' },
+ { id: 3, username: 'xiaohong', password: '123456', nickname: '小红', role: 'user', avatar: '🌸', createdAt: '2024-05-20' },
+ ],
+ categories: [
+ { id: 1, name: '技术笔记', icon: '💻', color: '#6366f1' },
+ { id: 2, name: '生活随笔', icon: '🌿', color: '#10b981' },
+ { id: 3, name: '读书心得', icon: '📚', color: '#f59e0b' },
+ { id: 4, name: '旅行记录', icon: '✈️', color: '#06b6d4' },
+ ],
+ notes: [
+ { id: 1, categoryId: 1, title: 'JavaScript 异步编程完全指南', excerpt: '掌握 Promise、async/await 和异步错误处理,让你的 JavaScript 代码更加优雅高效。', content: '在现代 JavaScript 开发中,异步编程是每个开发者必须掌握的技能。\n\n## Promise\nPromise 是 ES6 引入的一种异步编程解决方案。它代表一个异步操作的最终完成或失败。\n\n## async/await\nasync/await 是 ES2017 引入的语法糖,让异步代码看起来像同步代码。使用 try/catch 来处理异步错误比 .catch() 链更清晰易读。\n\n## 并发控制\n使用 Promise.all() 可以同时发起多个请求,显著提升性能。但需要注意控制并发数量。', authorId: 1, authorName: '管理员', views: 1520, likes: 89, createdAt: '2025-08-10' },
+ { id: 2, categoryId: 1, title: 'CSS Grid 布局实战技巧', excerpt: '从基础到进阶,深入了解 CSS Grid 布局系统,掌握现代网页布局的核心技术。', content: 'CSS Grid 是二维布局系统,让我们能同时控制行和列。\n\n## 容器属性\n- grid-template-columns:定义列\n- grid-template-rows:定义行\n- gap:行列间距\n- grid-template-areas:命名区域\n\n## 项目属性\n- grid-column:控制列跨度\n- grid-row:控制行跨度\n\n使用 Grid 可以轻松实现自适应布局,告别传统 hack 方案。', authorId: 1, authorName: '管理员', views: 980, likes: 56, createdAt: '2025-08-08' },
+ { id: 3, categoryId: 2, title: '周末烘焙日记:自制法式可颂', excerpt: '金黄酥脆、层次分明的法式可颂,一次成功的家庭烘焙体验全记录。', content: '这周末终于尝试了心心念念的法式可颂!\n\n## 材料准备\n- 高筋面粉 250g\n- 黄油 150g(裹入用)\n- 酵母 5g / 糖 25g / 盐 5g / 温水 130ml\n\n## 步骤\n1. 和面:将面粉、糖、盐、酵母混合,加入温水揉成光滑面团\n2. 一次发酵:室温发酵 1 小时至 2 倍大\n3. 裹黄油:将面团擀开,放入黄油片,进行四折\n4. 冷藏松弛:每次折叠后冷藏 30 分钟\n5. 成型烘烤:切成三角形卷起,200°C 烤 18 分钟\n\n成品金黄酥脆,层次分明,配上一杯拿铁,完美的周末下午!', authorId: 2, authorName: '小明', views: 756, likes: 134, createdAt: '2025-08-06' },
+ { id: 4, categoryId: 3, title: '《人类简史》读书笔记', excerpt: '认知革命、农业陷阱与科学革命——重新审视人类文明的演进与代价。', content: '尤瓦尔·赫拉利的《人类简史》是一本让人重新思考人类文明的书。\n\n## 认知革命\n大约 7 万年前,智人出现了新的思维和沟通方式。语言不仅能描述现实,还能创造虚构的故事,让大规模合作成为可能。\n\n## 农业革命\n农业革命并非人类的进步,而是一个陷阱。农民的生活比狩猎采集者更辛苦,但人口增长让人无法回头。\n\n## 科学革命\n现代科学与帝国、资本主义的结合,创造了今天的世界。科学的核心是"承认无知",正是这种谦逊推动了进步。\n\n## 感想\n我们所谓的进步,到底让谁受益?这是个值得深思的问题。', authorId: 3, authorName: '小红', views: 2340, likes: 267, createdAt: '2025-08-04' },
+ { id: 5, categoryId: 1, title: 'React Hooks 深入解析', excerpt: '从 useState 到自定义 Hook,全面解析 React Hooks 的使用场景与最佳实践。', content: 'React Hooks 是 React 16.8 引入的革命性特性。\n\n## useState\n最基础的 Hook,用于在函数组件中添加状态。\n\n## useEffect\n处理副作用的神器。可以用来做数据获取、订阅、DOM 操作等。记得返回清理函数。\n\n## useCallback & useMemo\n性能优化的关键。useCallback 缓存函数,useMemo 缓存计算结果。\n\n## 自定义 Hook\n封装可复用的逻辑,是 Hooks 最强大的特性之一。', authorId: 1, authorName: '管理员', views: 1890, likes: 145, createdAt: '2025-08-02' },
+ { id: 6, categoryId: 4, title: '京都红叶季旅行攻略', excerpt: '岚山竹林、清水寺夜枫、永观堂红叶——京都最美的秋日体验全记录。', content: '十一月的京都,是一年中最美的季节。\n\n## 推荐赏枫地点\n### 岚山\n天龙寺的庭院和竹林小径是最佳拍照地,建议清晨前往避开人群。\n\n### 清水寺\n站在清水舞台上俯瞰京都市区,红叶与古建筑交相辉映。\n\n### 永观堂\n被誉为"红叶的永观堂",夜枫尤其惊艳。\n\n## 交通建议\n购买京都巴士一日券,500日元即可无限次乘坐。\n\n## 美食推荐\n- 抹茶甜品:中村藤吉\n- 汤豆腐:南禅寺顺正\n- 拉面:一兰拉面', authorId: 2, authorName: '小明', views: 2100, likes: 320, createdAt: '2025-07-28' },
+ { id: 7, categoryId: 2, title: '阳台花园养成记', excerpt: '在城市阳台打造迷你花园,多肉、薄荷、月季——用绿植治愈生活。', content: '在城市的钢筋水泥中,拥有一个属于自己的小花园,是治愈生活最好的方式。\n\n## 适合阳台的植物\n- 多肉植物:耐旱好养,品种丰富\n- 薄荷:清香怡人,还能泡茶\n- 月季:阳台也能开花不断\n- 绿萝:垂吊种植,不占空间\n\n## 养护心得\n每天早晚浇水时,观察植物的变化是最治愈的时刻。看着新芽冒出、花苞绽放,生活的小确幸就在这些细微之处。', authorId: 3, authorName: '小红', views: 645, likes: 89, createdAt: '2025-07-25' },
+ { id: 8, categoryId: 3, title: '《设计心理学》核心要点', excerpt: '可视性、反馈、映射——六个核心原则,打造用户友好的产品设计。', content: '唐纳德·诺曼的《设计心理学》是每个产品设计师的必读书。\n\n## 六大核心原则\n1. 可视性 - 功能要能被看见\n2. 反馈 - 每个操作都要有反馈\n3. 约束 - 物理、文化、逻辑约束\n4. 映射 - 控制与效果的关系\n5. 一致性 - 统一的设计语言\n6. 容错性 - 允许犯错并方便恢复\n\n## 人本设计\n好的设计应该是"不可见"的——它如此自然,以至于用户感觉不到设计的存在。', authorId: 1, authorName: '管理员', views: 1200, likes: 178, createdAt: '2025-07-20' },
+ ],
+ comments: [
+ { id: 1, noteId: 1, userId: 2, userName: '小明', userAvatar: '😊', content: '写得太清晰了!Promise 那部分解决了我的疑惑。', createdAt: '2025-08-11 14:30' },
+ { id: 2, noteId: 1, userId: 3, userName: '小红', userAvatar: '🌸', content: 'async/await 示例很好理解,期待更多这类文章!', createdAt: '2025-08-11 16:00' },
+ { id: 3, noteId: 1, userId: 1, userName: '管理员', userAvatar: '👑', content: '感谢支持!后续会出更多异步相关的文章。', createdAt: '2025-08-11 17:20' },
+ { id: 4, noteId: 3, userId: 1, userName: '管理员', userAvatar: '👑', content: '看着就很好吃,周末我也要试试!', createdAt: '2025-08-07 09:00' },
+ { id: 5, noteId: 3, userId: 3, userName: '小红', userAvatar: '🌸', content: '烤箱温度有什么需要注意的吗?', createdAt: '2025-08-07 10:30' },
+ { id: 6, noteId: 3, userId: 2, userName: '小明', userAvatar: '😊', content: '每个烤箱脾气不同,建议买个烤箱温度计。', createdAt: '2025-08-07 11:00' },
+ { id: 7, noteId: 4, userId: 2, userName: '小明', userAvatar: '😊', content: '这本书我去年也读了,农业革命那章确实颠覆认知。', createdAt: '2025-08-05 15:00' },
+ { id: 8, noteId: 4, userId: 3, userName: '小红', userAvatar: '🌸', content: '接着看《未来简史》吧,同样精彩!', createdAt: '2025-08-05 16:30' },
+ { id: 9, noteId: 6, userId: 3, userName: '小红', userAvatar: '🌸', content: '太美了!今年秋天一定要去一次。', createdAt: '2025-07-30 20:00' },
+ { id: 10, noteId: 6, userId: 1, userName: '管理员', userAvatar: '👑', content: '岚山的竹林确实值得早起去,体验完全不一样。', createdAt: '2025-07-31 08:00' },
+ { id: 11, noteId: 8, userId: 2, userName: '小明', userAvatar: '😊', content: '总结得很到位,设计心理学确实值得反复读。', createdAt: '2025-07-22 11:00' },
+ ],
+};
diff --git a/03.数据库/03.数据库开发项目代码示例/01.笔记网站/login.html b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/login.html
new file mode 100644
index 0000000..914aaa5
--- /dev/null
+++ b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/login.html
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+ 登录 - 笔记小站
+
+
+
+
+
+
+
+
+
+
🔑 欢迎登录
+
登录后即可使用完整功能
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/03.数据库/03.数据库开发项目代码示例/01.笔记网站/note.html b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/note.html
new file mode 100644
index 0000000..715da35
--- /dev/null
+++ b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/note.html
@@ -0,0 +1,106 @@
+
+
+
+
+
+笔记详情 - 笔记小站
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/03.数据库/03.数据库开发项目代码示例/01.笔记网站/register.html b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/register.html
new file mode 100644
index 0000000..76f010f
--- /dev/null
+++ b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/register.html
@@ -0,0 +1,104 @@
+
+
+
+
+
+注册 - 笔记小站
+
+
+
+
+
+
+
+
+
✍️ 注册账号
+
创建账号,开始你的笔记之旅
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/03.数据库/03.数据库开发项目代码示例/01.笔记网站/spec.md b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/spec.md
new file mode 100644
index 0000000..633b8c9
--- /dev/null
+++ b/03.数据库/03.数据库开发项目代码示例/01.笔记网站/spec.md
@@ -0,0 +1,220 @@
+- 技术栈
+
+ - 原生HTML+CSS+JS
+ - php8.0
+ - Mysql5.7
+- 界面设计
+
+ - 站点主页无需登录,就可以进行内容的浏览
+ - 点击登录,出现登录页面,管理员登录后进入后台管理,普通用户继续浏览主页
+- 用户管理
+
+ - 需要支持用户登录,用户分为管理员和普通用户
+ - 管理员支持对笔记、分类、评论、用户有完整的权限
+ - 普通用户只有查看笔记、分类、评论和发表评论的权限
+- 分类管理
+- 笔记管理
+- 评论管理
+
+---
+
+## 数据库设计
+
+### 数据库信息
+
+- 数据库名: `site03`
+- 字符集: `utf8mb4`
+- 排序规则: `utf8mb4_unicode_ci`
+
+```sql
+CREATE DATABASE IF NOT EXISTS `site03`
+ DEFAULT CHARACTER SET utf8mb4
+ COLLATE utf8mb4_unicode_ci;
+
+USE `site03`;
+```
+
+---
+
+### 表结构
+
+#### 1. users — 用户表
+
+| 字段 | 类型 | 必填 | 说明 |
+| ---------- | ---------------- | ---- | --------------------------------- |
+| id | INT UNSIGNED | 是 | 主键,自增 |
+| username | VARCHAR(50) | 是 | 用户名,唯一 |
+| password | VARCHAR(255) | 是 | 密码哈希(password_hash) |
+| role | ENUM('admin','user') | 是 | 角色:管理员 / 普通用户,默认 user |
+| email | VARCHAR(100) | 否 | 邮箱 |
+| avatar | VARCHAR(255) | 否 | 头像路径 |
+| status | TINYINT(1) | 是 | 状态:0=禁用, 1=启用,默认 1 |
+| created_at | DATETIME | 是 | 创建时间 |
+| updated_at | DATETIME | 是 | 更新时间 |
+
+```sql
+CREATE TABLE `users` (
+ `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ `username` VARCHAR(50) NOT NULL,
+ `password` VARCHAR(255) NOT NULL,
+ `role` ENUM('admin','user') NOT NULL DEFAULT 'user',
+ `email` VARCHAR(100) DEFAULT NULL,
+ `avatar` VARCHAR(255) DEFAULT NULL,
+ `status` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '0=禁用 1=启用',
+ `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_username` (`username`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+```
+
+---
+
+#### 2. categories — 分类表
+
+| 字段 | 类型 | 必填 | 说明 |
+| ----------- | ---------------- | ---- | ---------------------------- |
+| id | INT UNSIGNED | 是 | 主键,自增 |
+| name | VARCHAR(50) | 是 | 分类名称,唯一 |
+| slug | VARCHAR(50) | 是 | URL 别名,唯一 |
+| description | TEXT | 否 | 分类描述 |
+| sort_order | INT UNSIGNED | 是 | 排序权重,默认 0 |
+| created_at | DATETIME | 是 | 创建时间 |
+| updated_at | DATETIME | 是 | 更新时间 |
+
+```sql
+CREATE TABLE `categories` (
+ `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ `name` VARCHAR(50) NOT NULL,
+ `slug` VARCHAR(50) NOT NULL,
+ `description` TEXT DEFAULT NULL,
+ `sort_order` INT UNSIGNED NOT NULL DEFAULT 0,
+ `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_name` (`name`),
+ UNIQUE KEY `uk_slug` (`slug`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+```
+
+---
+
+#### 3. notes — 笔记表
+
+| 字段 | 类型 | 必填 | 说明 |
+| ----------- | --------------------- | ---- | ----------------------------------- |
+| id | INT UNSIGNED | 是 | 主键,自增 |
+| title | VARCHAR(255) | 是 | 笔记标题 |
+| content | LONGTEXT | 是 | 笔记正文 |
+| summary | VARCHAR(500) | 否 | 摘要 |
+| category_id | INT UNSIGNED | 是 | 所属分类,外键 |
+| user_id | INT UNSIGNED | 是 | 作者,外键 |
+| status | ENUM('draft','published') | 是 | 状态:草稿 / 已发布,默认 draft |
+| view_count | INT UNSIGNED | 是 | 浏览次数,默认 0 |
+| created_at | DATETIME | 是 | 创建时间 |
+| updated_at | DATETIME | 是 | 更新时间 |
+
+```sql
+CREATE TABLE `notes` (
+ `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ `title` VARCHAR(255) NOT NULL,
+ `content` LONGTEXT NOT NULL,
+ `summary` VARCHAR(500) DEFAULT NULL,
+ `category_id` INT UNSIGNED NOT NULL,
+ `user_id` INT UNSIGNED NOT NULL,
+ `status` ENUM('draft','published') NOT NULL DEFAULT 'draft',
+ `view_count` INT UNSIGNED NOT NULL DEFAULT 0,
+ `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`),
+ KEY `idx_category_id` (`category_id`),
+ KEY `idx_user_id` (`user_id`),
+ KEY `idx_status` (`status`),
+ CONSTRAINT `fk_notes_category` FOREIGN KEY (`category_id`) REFERENCES `categories`(`id`) ON DELETE RESTRICT,
+ CONSTRAINT `fk_notes_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+```
+
+---
+
+#### 4. comments — 评论表
+
+| 字段 | 类型 | 必填 | 说明 |
+| ---------- | ----------------------------------- | ---- | --------------------------------- |
+| id | INT UNSIGNED | 是 | 主键,自增 |
+| note_id | INT UNSIGNED | 是 | 所属笔记,外键 |
+| user_id | INT UNSIGNED | 是 | 评论人,外键 |
+| parent_id | INT UNSIGNED | 否 | 父评论 ID(支持回复),外键 |
+| content | TEXT | 是 | 评论内容 |
+| status | ENUM('pending','approved','rejected') | 是 | 审核状态,默认 pending |
+| created_at | DATETIME | 是 | 创建时间 |
+| updated_at | DATETIME | 是 | 更新时间 |
+
+```sql
+CREATE TABLE `comments` (
+ `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ `note_id` INT UNSIGNED NOT NULL,
+ `user_id` INT UNSIGNED NOT NULL,
+ `parent_id` INT UNSIGNED DEFAULT NULL,
+ `content` TEXT NOT NULL,
+ `status` ENUM('pending','approved','rejected') NOT NULL DEFAULT 'pending',
+ `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`),
+ KEY `idx_note_id` (`note_id`),
+ KEY `idx_user_id` (`user_id`),
+ KEY `idx_parent_id` (`parent_id`),
+ CONSTRAINT `fk_comments_note` FOREIGN KEY (`note_id`) REFERENCES `notes`(`id`) ON DELETE CASCADE,
+ CONSTRAINT `fk_comments_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE RESTRICT,
+ CONSTRAINT `fk_comments_parent` FOREIGN KEY (`parent_id`) REFERENCES `comments`(`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+```
+
+---
+
+### 索引说明
+
+| 表 | 索引名 | 字段 | 类型 | 用途 |
+| ---------- | ------------------- | ----------- | ------ | -------------------------- |
+| users | PRIMARY | id | 主键 | 用户唯一标识 |
+| users | uk_username | username | 唯一 | 用户名唯一约束 |
+| categories | PRIMARY | id | 主键 | 分类唯一标识 |
+| categories | uk_name | name | 唯一 | 分类名唯一约束 |
+| categories | uk_slug | slug | 唯一 | URL 别名唯一约束 |
+| notes | PRIMARY | id | 主键 | 笔记唯一标识 |
+| notes | idx_category_id | category_id | 普通 | 按分类查询笔记 |
+| notes | idx_user_id | user_id | 普通 | 按作者查询笔记 |
+| notes | idx_status | status | 普通 | 按发布状态筛选 |
+| comments | PRIMARY | id | 主键 | 评论唯一标识 |
+| comments | idx_note_id | note_id | 普通 | 按笔记查询评论 |
+| comments | idx_user_id | user_id | 普通 | 按用户查询评论 |
+| comments | idx_parent_id | parent_id | 普通 | 查询回复链 |
+
+---
+
+### 外键约束说明
+
+- `notes.category_id` → `categories.id`: RESTRICT,存在笔记时不可删除分类
+- `notes.user_id` → `users.id`: RESTRICT,存在笔记时不可删除用户
+- `comments.note_id` → `notes.id`: CASCADE,删除笔记时级联删除评论
+- `comments.user_id` → `users.id`: RESTRICT,存在评论时不可删除用户
+- `comments.parent_id` → `comments.id`: CASCADE,删除父评论时级联删除子回复
+
+---
+
+### 种子数据
+
+```sql
+-- 默认管理员: admin / admin123
+INSERT INTO `users` (`username`, `password`, `role`) VALUES
+('admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'admin');
+
+-- 示例分类
+INSERT INTO `categories` (`name`, `slug`, `description`, `sort_order`) VALUES
+('技术笔记', 'tech', '编程与技术相关笔记', 1),
+('生活随笔', 'life', '日常生活与感悟', 2),
+('读书笔记', 'reading', '书籍阅读与书评', 3);
+```
+
+> 密码 `admin123` 使用 PHP `password_hash('admin123', PASSWORD_DEFAULT)` 生成,实际部署时请更换。
+> 详细搭建说明参见 [spec.md](#) 附录。
\ No newline at end of file