feat(bill): convert bill records modal into dedicated independent page
这个提交包含在:
@@ -142,6 +142,16 @@
|
||||
"backgroundColor": "#0A0F1D"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/bill-records",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "账单明细",
|
||||
"navigationBarBackgroundColor": "#0A0F1D",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#0A0F1D"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/settings",
|
||||
"style": {
|
||||
|
||||
1299
pwuniapp/pages/user/bill-records.vue
普通文件
1299
pwuniapp/pages/user/bill-records.vue
普通文件
文件差异内容过多而无法显示
加载差异
@@ -972,32 +972,11 @@ const ensureLoggedIn = () => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const viewWalletLog = async () => {
|
||||
const viewWalletLog = () => {
|
||||
if (!ensureLoggedIn()) return;
|
||||
try {
|
||||
const list = await getUserFinanceRecords(user.value.id || 1001);
|
||||
if (!list || list.length === 0) {
|
||||
uni.showModal({
|
||||
title: '账单明细',
|
||||
content: '暂无近期财务收支流水记录',
|
||||
showCancel: false,
|
||||
confirmText: '我知道了'
|
||||
});
|
||||
return;
|
||||
}
|
||||
const lines = list.slice(0, 5).map(item => {
|
||||
const sign = item.direction === 'IN' ? '+' : '-';
|
||||
return `${item.type}: ${sign}¥${Number(item.amount).toFixed(2)} (${item.counterparty || '系统'})`;
|
||||
});
|
||||
uni.showModal({
|
||||
title: '最近收支明细',
|
||||
content: lines.join('\r\n'),
|
||||
showCancel: false,
|
||||
confirmText: '确定'
|
||||
});
|
||||
} catch (err) {
|
||||
uni.showToast({ title: '账单获取异常', icon: 'none' });
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/pages/user/bill-records'
|
||||
});
|
||||
};
|
||||
|
||||
const handleRecharge = async () => {
|
||||
|
||||
@@ -2183,12 +2183,12 @@ export class MysqlRepository {
|
||||
const [uRows]: any = await pool.query('SELECT nickname FROM users WHERE id = ?', [userId]);
|
||||
const nickname = uRows[0]?.nickname || '';
|
||||
const [rows]: any = await pool.query(`
|
||||
SELECT id, record_no as recordNo, type, amount, direction, counterparty, related_id as relatedId, created_at as createdAt
|
||||
SELECT id, record_no as recordNo, user_id as userId, type, amount, direction, counterparty, related_id as relatedId, created_at as createdAt
|
||||
FROM finance_records
|
||||
WHERE counterparty LIKE ? OR counterparty LIKE ? OR related_id = ?
|
||||
WHERE user_id = ? OR counterparty LIKE ? OR counterparty LIKE ? OR related_id = ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 50
|
||||
`, [`%${nickname}%`, `%${userId}%`, String(userId)]);
|
||||
LIMIT 100
|
||||
`, [userId, `%${nickname}%`, `%${userId}%`, String(userId)]);
|
||||
return rows;
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安
|
||||
const raw = fs.readFileSync(pagesJsonPath, 'utf-8');
|
||||
const config = JSON.parse(raw);
|
||||
expect(Array.isArray(config.pages)).toBe(true);
|
||||
expect(config.pages.length).toBe(20);
|
||||
expect(config.pages.length).toBe(21);
|
||||
|
||||
for (const pageItem of config.pages) {
|
||||
const vueFilePath = path.join(rootDir, 'pwuniapp', `${pageItem.path}.vue`);
|
||||
@@ -758,7 +758,58 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('17. 独立专属收支账单明细页面验证 (pages/user/bill-records.vue)', () => {
|
||||
it('pages.json 必须成功注册 pages/user/bill-records 且物理文件必须存在', () => {
|
||||
const billPath = path.join(rootDir, 'pwuniapp', 'pages', 'user', 'bill-records.vue');
|
||||
expect(fs.existsSync(billPath)).toBe(true);
|
||||
const pagesJsonPath = path.join(rootDir, 'pwuniapp', 'pages.json');
|
||||
const pagesJson = fs.readFileSync(pagesJsonPath, 'utf-8');
|
||||
expect(pagesJson).toContain('pages/user/bill-records');
|
||||
expect(pagesJson).toContain('账单明细');
|
||||
});
|
||||
|
||||
it('个人中心页面 (pages/user/index.vue) 账单明细入口必须原生跳转至 /pages/user/bill-records 且杜绝弹窗', () => {
|
||||
const userVuePath = path.join(rootDir, 'pwuniapp', 'pages', 'user', 'index.vue');
|
||||
const content = fs.readFileSync(userVuePath, 'utf-8');
|
||||
expect(content).toContain('/pages/user/bill-records');
|
||||
expect(content).not.toContain("title: '最近收支明细'");
|
||||
});
|
||||
|
||||
it('独立账单明细页面必须支持分类选项卡筛选、总览统计、一键复制流水号与双主题适配', () => {
|
||||
const billPath = path.join(rootDir, 'pwuniapp', 'pages', 'user', 'bill-records.vue');
|
||||
const content = fs.readFileSync(billPath, 'utf-8');
|
||||
expect(content).toContain('tabList');
|
||||
expect(content).toContain('filteredRecords');
|
||||
expect(content).toContain('getUserFinanceRecords');
|
||||
expect(content).toContain('handleCopyOrderNo');
|
||||
expect(content).toContain('theme-warm');
|
||||
expect(content).toContain('theme-cool');
|
||||
expect(content).toContain('userBalance');
|
||||
});
|
||||
|
||||
it('仓储层 getUserFinanceRecords 必须包含 user_id 字段选择与查询', () => {
|
||||
const repoPath = path.join(rootDir, 'server', 'src', 'db', 'repository.ts');
|
||||
const content = fs.readFileSync(repoPath, 'utf-8');
|
||||
expect(content).toContain('user_id as userId');
|
||||
expect(content).toContain('user_id = ?');
|
||||
});
|
||||
|
||||
it('账单明细页面代码严禁包含任何系统 Emoji 与 showModal 4 字门禁违规', () => {
|
||||
const billPath = path.join(rootDir, 'pwuniapp', 'pages', 'user', 'bill-records.vue');
|
||||
const content = fs.readFileSync(billPath, 'utf-8');
|
||||
const emojiRegex = /(?:\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u2600-\u27BF])/u;
|
||||
expect(emojiRegex.test(content)).toBe(false);
|
||||
|
||||
const modalMatches = content.matchAll(/(?:confirmText|cancelText):\s*['"]([^'"]+)['"]/g);
|
||||
for (const match of modalMatches) {
|
||||
const btnText = match[1];
|
||||
expect(btnText.length).toBeLessThanOrEqual(4);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户