feat(nav-category): support dynamic home grid categories in admin and uniapp

这个提交包含在:
xcbyy
2026-10-10 09:08:18 +08:00
父节点 f010cfb7ab
当前提交 51682fffea
共有 11 个文件被更改,包括 1077 次插入 和 6 次删除

查看文件

@@ -317,6 +317,26 @@ export async function initDatabase(): Promise<void> {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`);
// 17. 首页金刚区活动品类导航配置表
await conn.query(`
CREATE TABLE IF NOT EXISTS nav_categories (
id INT PRIMARY KEY AUTO_INCREMENT,
code VARCHAR(64) UNIQUE NOT NULL,
name VARCHAR(64) NOT NULL,
icon TEXT NOT NULL,
bg_class VARCHAR(64) DEFAULT 'bg-indigo',
cat VARCHAR(64) DEFAULT 'all',
target_url VARCHAR(255) NOT NULL DEFAULT '/pages/activity/list?category=ALL',
badge_text VARCHAR(32) DEFAULT '',
sort_order INT DEFAULT 0,
status TINYINT DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_sort (sort_order),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`);
// 初始化配置单条记录
const [cfgRows]: any = await conn.query('SELECT COUNT(*) as count FROM finance_config WHERE id = 1');
if (cfgRows[0].count === 0) {
@@ -525,4 +545,23 @@ async function seedInitialDataIfEmpty(conn: any): Promise<void> {
(3, '精酿清吧微醺畅聊 | 线下陪吃陪喝立减30元', '望京跳海酒馆 · 慢调交友', '同城陪喝', '同城陪喝', 'badge-bar', 'linear-gradient(135deg, #059669 0%, #10B981 100%)', 'https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?w=800', '/pages/activity/detail?id=act_103', 3, 1);
`);
}
// 14. 种子金刚区活动品类导航
const [navRows]: any = await conn.query('SELECT COUNT(*) as count FROM nav_categories');
if (navRows[0].count === 0) {
await conn.query(`
INSERT INTO nav_categories (id, code, name, icon, bg_class, cat, target_url, badge_text, sort_order, status)
VALUES
(1, 'esports', '电子竞技', 'gameAll', 'bg-indigo', 'all', '/pages/activity/list?category=ALL', 'HOT', 1, 1),
(2, 'meal', '美食探店', 'utensils', 'bg-amber', 'offline_meal', '/pages/activity/list?category=MEAL', 'AA制', 2, 1),
(3, 'sing', 'KTV嗨唱', 'micVocal', 'bg-rose', 'offline_sing', '/pages/activity/list?category=SING', '热度高', 3, 1),
(4, 'billiards', '同城台球', 'billiards', 'bg-sky', 'offline_play', '/pages/activity/list?category=ALL', '', 4, 1),
(5, 'hiking', '户外爬山', 'mountain', 'bg-emerald', 'offline_play', '/pages/activity/list?category=ALL', '周末', 5, 1),
(6, 'drink', '下午茶歇', 'coffee', 'bg-teal', 'offline_drink', '/pages/activity/list?category=DRINK', '', 6, 1),
(7, 'boardgame', '剧本桌游', 'dice', 'bg-purple', 'offline_play', '/pages/activity/list?category=ALL', '', 7, 1),
(8, 'fitness', '运动健身', 'fitness', 'bg-orange', 'offline_play', '/pages/activity/list?category=ALL', '', 8, 1),
(9, 'bar', '同城微醺', 'warmFlame', 'bg-rose', 'offline_drink', '/pages/activity/list?category=DRINK', '清吧', 9, 1),
(10, 'more', '全部活动', 'gridAll', 'bg-blue', 'all', '/pages/activity/list?category=ALL', '', 10, 1);
`);
}
}

查看文件

