feat: 用户级偏好配置与外观设置,枚举统一抽取与账单类型后端下发
- 新增用户偏好表 t_user_preference 与进程内缓存,账单来源统计默认值由全局改为按用户存储(/api/setting) - 新增外观设置:不计入统计记录支持删除线与灰色标签两种展示样式 - 抽取 ChannelType / TransactionType / StatisticsScope 枚举,替换各处硬编码的渠道、类型与统计口径 - BillType 内聚各来源扩展名,新增 /transaction/import/types 接口,前端改为按后端下发渲染 - 仪表盘趋势图支持点击柱子跳转收支记录并带入筛选条件 - 布局内容区按路由记忆滚动位置;已登录访问登录/注册页自动跳转
This commit is contained in:
@@ -21,6 +21,8 @@ import type {
|
||||
EmailBillSenderRulePayload,
|
||||
} from '../api/emailBill'
|
||||
import type { ParsedBillRow, BillType, BillImportItem } from '../api/transaction'
|
||||
import { getBillTypes } from '../api/transaction'
|
||||
import type { BillTypeOption } from '../api/transaction'
|
||||
import { getCategoryList } from '../api/category'
|
||||
import { getTagList } from '../api/tag'
|
||||
import { renderCategoryLabel } from '../utils/render'
|
||||
@@ -44,13 +46,25 @@ const EMAIL_BILL_TAB_KEY = 'emailBill.tab'
|
||||
const activeTab = ref<string>((route.params.tab as string) || 'configs')
|
||||
|
||||
// ===== 公共选项 =====
|
||||
const billTypeOptions = computed(() => [
|
||||
{ label: t('import.billTypeALIPAY'), value: 'ALIPAY' },
|
||||
{ label: t('import.billTypeWECHAT'), value: 'WECHAT' },
|
||||
{ label: t('import.billTypeMEITUAN'), value: 'MEITUAN' },
|
||||
{ label: t('import.billTypeCMB'), value: 'CMB' },
|
||||
{ label: t('import.billTypeBOCOM'), value: 'BOCOM' },
|
||||
])
|
||||
// 后端下发的账单类型(编码 + 允许的扩展名)
|
||||
const billTypes = ref<BillTypeOption[]>([])
|
||||
|
||||
// 账单类型对应的名称文案 key(文案由前端按语言维护,新增来源未补文案时回退编码)
|
||||
const billTypeLabelKeys: Record<string, string> = {
|
||||
ALIPAY: 'import.billTypeALIPAY',
|
||||
WECHAT: 'import.billTypeWECHAT',
|
||||
MEITUAN: 'import.billTypeMEITUAN',
|
||||
CMB: 'import.billTypeCMB',
|
||||
BOCOM: 'import.billTypeBOCOM',
|
||||
}
|
||||
|
||||
// 账单类型选项,来源于后端
|
||||
const billTypeOptions = computed(() =>
|
||||
billTypes.value.map((item) => ({
|
||||
label: billTypeLabelKeys[item.code] ? t(billTypeLabelKeys[item.code]) : item.code,
|
||||
value: item.code,
|
||||
})),
|
||||
)
|
||||
|
||||
/** 账单类型编码转当前语言标签,未知值回退原编码 */
|
||||
function billTypeLabel(value: string | null) {
|
||||
@@ -58,6 +72,21 @@ function billTypeLabel(value: string | null) {
|
||||
return billTypeOptions.value.find((option) => option.value === value)?.label || value
|
||||
}
|
||||
|
||||
/** 默认账单类型:取后端下发的第一个 */
|
||||
function defaultBillType(): BillType {
|
||||
return billTypeOptions.value[0]?.value || ''
|
||||
}
|
||||
|
||||
/** 加载后端支持的账单类型 */
|
||||
async function loadBillTypes() {
|
||||
try {
|
||||
const res: any = await getBillTypes()
|
||||
billTypes.value = res.data || []
|
||||
} catch (e) {
|
||||
// 静默处理,账单类型下拉保持为空
|
||||
}
|
||||
}
|
||||
|
||||
/** 常用 IMAP 服务器候选项,选择后自动填充默认端口与 SSL */
|
||||
const imapHostOptions = computed(() => [
|
||||
{ label: `${t('emailBill.imapHosts.qq')} · imap.qq.com`, value: 'imap.qq.com' },
|
||||
@@ -326,7 +355,7 @@ const ruleSaving = ref(false) // 发件人规则保存状态
|
||||
|
||||
function emptyRuleForm() {
|
||||
return {
|
||||
name: '', enabled: true, sortOrder: 0, patternsText: '', billType: 'ALIPAY' as BillType,
|
||||
name: '', enabled: true, sortOrder: 0, patternsText: '', billType: defaultBillType(),
|
||||
archivePassword: '', expenseCategoryId: null, incomeCategoryId: null, tagIds: [] as number[],
|
||||
}
|
||||
}
|
||||
@@ -560,7 +589,7 @@ function handleDeleteMessage(row: EmailBillMessage) {
|
||||
const parseModalVisible = ref(false)
|
||||
const parseStage = ref<'select' | 'preview' | 'result'>('select')
|
||||
const parseMessageRow = ref<EmailBillMessage | null>(null)
|
||||
const parseBillType = ref<BillType>('ALIPAY')
|
||||
const parseBillType = ref<BillType>(defaultBillType())
|
||||
const parsePassword = ref('')
|
||||
const parsing = ref(false)
|
||||
const importing = ref(false)
|
||||
@@ -572,7 +601,7 @@ const parseResult = ref<{ total: number; imported: number; skipped: number; fail
|
||||
function openParseModal(row: EmailBillMessage) {
|
||||
parseMessageRow.value = row
|
||||
parseStage.value = 'select'
|
||||
parseBillType.value = (row.billType || 'ALIPAY') as BillType
|
||||
parseBillType.value = (row.billType || defaultBillType()) as BillType
|
||||
parsePassword.value = ''
|
||||
parsedRows.value = []
|
||||
parseResult.value = null
|
||||
@@ -645,7 +674,7 @@ watch(activeTab, (tab) => {
|
||||
let timer: number | null = null
|
||||
|
||||
onMounted(async () => {
|
||||
await loadOptions()
|
||||
await Promise.all([loadOptions(), loadBillTypes()])
|
||||
await Promise.all([loadConfigs(), loadSenderRules(), loadMessages()])
|
||||
timer = window.setInterval(() => {
|
||||
if (activeTab.value === 'messages') {
|
||||
|
||||
Reference in New Issue
Block a user