feat(nav-category): support dynamic home grid categories in admin and uniapp
这个提交包含在:
@@ -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[]> {
|
||||
|
||||
在新工单中引用
屏蔽一个用户