@@ -851,6 +851,154 @@ export class MysqlRepository {
return res.affectedRows > 0;
}
async getNavCategories(): Promise<any[]> {
const [rows]: any = await pool.query(`
SELECT
id, code, name, icon, bg_class as bgClass, cat,
target_url as targetUrl, badge_text as badgeText,
sort_order as sortOrder, status,
created_at as createdAt, updated_at as updatedAt
FROM nav_categories
WHERE status = 1
ORDER BY sort_order ASC, id ASC
`);
return rows;
}
async getAdminNavCategories(): Promise<any[]> {
const [rows]: any = await pool.query(`
SELECT
id, code, name, icon, bg_class as bgClass, cat,
target_url as targetUrl, badge_text as badgeText,
sort_order as sortOrder, status,
created_at as createdAt, updated_at as updatedAt
FROM nav_categories
ORDER BY sort_order ASC, id ASC
`);
return rows;
}
async createNavCategory(data: {
code?: string;
name: string;
icon: string;
bgClass?: string;
cat?: string;
targetUrl?: string;
badgeText?: string;
sortOrder?: number;
status?: number;
}): Promise<any> {
const code = data.code || `cat_${Date.now()}`;
const name = data.name;
const icon = data.icon;
const bgClass = data.bgClass || 'bg-indigo';
const cat = data.cat || 'all';
const targetUrl = data.targetUrl || '/pages/activity/list?category=ALL';
const badgeText = data.badgeText || '';
const sortOrder = Number(data.sortOrder) || 0;
const status = data.status !== undefined ? Number(data.status) : 1;
const [res]: any = await pool.query(`
INSERT INTO nav_categories (code, name, icon, bg_class, cat, target_url, badge_text, sort_order, status)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`, [code, name, icon, bgClass, cat, targetUrl, badgeText, sortOrder, status]);
const newId = res.insertId;
const [rows]: any = await pool.query(`
SELECT
id, code, name, icon, bg_class as bgClass, cat,
target_url as targetUrl, badge_text as badgeText,
sort_order as sortOrder, status,
created_at as createdAt, updated_at as updatedAt
FROM nav_categories
WHERE id = ?
`, [newId]);
return rows[0];
}
async updateNavCategory(id: number | string, data: Partial<{
name: string;
icon: string;
bgClass: string;
cat: string;
targetUrl: string;
badgeText: string;
sortOrder: number;
status: number;
}>): Promise<any> {
const fields: string[] = [];
const values: any[] = [];
if (data.name !== undefined) {
fields.push('name = ?');
values.push(data.name);
}
if (data.icon !== undefined) {
fields.push('icon = ?');
values.push(data.icon);
}
if (data.bgClass !== undefined) {
fields.push('bg_class = ?');
values.push(data.bgClass);
}
if (data.cat !== undefined) {
fields.push('cat = ?');
values.push(data.cat);
}
if (data.targetUrl !== undefined) {
fields.push('target_url = ?');
values.push(data.targetUrl);
}
if (data.badgeText !== undefined) {
fields.push('badge_text = ?');
values.push(data.badgeText);
}
if (data.sortOrder !== undefined) {
fields.push('sort_order = ?');
values.push(Number(data.sortOrder));
}
if (data.status !== undefined) {
fields.push('status = ?');
values.push(Number(data.status));
}
if (fields.length > 0) {
values.push(id);
await pool.query(`UPDATE nav_categories SET ${fields.join(', ')} WHERE id = ?`, values);
}
const [rows]: any = await pool.query(`
SELECT
id, code, name, icon, bg_class as bgClass, cat,
target_url as targetUrl, badge_text as badgeText,
sort_order as sortOrder, status,
created_at as createdAt, updated_at as updatedAt
FROM nav_categories
WHERE id = ?
`, [id]);
return rows[0] || null;
}
async updateNavCategoryStatus(id: number | string, status: number): Promise<any> {
await pool.query('UPDATE nav_categories SET status = ? WHERE id = ?', [Number(status), id]);
const [rows]: any = await pool.query(`
SELECT
id, code, name, icon, bg_class as bgClass, cat,
target_url as targetUrl, badge_text as badgeText,
sort_order as sortOrder, status,
created_at as createdAt, updated_at as updatedAt
FROM nav_categories
WHERE id = ?
`, [id]);
return rows[0] || null;
}
async deleteNavCategory(id: number | string): Promise<boolean> {
const [res]: any = await pool.query('DELETE FROM nav_categories WHERE id = ?', [id]);
return res.affectedRows > 0;
}
async getArbitrations(): Promise<any[]> {