fix
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 16s
Docker Build & Deploy / Deploy to Production (push) Successful in 6s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 16s
Docker Build & Deploy / Deploy to Production (push) Successful in 6s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
This commit is contained in:
@@ -1,30 +1,45 @@
|
||||
<template>
|
||||
<PopupContainer
|
||||
<PopupContainerV2
|
||||
v-model:show="show"
|
||||
title="新增交易分类"
|
||||
show-cancel-button
|
||||
show-confirm-button
|
||||
confirm-text="确认"
|
||||
cancel-text="取消"
|
||||
@confirm="handleConfirm"
|
||||
@cancel="resetAddForm"
|
||||
:height="'auto'"
|
||||
>
|
||||
<van-form ref="addFormRef">
|
||||
<van-field
|
||||
v-model="classifyName"
|
||||
name="name"
|
||||
label="分类名称"
|
||||
placeholder="请输入分类名称"
|
||||
:rules="[{ required: true, message: '请输入分类名称' }]"
|
||||
/>
|
||||
</van-form>
|
||||
</PopupContainer>
|
||||
<div style="padding: 16px">
|
||||
<van-form ref="addFormRef">
|
||||
<van-field
|
||||
v-model="classifyName"
|
||||
name="name"
|
||||
label="分类名称"
|
||||
placeholder="请输入分类名称"
|
||||
:rules="[{ required: true, message: '请输入分类名称' }]"
|
||||
/>
|
||||
</van-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div style="display: flex; gap: 12px">
|
||||
<van-button
|
||||
plain
|
||||
style="flex: 1"
|
||||
@click="resetAddForm"
|
||||
>
|
||||
取消
|
||||
</van-button>
|
||||
<van-button
|
||||
type="primary"
|
||||
style="flex: 1"
|
||||
@click="handleConfirm"
|
||||
>
|
||||
确认
|
||||
</van-button>
|
||||
</div>
|
||||
</template>
|
||||
</PopupContainerV2>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { showToast } from 'vant'
|
||||
import PopupContainer from './PopupContainer.vue'
|
||||
import PopupContainerV2 from './PopupContainerV2.vue'
|
||||
|
||||
const emit = defineEmits(['confirm'])
|
||||
|
||||
|
||||
@@ -209,10 +209,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 关联账单列表弹窗 -->
|
||||
<PopupContainer
|
||||
v-model="showBillListModal"
|
||||
<PopupContainerV2
|
||||
v-model:show="showBillListModal"
|
||||
title="关联账单列表"
|
||||
height="75%"
|
||||
:height="'75%'"
|
||||
>
|
||||
<BillListComponent
|
||||
data-source="custom"
|
||||
@@ -225,7 +225,7 @@
|
||||
@click="handleBillClick"
|
||||
@delete="handleBillDelete"
|
||||
/>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
</div>
|
||||
|
||||
<!-- 不记额预算卡片 -->
|
||||
@@ -406,10 +406,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 关联账单列表弹窗 -->
|
||||
<PopupContainer
|
||||
v-model="showBillListModal"
|
||||
<PopupContainerV2
|
||||
v-model:show="showBillListModal"
|
||||
title="关联账单列表"
|
||||
height="75%"
|
||||
:height="'75%'"
|
||||
>
|
||||
<BillListComponent
|
||||
data-source="custom"
|
||||
@@ -422,14 +422,14 @@
|
||||
@click="handleBillClick"
|
||||
@delete="handleBillDelete"
|
||||
/>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { BudgetPeriodType } from '@/constants/enums'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
import BillListComponent from '@/components/Bill/BillListComponent.vue'
|
||||
import { getTransactionList } from '@/api/transactionRecord'
|
||||
|
||||
|
||||
@@ -187,13 +187,14 @@
|
||||
</div>
|
||||
|
||||
<!-- 详细描述弹窗 -->
|
||||
<PopupContainer
|
||||
v-model="showDescriptionPopup"
|
||||
<PopupContainerV2
|
||||
v-model:show="showDescriptionPopup"
|
||||
:title="activeDescTab === 'month' ? '预算额度/实际详情(月度)' : '预算额度/实际详情(年度)'"
|
||||
height="70%"
|
||||
:height="'70%'"
|
||||
>
|
||||
<div
|
||||
class="rich-html-content popup-content-padding"
|
||||
class="rich-html-content"
|
||||
style="padding: 16px"
|
||||
v-html="
|
||||
activeDescTab === 'month'
|
||||
? overallStats.month?.description ||
|
||||
@@ -202,14 +203,14 @@
|
||||
'<p style=\'text-align:center;color:var(--van-text-color-3)\'>暂无数据</p>'
|
||||
"
|
||||
/>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { BudgetCategory, BudgetPeriodType } from '@/constants/enums'
|
||||
import { getCssVar } from '@/utils/theme'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
import BaseChart from '@/components/Charts/BaseChart.vue'
|
||||
import { useChartTheme } from '@/composables/useChartTheme'
|
||||
import { chartjsGaugePlugin } from '@/plugins/chartjs-gauge-plugin'
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<template>
|
||||
<PopupContainer
|
||||
v-model="visible"
|
||||
<PopupContainerV2
|
||||
v-model:show="visible"
|
||||
:title="
|
||||
isEdit
|
||||
? `编辑${getCategoryName(form.category)}预算`
|
||||
: `新增${getCategoryName(form.category)}预算`
|
||||
"
|
||||
height="75%"
|
||||
:height="'75%'"
|
||||
>
|
||||
<div class="add-budget-form">
|
||||
<van-form>
|
||||
<van-cell-group inset>
|
||||
<van-field
|
||||
v-model="form.name"
|
||||
v-model:show="form.name"
|
||||
name="name"
|
||||
label="预算名称"
|
||||
placeholder="例如:每月餐饮、年度奖金"
|
||||
@@ -22,7 +22,7 @@
|
||||
<van-field label="不记额预算">
|
||||
<template #input>
|
||||
<van-checkbox
|
||||
v-model="form.noLimit"
|
||||
v-model:show="form.noLimit"
|
||||
@update:model-value="onNoLimitChange"
|
||||
>
|
||||
不记额预算
|
||||
@@ -34,7 +34,7 @@
|
||||
<template #input>
|
||||
<div class="mandatory-wrapper">
|
||||
<van-checkbox
|
||||
v-model="form.isMandatoryExpense"
|
||||
v-model:show="form.isMandatoryExpense"
|
||||
:disabled="form.noLimit"
|
||||
>
|
||||
{{ form.category === BudgetCategory.Expense ? '硬性消费' : '硬性收入' }}
|
||||
@@ -49,7 +49,7 @@
|
||||
>
|
||||
<template #input>
|
||||
<van-radio-group
|
||||
v-model="form.type"
|
||||
v-model:show="form.type"
|
||||
direction="horizontal"
|
||||
:disabled="isEdit || form.noLimit"
|
||||
>
|
||||
@@ -65,7 +65,7 @@
|
||||
<!-- 仅当未选中"不记额预算"时显示预算金额 -->
|
||||
<van-field
|
||||
v-if="!form.noLimit"
|
||||
v-model="form.limit"
|
||||
v-model:show="form.limit"
|
||||
type="number"
|
||||
name="limit"
|
||||
label="预算金额"
|
||||
@@ -95,7 +95,7 @@
|
||||
</template>
|
||||
</van-field>
|
||||
<ClassifySelector
|
||||
v-model="form.selectedCategories"
|
||||
v-model:show="form.selectedCategories"
|
||||
:type="budgetType"
|
||||
multiple
|
||||
:show-add="false"
|
||||
@@ -114,7 +114,7 @@
|
||||
保存预算
|
||||
</van-button>
|
||||
</template>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -122,7 +122,7 @@ import { ref, reactive, computed } from 'vue'
|
||||
import { showToast } from 'vant'
|
||||
import { createBudget, updateBudget } from '@/api/budget'
|
||||
import { BudgetPeriodType, BudgetCategory } from '@/constants/enums'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
import ClassifySelector from '@/components/ClassifySelector.vue'
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<PopupContainer
|
||||
v-model="visible"
|
||||
<PopupContainerV2
|
||||
v-model:show="visible"
|
||||
title="设置存款分类"
|
||||
height="60%"
|
||||
:height="'60%'"
|
||||
>
|
||||
<div class="savings-config-content">
|
||||
<div class="config-header">
|
||||
@@ -16,7 +16,7 @@
|
||||
可多选分类
|
||||
</div>
|
||||
<ClassifySelector
|
||||
v-model="selectedCategories"
|
||||
v-model:show="selectedCategories"
|
||||
:type="2"
|
||||
multiple
|
||||
:show-add="false"
|
||||
@@ -35,14 +35,14 @@
|
||||
保存配置
|
||||
</van-button>
|
||||
</template>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { showToast, showLoadingToast, closeToast } from 'vant'
|
||||
import { getConfig, setConfig } from '@/api/config'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
import ClassifySelector from '@/components/ClassifySelector.vue'
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
|
||||
@@ -1,111 +1,120 @@
|
||||
<template>
|
||||
<PopupContainer
|
||||
<PopupContainerV2
|
||||
v-model:show="visible"
|
||||
:title="title"
|
||||
:subtitle="total > 0 ? `共 ${total} 笔交易` : ''"
|
||||
:closeable="true"
|
||||
:height="'80%'"
|
||||
>
|
||||
<!-- 交易列表 -->
|
||||
<div class="transactions">
|
||||
<!-- 加载状态 -->
|
||||
<van-loading
|
||||
v-if="loading && transactions.length === 0"
|
||||
class="txn-loading"
|
||||
size="24px"
|
||||
vertical
|
||||
>
|
||||
加载中...
|
||||
</van-loading>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div style="padding: 0">
|
||||
<!-- Subtitle 作为内容区域顶部 -->
|
||||
<div
|
||||
v-else-if="transactions.length === 0"
|
||||
class="txn-empty"
|
||||
v-if="total > 0"
|
||||
style="padding: 12px 16px; text-align: center; color: #999; font-size: 14px; border-bottom: 1px solid var(--van-border-color)"
|
||||
>
|
||||
<div class="empty-icon">
|
||||
<van-icon
|
||||
name="balance-list-o"
|
||||
size="48"
|
||||
/>
|
||||
</div>
|
||||
<div class="empty-text">
|
||||
暂无交易记录
|
||||
</div>
|
||||
共 {{ total }} 笔交易
|
||||
</div>
|
||||
|
||||
<!-- 交易列表 -->
|
||||
<div
|
||||
v-else
|
||||
class="txn-list"
|
||||
>
|
||||
<div
|
||||
v-for="txn in transactions"
|
||||
:key="txn.id"
|
||||
class="txn-card"
|
||||
@click="onTransactionClick(txn)"
|
||||
<div class="transactions">
|
||||
<!-- 加载状态 -->
|
||||
<van-loading
|
||||
v-if="loading && transactions.length === 0"
|
||||
class="txn-loading"
|
||||
size="24px"
|
||||
vertical
|
||||
>
|
||||
<div
|
||||
class="txn-icon"
|
||||
:style="{ backgroundColor: txn.iconBg }"
|
||||
>
|
||||
加载中...
|
||||
</van-loading>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div
|
||||
v-else-if="transactions.length === 0"
|
||||
class="txn-empty"
|
||||
>
|
||||
<div class="empty-icon">
|
||||
<van-icon
|
||||
:name="txn.icon"
|
||||
:color="txn.iconColor"
|
||||
name="balance-list-o"
|
||||
size="48"
|
||||
/>
|
||||
</div>
|
||||
<div class="txn-content">
|
||||
<div class="txn-name">
|
||||
{{ txn.reason }}
|
||||
</div>
|
||||
<div class="txn-footer">
|
||||
<div class="txn-time">
|
||||
{{ formatDateTime(txn.occurredAt) }}
|
||||
</div>
|
||||
<span
|
||||
v-if="txn.classify"
|
||||
class="txn-classify-tag"
|
||||
:class="txn.type === 1 ? 'tag-income' : 'tag-expense'"
|
||||
>
|
||||
{{ txn.classify }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="txn-amount">
|
||||
{{ formatAmount(txn.amount, txn.type) }}
|
||||
<div class="empty-text">
|
||||
暂无交易记录
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<div
|
||||
v-if="!finished"
|
||||
class="load-more"
|
||||
>
|
||||
<van-loading
|
||||
v-if="loading"
|
||||
size="20px"
|
||||
>
|
||||
加载中...
|
||||
</van-loading>
|
||||
<van-button
|
||||
v-else
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="loadMore"
|
||||
>
|
||||
加载更多
|
||||
</van-button>
|
||||
</div>
|
||||
|
||||
<!-- 已加载全部 -->
|
||||
<!-- 交易列表 -->
|
||||
<div
|
||||
v-else
|
||||
class="finished-text"
|
||||
class="txn-list"
|
||||
>
|
||||
已加载全部
|
||||
<div
|
||||
v-for="txn in transactions"
|
||||
:key="txn.id"
|
||||
class="txn-card"
|
||||
@click="onTransactionClick(txn)"
|
||||
>
|
||||
<div
|
||||
class="txn-icon"
|
||||
:style="{ backgroundColor: txn.iconBg }"
|
||||
>
|
||||
<van-icon
|
||||
:name="txn.icon"
|
||||
:color="txn.iconColor"
|
||||
/>
|
||||
</div>
|
||||
<div class="txn-content">
|
||||
<div class="txn-name">
|
||||
{{ txn.reason }}
|
||||
</div>
|
||||
<div class="txn-footer">
|
||||
<div class="txn-time">
|
||||
{{ formatDateTime(txn.occurredAt) }}
|
||||
</div>
|
||||
<span
|
||||
v-if="txn.classify"
|
||||
class="txn-classify-tag"
|
||||
:class="txn.type === 1 ? 'tag-income' : 'tag-expense'"
|
||||
>
|
||||
{{ txn.classify }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="txn-amount">
|
||||
{{ formatAmount(txn.amount, txn.type) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<div
|
||||
v-if="!finished"
|
||||
class="load-more"
|
||||
>
|
||||
<van-loading
|
||||
v-if="loading"
|
||||
size="20px"
|
||||
>
|
||||
加载中...
|
||||
</van-loading>
|
||||
<van-button
|
||||
v-else
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="loadMore"
|
||||
>
|
||||
加载更多
|
||||
</van-button>
|
||||
</div>
|
||||
|
||||
<!-- 已加载全部 -->
|
||||
<div
|
||||
v-else
|
||||
class="finished-text"
|
||||
>
|
||||
已加载全部
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
|
||||
<!-- 交易详情弹窗 -->
|
||||
<TransactionDetailSheet
|
||||
@@ -120,7 +129,7 @@
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { showToast } from 'vant'
|
||||
import TransactionDetailSheet from '@/components/Transaction/TransactionDetailSheet.vue'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
import { getTransactionList } from '@/api/transactionRecord'
|
||||
|
||||
const props = defineProps({
|
||||
|
||||
@@ -9,41 +9,43 @@
|
||||
</div>
|
||||
|
||||
<!-- Add Bill Modal -->
|
||||
<PopupContainer
|
||||
v-model="showAddBill"
|
||||
<PopupContainerV2
|
||||
v-model:show="showAddBill"
|
||||
title="记一笔"
|
||||
height="75%"
|
||||
:height="'75%'"
|
||||
>
|
||||
<van-tabs
|
||||
v-model:active="activeTab"
|
||||
shrink
|
||||
>
|
||||
<van-tab
|
||||
title="一句话录账"
|
||||
name="one"
|
||||
<div style="padding: 0">
|
||||
<van-tabs
|
||||
v-model:active="activeTab"
|
||||
shrink
|
||||
>
|
||||
<OneLineBillAdd
|
||||
:key="componentKey"
|
||||
@success="handleSuccess"
|
||||
/>
|
||||
</van-tab>
|
||||
<van-tab
|
||||
title="手动录账"
|
||||
name="manual"
|
||||
>
|
||||
<ManualBillAdd
|
||||
:key="componentKey"
|
||||
@success="handleSuccess"
|
||||
/>
|
||||
</van-tab>
|
||||
</van-tabs>
|
||||
</PopupContainer>
|
||||
<van-tab
|
||||
title="一句话录账"
|
||||
name="one"
|
||||
>
|
||||
<OneLineBillAdd
|
||||
:key="componentKey"
|
||||
@success="handleSuccess"
|
||||
/>
|
||||
</van-tab>
|
||||
<van-tab
|
||||
title="手动录账"
|
||||
name="manual"
|
||||
>
|
||||
<ManualBillAdd
|
||||
:key="componentKey"
|
||||
@success="handleSuccess"
|
||||
/>
|
||||
</van-tab>
|
||||
</van-tabs>
|
||||
</div>
|
||||
</PopupContainerV2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineEmits } from 'vue'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
import OneLineBillAdd from '@/components/Bill/OneLineBillAdd.vue'
|
||||
import ManualBillAdd from '@/components/Bill/ManualBillAdd.vue'
|
||||
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
<template>
|
||||
<PopupContainer
|
||||
<PopupContainerV2
|
||||
:show="show"
|
||||
:title="title"
|
||||
show-cancel-button
|
||||
show-confirm-button
|
||||
confirm-text="选择"
|
||||
cancel-text="取消"
|
||||
:height="'80%'"
|
||||
@update:show="emit('update:show', $event)"
|
||||
@confirm="handleConfirm"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<div class="icon-selector">
|
||||
<!-- 搜索框 -->
|
||||
@@ -56,14 +51,32 @@
|
||||
@change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</PopupContainer>
|
||||
<template #footer>
|
||||
<div style="display: flex; gap: 12px">
|
||||
<van-button
|
||||
plain
|
||||
style="flex: 1"
|
||||
@click="handleCancel"
|
||||
>
|
||||
取消
|
||||
</van-button>
|
||||
<van-button
|
||||
type="primary"
|
||||
style="flex: 1"
|
||||
@click="handleConfirm"
|
||||
>
|
||||
选择
|
||||
</van-button>
|
||||
</div>
|
||||
</template>
|
||||
</PopupContainerV2>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { showToast } from 'vant'
|
||||
import Icon from './Icon.vue'
|
||||
import PopupContainer from './PopupContainer.vue'
|
||||
import PopupContainerV2 from './PopupContainerV2.vue'
|
||||
|
||||
const props = defineProps({
|
||||
show: {
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
<!--
|
||||
统一弹窗组件
|
||||
|
||||
## 基础用法
|
||||
<PopupContainer v-model:show="show" title="标题">
|
||||
内容
|
||||
</PopupContainer>
|
||||
|
||||
## 确认对话框用法
|
||||
<PopupContainer
|
||||
v-model:show="showConfirm"
|
||||
title="确认操作"
|
||||
show-confirm-button
|
||||
show-cancel-button
|
||||
confirm-text="确定"
|
||||
cancel-text="取消"
|
||||
@confirm="handleConfirm"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
确定要执行此操作吗?
|
||||
</PopupContainer>
|
||||
|
||||
## 带副标题和页脚
|
||||
<PopupContainer
|
||||
v-model:show="show"
|
||||
title="分类详情"
|
||||
subtitle="共 10 笔交易"
|
||||
>
|
||||
内容区域
|
||||
<template #footer>
|
||||
<van-button type="primary">提交</van-button>
|
||||
</template>
|
||||
</PopupContainer>
|
||||
-->
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<template>
|
||||
<van-popup
|
||||
v-model:show="visible"
|
||||
position="bottom"
|
||||
:style="{ height: height }"
|
||||
round
|
||||
:closeable="closeable"
|
||||
teleport="body"
|
||||
>
|
||||
<div class="popup-container">
|
||||
<!-- 头部区域 -->
|
||||
<div class="popup-header-fixed">
|
||||
<!-- 标题行 (无子标题且有操作时使用 Grid 布局) -->
|
||||
<div
|
||||
class="header-title-row"
|
||||
:class="{ 'has-actions': !subtitle && hasActions }"
|
||||
>
|
||||
<h3 class="popup-title">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<!-- 无子标题时,操作按钮与标题同行 -->
|
||||
<div
|
||||
v-if="!subtitle && hasActions"
|
||||
class="header-actions-inline"
|
||||
>
|
||||
<slot name="header-actions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 子标题/统计信息 -->
|
||||
<div
|
||||
v-if="subtitle"
|
||||
class="header-stats"
|
||||
>
|
||||
<span
|
||||
class="stats-text"
|
||||
v-html="subtitle"
|
||||
/>
|
||||
<!-- 额外操作插槽 -->
|
||||
<slot
|
||||
v-if="hasActions"
|
||||
name="header-actions"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内容区域(可滚动) -->
|
||||
<div class="popup-scroll-content">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<!-- 底部页脚,固定不可滚动 -->
|
||||
<div
|
||||
v-if="slots.footer || showConfirmButton || showCancelButton"
|
||||
class="popup-footer-fixed"
|
||||
>
|
||||
<!-- 用户自定义页脚插槽 -->
|
||||
<slot name="footer">
|
||||
<!-- 默认确认/取消按钮 -->
|
||||
<div class="footer-buttons">
|
||||
<van-button
|
||||
v-if="showCancelButton"
|
||||
plain
|
||||
@click="handleCancel"
|
||||
>
|
||||
{{ cancelText }}
|
||||
</van-button>
|
||||
<van-button
|
||||
v-if="showConfirmButton"
|
||||
type="primary"
|
||||
@click="handleConfirm"
|
||||
>
|
||||
{{ confirmText }}
|
||||
</van-button>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, useSlots } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
subtitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '80%'
|
||||
},
|
||||
closeable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showConfirmButton: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showCancelButton: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
confirmText: {
|
||||
type: String,
|
||||
default: '确认'
|
||||
},
|
||||
cancelText: {
|
||||
type: String,
|
||||
default: '取消'
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'confirm', 'cancel'])
|
||||
|
||||
const slots = useSlots()
|
||||
|
||||
// 双向绑定
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => emit('update:modelValue', value)
|
||||
})
|
||||
|
||||
// 判断是否有操作按钮
|
||||
const hasActions = computed(() => !!slots['header-actions'])
|
||||
|
||||
// 确认按钮点击
|
||||
const handleConfirm = () => {
|
||||
emit('confirm')
|
||||
}
|
||||
|
||||
// 取消按钮点击
|
||||
const handleCancel = () => {
|
||||
emit('cancel')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.popup-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.popup-header-fixed {
|
||||
flex-shrink: 0;
|
||||
padding: 16px;
|
||||
background: linear-gradient(180deg, var(--van-background) 0%, var(--van-background-2) 100%);
|
||||
border-bottom: 1px solid var(--van-border-color);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.header-title-row.has-actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-title-row.has-actions .popup-title {
|
||||
grid-column: 2;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.header-actions-inline {
|
||||
grid-column: 3;
|
||||
justify-self: end;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
color: var(--van-text-color);
|
||||
letter-spacing: -0.02em;
|
||||
/*超出长度*/
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.header-stats {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.stats-text {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
color: var(--van-text-color-2);
|
||||
grid-column: 2;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 按钮区域放在右侧 */
|
||||
.header-stats :deep(> :last-child:not(.stats-text)) {
|
||||
grid-column: 3;
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
.popup-scroll-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.popup-footer-fixed {
|
||||
flex-shrink: 0;
|
||||
border-top: 1px solid var(--van-border-color);
|
||||
background-color: var(--van-background-2);
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.footer-buttons {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.footer-buttons .van-button {
|
||||
flex: 1;
|
||||
max-width: 120px;
|
||||
}
|
||||
</style>
|
||||
@@ -14,7 +14,7 @@ PopupContainer V2 - 通用底部弹窗组件(采用 TransactionDetailSheet 样
|
||||
</PopupContainerV2>
|
||||
|
||||
## Props
|
||||
- modelValue (Boolean, required): 控制弹窗显示/隐藏
|
||||
- show (Boolean, required): 控制弹窗显示/隐藏
|
||||
- title (String, required): 标题文本
|
||||
- height (String, default: 'auto'): 弹窗高度,支持 'auto', '80%', '500px' 等
|
||||
- maxHeight (String, default: '85%'): 最大高度
|
||||
@@ -24,7 +24,7 @@ PopupContainer V2 - 通用底部弹窗组件(采用 TransactionDetailSheet 样
|
||||
- footer: 固定底部区域(操作按钮等)
|
||||
|
||||
## Events
|
||||
- update:modelValue: 弹窗显示/隐藏状态变更
|
||||
- update:show: 弹窗显示/隐藏状态变更
|
||||
-->
|
||||
<template>
|
||||
<van-popup
|
||||
@@ -71,7 +71,7 @@ PopupContainer V2 - 通用底部弹窗组件(采用 TransactionDetailSheet 样
|
||||
import { computed, useSlots } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
@@ -89,14 +89,14 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits(['update:show'])
|
||||
|
||||
const slots = useSlots()
|
||||
|
||||
// 双向绑定
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => emit('update:modelValue', value)
|
||||
get: () => props.show,
|
||||
set: (value) => emit('update:show', value)
|
||||
})
|
||||
|
||||
// 判断是否有 footer 插槽
|
||||
|
||||
@@ -61,34 +61,42 @@
|
||||
</van-cell-group>
|
||||
|
||||
<!-- 账单列表弹窗 -->
|
||||
<PopupContainer
|
||||
v-model="showTransactionList"
|
||||
<PopupContainerV2
|
||||
v-model:show="showTransactionList"
|
||||
:title="selectedGroup?.reason || '交易记录'"
|
||||
:subtitle="groupTransactionsTotal ? `共 ${groupTransactionsTotal} 笔交易` : ''"
|
||||
height="75%"
|
||||
:height="'75%'"
|
||||
>
|
||||
<template #header-actions>
|
||||
<van-button
|
||||
type="primary"
|
||||
size="small"
|
||||
class="batch-classify-btn"
|
||||
@click.stop="handleBatchClassify(selectedGroup)"
|
||||
>
|
||||
批量分类
|
||||
</van-button>
|
||||
</template>
|
||||
<div style="padding: 0">
|
||||
<!-- Subtitle 和操作按钮 -->
|
||||
<div style="padding: 12px 16px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--van-border-color)">
|
||||
<span
|
||||
v-if="groupTransactionsTotal"
|
||||
style="color: #999; font-size: 14px"
|
||||
>
|
||||
共 {{ groupTransactionsTotal }} 笔交易
|
||||
</span>
|
||||
<van-button
|
||||
type="primary"
|
||||
size="small"
|
||||
class="batch-classify-btn"
|
||||
@click.stop="handleBatchClassify(selectedGroup)"
|
||||
>
|
||||
批量分类
|
||||
</van-button>
|
||||
</div>
|
||||
|
||||
<BillListComponent
|
||||
data-source="custom"
|
||||
:transactions="groupTransactions"
|
||||
:loading="transactionLoading"
|
||||
:finished="transactionFinished"
|
||||
:enable-filter="false"
|
||||
@load="loadGroupTransactions"
|
||||
@click="handleTransactionClick"
|
||||
@delete="handleGroupTransactionDelete"
|
||||
/>
|
||||
</PopupContainer>
|
||||
<BillListComponent
|
||||
data-source="custom"
|
||||
:transactions="groupTransactions"
|
||||
:loading="transactionLoading"
|
||||
:finished="transactionFinished"
|
||||
:enable-filter="false"
|
||||
@load="loadGroupTransactions"
|
||||
@click="handleTransactionClick"
|
||||
@delete="handleGroupTransactionDelete"
|
||||
/>
|
||||
</div>
|
||||
</PopupContainerV2>
|
||||
|
||||
<!-- 账单详情弹窗 -->
|
||||
<TransactionDetail
|
||||
@@ -98,76 +106,78 @@
|
||||
/>
|
||||
|
||||
<!-- 批量设置对话框 -->
|
||||
<PopupContainer
|
||||
v-model="showBatchDialog"
|
||||
<PopupContainerV2
|
||||
v-model:show="showBatchDialog"
|
||||
title="批量设置分类"
|
||||
height="60%"
|
||||
:height="'60%'"
|
||||
>
|
||||
<van-form
|
||||
ref="batchFormRef"
|
||||
class="setting-form"
|
||||
>
|
||||
<van-cell-group inset>
|
||||
<!-- 显示选中的摘要 -->
|
||||
<van-field
|
||||
:model-value="batchGroup?.reason"
|
||||
label="交易摘要"
|
||||
readonly
|
||||
input-align="left"
|
||||
/>
|
||||
<div style="padding: 0">
|
||||
<van-form
|
||||
ref="batchFormRef"
|
||||
class="setting-form"
|
||||
>
|
||||
<van-cell-group inset>
|
||||
<!-- 显示选中的摘要 -->
|
||||
<van-field
|
||||
:model-value="batchGroup?.reason"
|
||||
label="交易摘要"
|
||||
readonly
|
||||
input-align="left"
|
||||
/>
|
||||
|
||||
<!-- 显示记录数量 -->
|
||||
<van-field
|
||||
:model-value="`${batchGroup?.count || 0} 条`"
|
||||
label="记录数量"
|
||||
readonly
|
||||
input-align="left"
|
||||
/>
|
||||
<!-- 显示记录数量 -->
|
||||
<van-field
|
||||
:model-value="`${batchGroup?.count || 0} 条`"
|
||||
label="记录数量"
|
||||
readonly
|
||||
input-align="left"
|
||||
/>
|
||||
|
||||
<!-- 交易类型 -->
|
||||
<van-field
|
||||
name="type"
|
||||
label="交易类型"
|
||||
>
|
||||
<template #input>
|
||||
<van-radio-group
|
||||
v-model="batchForm.type"
|
||||
direction="horizontal"
|
||||
>
|
||||
<van-radio :name="0">
|
||||
支出
|
||||
</van-radio>
|
||||
<van-radio :name="1">
|
||||
收入
|
||||
</van-radio>
|
||||
<van-radio :name="2">
|
||||
不计
|
||||
</van-radio>
|
||||
</van-radio-group>
|
||||
</template>
|
||||
</van-field>
|
||||
<!-- 交易类型 -->
|
||||
<van-field
|
||||
name="type"
|
||||
label="交易类型"
|
||||
>
|
||||
<template #input>
|
||||
<van-radio-group
|
||||
v-model="batchForm.type"
|
||||
direction="horizontal"
|
||||
>
|
||||
<van-radio :name="0">
|
||||
支出
|
||||
</van-radio>
|
||||
<van-radio :name="1">
|
||||
收入
|
||||
</van-radio>
|
||||
<van-radio :name="2">
|
||||
不计
|
||||
</van-radio>
|
||||
</van-radio-group>
|
||||
</template>
|
||||
</van-field>
|
||||
|
||||
<!-- 分类选择 -->
|
||||
<van-field
|
||||
name="classify"
|
||||
label="分类"
|
||||
>
|
||||
<template #input>
|
||||
<span
|
||||
v-if="!batchForm.classify"
|
||||
style="opacity: 0.4"
|
||||
>请选择分类</span>
|
||||
<span v-else>{{ batchForm.classify }}</span>
|
||||
</template>
|
||||
</van-field>
|
||||
<!-- 分类选择 -->
|
||||
<van-field
|
||||
name="classify"
|
||||
label="分类"
|
||||
>
|
||||
<template #input>
|
||||
<span
|
||||
v-if="!batchForm.classify"
|
||||
style="opacity: 0.4"
|
||||
>请选择分类</span>
|
||||
<span v-else>{{ batchForm.classify }}</span>
|
||||
</template>
|
||||
</van-field>
|
||||
|
||||
<!-- 分类选择组件 -->
|
||||
<ClassifySelector
|
||||
v-model="batchForm.classify"
|
||||
:type="batchForm.type"
|
||||
/>
|
||||
</van-cell-group>
|
||||
</van-form>
|
||||
<!-- 分类选择组件 -->
|
||||
<ClassifySelector
|
||||
v-model="batchForm.classify"
|
||||
:type="batchForm.type"
|
||||
/>
|
||||
</van-cell-group>
|
||||
</van-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<van-button
|
||||
round
|
||||
@@ -178,7 +188,7 @@
|
||||
确定
|
||||
</van-button>
|
||||
</template>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -189,7 +199,7 @@ import { getReasonGroups, batchUpdateByReason, getTransactionList } from '@/api/
|
||||
import ClassifySelector from './ClassifySelector.vue'
|
||||
import BillListComponent from './Bill/BillListComponent.vue'
|
||||
import TransactionDetail from './TransactionDetail.vue'
|
||||
import PopupContainer from './PopupContainer.vue'
|
||||
import PopupContainerV2 from './PopupContainerV2.vue'
|
||||
|
||||
const props = defineProps({
|
||||
// 是否支持多选
|
||||
|
||||
@@ -1,134 +1,135 @@
|
||||
<template>
|
||||
<PopupContainer
|
||||
v-model="visible"
|
||||
<PopupContainerV2
|
||||
v-model:show="visible"
|
||||
title="交易详情"
|
||||
height="75%"
|
||||
:closeable="false"
|
||||
:height="'75%'"
|
||||
>
|
||||
<van-form style="margin-top: 12px">
|
||||
<van-cell-group inset>
|
||||
<van-cell
|
||||
title="记录时间"
|
||||
:value="formatDate(transaction.createTime)"
|
||||
/>
|
||||
</van-cell-group>
|
||||
<div style="padding: 0">
|
||||
<van-form style="margin-top: 12px">
|
||||
<van-cell-group inset>
|
||||
<van-cell
|
||||
title="记录时间"
|
||||
:value="formatDate(transaction.createTime)"
|
||||
/>
|
||||
</van-cell-group>
|
||||
|
||||
<van-cell-group
|
||||
inset
|
||||
title="交易明细"
|
||||
>
|
||||
<van-field
|
||||
v-model="occurredAtLabel"
|
||||
name="occurredAt"
|
||||
label="交易时间"
|
||||
readonly
|
||||
is-link
|
||||
placeholder="请选择交易时间"
|
||||
:rules="[{ required: true, message: '请选择交易时间' }]"
|
||||
@click="showDatePicker = true"
|
||||
/>
|
||||
<van-field
|
||||
v-model="editForm.reason"
|
||||
name="reason"
|
||||
label="交易摘要"
|
||||
placeholder="请输入交易摘要"
|
||||
type="textarea"
|
||||
rows="2"
|
||||
autosize
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
<van-field
|
||||
v-model="editForm.amount"
|
||||
name="amount"
|
||||
label="交易金额"
|
||||
placeholder="请输入交易金额"
|
||||
type="number"
|
||||
:rules="[{ required: true, message: '请输入交易金额' }]"
|
||||
/>
|
||||
<van-field
|
||||
v-model="editForm.balance"
|
||||
name="balance"
|
||||
label="交易后余额"
|
||||
placeholder="请输入交易后余额"
|
||||
type="number"
|
||||
:rules="[{ required: true, message: '请输入交易后余额' }]"
|
||||
/>
|
||||
|
||||
<van-field
|
||||
name="type"
|
||||
label="交易类型"
|
||||
<van-cell-group
|
||||
inset
|
||||
title="交易明细"
|
||||
>
|
||||
<template #input>
|
||||
<van-radio-group
|
||||
v-model="editForm.type"
|
||||
direction="horizontal"
|
||||
@change="handleTypeChange"
|
||||
>
|
||||
<van-radio :name="0">
|
||||
支出
|
||||
</van-radio>
|
||||
<van-radio :name="1">
|
||||
收入
|
||||
</van-radio>
|
||||
<van-radio :name="2">
|
||||
不计
|
||||
</van-radio>
|
||||
</van-radio-group>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field
|
||||
v-model="occurredAtLabel"
|
||||
name="occurredAt"
|
||||
label="交易时间"
|
||||
readonly
|
||||
is-link
|
||||
placeholder="请选择交易时间"
|
||||
:rules="[{ required: true, message: '请选择交易时间' }]"
|
||||
@click="showDatePicker = true"
|
||||
/>
|
||||
<van-field
|
||||
v-model="editForm.reason"
|
||||
name="reason"
|
||||
label="交易摘要"
|
||||
placeholder="请输入交易摘要"
|
||||
type="textarea"
|
||||
rows="2"
|
||||
autosize
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
<van-field
|
||||
v-model="editForm.amount"
|
||||
name="amount"
|
||||
label="交易金额"
|
||||
placeholder="请输入交易金额"
|
||||
type="number"
|
||||
:rules="[{ required: true, message: '请输入交易金额' }]"
|
||||
/>
|
||||
<van-field
|
||||
v-model="editForm.balance"
|
||||
name="balance"
|
||||
label="交易后余额"
|
||||
placeholder="请输入交易后余额"
|
||||
type="number"
|
||||
:rules="[{ required: true, message: '请输入交易后余额' }]"
|
||||
/>
|
||||
|
||||
<van-field
|
||||
name="classify"
|
||||
label="交易分类"
|
||||
>
|
||||
<template #input>
|
||||
<div style="flex: 1">
|
||||
<div
|
||||
v-if="
|
||||
transaction &&
|
||||
transaction.unconfirmedClassify &&
|
||||
transaction.unconfirmedClassify !== editForm.classify
|
||||
"
|
||||
class="suggestion-tip"
|
||||
@click="applySuggestion"
|
||||
<van-field
|
||||
name="type"
|
||||
label="交易类型"
|
||||
>
|
||||
<template #input>
|
||||
<van-radio-group
|
||||
v-model="editForm.type"
|
||||
direction="horizontal"
|
||||
@change="handleTypeChange"
|
||||
>
|
||||
<van-icon
|
||||
name="bulb-o"
|
||||
class="suggestion-icon"
|
||||
/>
|
||||
<span class="suggestion-text">
|
||||
建议: {{ transaction.unconfirmedClassify }}
|
||||
<span
|
||||
v-if="
|
||||
transaction.unconfirmedType !== null &&
|
||||
transaction.unconfirmedType !== undefined &&
|
||||
transaction.unconfirmedType !== editForm.type
|
||||
"
|
||||
>
|
||||
({{ getTypeName(transaction.unconfirmedType) }})
|
||||
</span>
|
||||
</span>
|
||||
<div class="suggestion-apply">
|
||||
应用
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
v-else-if="!editForm.classify"
|
||||
style="color: var(--van-gray-5)"
|
||||
>请选择交易分类</span>
|
||||
<span v-else>{{ editForm.classify }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-radio :name="0">
|
||||
支出
|
||||
</van-radio>
|
||||
<van-radio :name="1">
|
||||
收入
|
||||
</van-radio>
|
||||
<van-radio :name="2">
|
||||
不计
|
||||
</van-radio>
|
||||
</van-radio-group>
|
||||
</template>
|
||||
</van-field>
|
||||
|
||||
<ClassifySelector
|
||||
v-model="editForm.classify"
|
||||
:type="editForm.type"
|
||||
@change="handleClassifyChange"
|
||||
/>
|
||||
</van-cell-group>
|
||||
</van-form>
|
||||
<van-field
|
||||
name="classify"
|
||||
label="交易分类"
|
||||
>
|
||||
<template #input>
|
||||
<div style="flex: 1">
|
||||
<div
|
||||
v-if="
|
||||
transaction &&
|
||||
transaction.unconfirmedClassify &&
|
||||
transaction.unconfirmedClassify !== editForm.classify
|
||||
"
|
||||
class="suggestion-tip"
|
||||
@click="applySuggestion"
|
||||
>
|
||||
<van-icon
|
||||
name="bulb-o"
|
||||
class="suggestion-icon"
|
||||
/>
|
||||
<span class="suggestion-text">
|
||||
建议: {{ transaction.unconfirmedClassify }}
|
||||
<span
|
||||
v-if="
|
||||
transaction.unconfirmedType !== null &&
|
||||
transaction.unconfirmedType !== undefined &&
|
||||
transaction.unconfirmedType !== editForm.type
|
||||
"
|
||||
>
|
||||
({{ getTypeName(transaction.unconfirmedType) }})
|
||||
</span>
|
||||
</span>
|
||||
<div class="suggestion-apply">
|
||||
应用
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
v-else-if="!editForm.classify"
|
||||
style="color: var(--van-gray-5)"
|
||||
>请选择交易分类</span>
|
||||
<span v-else>{{ editForm.classify }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
|
||||
<ClassifySelector
|
||||
v-model="editForm.classify"
|
||||
:type="editForm.type"
|
||||
@change="handleClassifyChange"
|
||||
/>
|
||||
</van-cell-group>
|
||||
</van-form>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<van-button
|
||||
@@ -141,7 +142,7 @@
|
||||
保存修改
|
||||
</van-button>
|
||||
</template>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
|
||||
<!-- 日期选择弹窗 -->
|
||||
<van-popup
|
||||
@@ -178,7 +179,7 @@
|
||||
import { ref, reactive, watch, defineProps, defineEmits, computed, nextTick } from 'vue'
|
||||
import { showToast } from 'vant'
|
||||
import dayjs from 'dayjs'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
import ClassifySelector from '@/components/ClassifySelector.vue'
|
||||
import { updateTransaction } from '@/api/transactionRecord'
|
||||
|
||||
|
||||
@@ -94,26 +94,41 @@
|
||||
</div>
|
||||
|
||||
<!-- 提示词设置弹窗 -->
|
||||
<PopupContainer
|
||||
<PopupContainerV2
|
||||
v-model:show="showPromptDialog"
|
||||
title="编辑分析提示词"
|
||||
show-cancel-button
|
||||
show-confirm-button
|
||||
confirm-text="保存"
|
||||
cancel-text="取消"
|
||||
@confirm="confirmPrompt"
|
||||
@cancel="showPromptDialog = false"
|
||||
:height="'75%'"
|
||||
>
|
||||
<van-field
|
||||
v-model="promptValue"
|
||||
rows="4"
|
||||
autosize
|
||||
type="textarea"
|
||||
maxlength="2000"
|
||||
placeholder="输入自定义的分析提示词..."
|
||||
show-word-limit
|
||||
/>
|
||||
</PopupContainer>
|
||||
<div style="padding: 16px">
|
||||
<van-field
|
||||
v-model="promptValue"
|
||||
rows="4"
|
||||
autosize
|
||||
type="textarea"
|
||||
maxlength="2000"
|
||||
placeholder="输入自定义的分析提示词..."
|
||||
show-word-limit
|
||||
/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div style="display: flex; gap: 12px">
|
||||
<van-button
|
||||
plain
|
||||
style="flex: 1"
|
||||
@click="showPromptDialog = false"
|
||||
>
|
||||
取消
|
||||
</van-button>
|
||||
<van-button
|
||||
type="primary"
|
||||
style="flex: 1"
|
||||
@click="confirmPrompt"
|
||||
>
|
||||
保存
|
||||
</van-button>
|
||||
</div>
|
||||
</template>
|
||||
</PopupContainerV2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -122,7 +137,7 @@ import { ref, nextTick } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { showToast, showLoadingToast, closeToast } from 'vant'
|
||||
import { getConfig, setConfig } from '@/api/config'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const userInput = ref('')
|
||||
|
||||
@@ -112,64 +112,107 @@
|
||||
</div>
|
||||
|
||||
<!-- 新增分类对话框 -->
|
||||
<PopupContainer
|
||||
<PopupContainerV2
|
||||
v-model:show="showAddDialog"
|
||||
title="新增分类"
|
||||
show-cancel-button
|
||||
show-confirm-button
|
||||
confirm-text="确认"
|
||||
cancel-text="取消"
|
||||
@confirm="handleConfirmAdd"
|
||||
@cancel="resetAddForm"
|
||||
:height="'auto'"
|
||||
>
|
||||
<van-form ref="addFormRef">
|
||||
<van-field
|
||||
v-model="addForm.name"
|
||||
name="name"
|
||||
label="分类名称"
|
||||
placeholder="请输入分类名称"
|
||||
:rules="[{ required: true, message: '请输入分类名称' }]"
|
||||
/>
|
||||
</van-form>
|
||||
</PopupContainer>
|
||||
<div style="padding: 16px">
|
||||
<van-form ref="addFormRef">
|
||||
<van-field
|
||||
v-model="addForm.name"
|
||||
name="name"
|
||||
label="分类名称"
|
||||
placeholder="请输入分类名称"
|
||||
:rules="[{ required: true, message: '请输入分类名称' }]"
|
||||
/>
|
||||
</van-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div style="display: flex; gap: 12px">
|
||||
<van-button
|
||||
plain
|
||||
style="flex: 1"
|
||||
@click="resetAddForm"
|
||||
>
|
||||
取消
|
||||
</van-button>
|
||||
<van-button
|
||||
type="primary"
|
||||
style="flex: 1"
|
||||
@click="handleConfirmAdd"
|
||||
>
|
||||
确认
|
||||
</van-button>
|
||||
</div>
|
||||
</template>
|
||||
</PopupContainerV2>
|
||||
|
||||
<!-- 编辑分类对话框 -->
|
||||
<PopupContainer
|
||||
<PopupContainerV2
|
||||
v-model:show="showEditDialog"
|
||||
title="编辑分类"
|
||||
show-cancel-button
|
||||
show-confirm-button
|
||||
confirm-text="保存"
|
||||
cancel-text="取消"
|
||||
@confirm="handleConfirmEdit"
|
||||
@cancel="showEditDialog = false"
|
||||
:height="'auto'"
|
||||
>
|
||||
<van-form ref="editFormRef">
|
||||
<van-field
|
||||
v-model="editForm.name"
|
||||
name="name"
|
||||
label="分类名称"
|
||||
placeholder="请输入分类名称"
|
||||
:rules="[{ required: true, message: '请输入分类名称' }]"
|
||||
/>
|
||||
</van-form>
|
||||
</PopupContainer>
|
||||
<div style="padding: 16px">
|
||||
<van-form ref="editFormRef">
|
||||
<van-field
|
||||
v-model="editForm.name"
|
||||
name="name"
|
||||
label="分类名称"
|
||||
placeholder="请输入分类名称"
|
||||
:rules="[{ required: true, message: '请输入分类名称' }]"
|
||||
/>
|
||||
</van-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div style="display: flex; gap: 12px">
|
||||
<van-button
|
||||
plain
|
||||
style="flex: 1"
|
||||
@click="showEditDialog = false"
|
||||
>
|
||||
取消
|
||||
</van-button>
|
||||
<van-button
|
||||
type="primary"
|
||||
style="flex: 1"
|
||||
@click="handleConfirmEdit"
|
||||
>
|
||||
保存
|
||||
</van-button>
|
||||
</div>
|
||||
</template>
|
||||
</PopupContainerV2>
|
||||
|
||||
<!-- 删除确认对话框 -->
|
||||
<PopupContainer
|
||||
<PopupContainerV2
|
||||
v-model:show="showDeleteConfirm"
|
||||
title="删除分类"
|
||||
show-cancel-button
|
||||
show-confirm-button
|
||||
confirm-text="确定"
|
||||
cancel-text="取消"
|
||||
@confirm="handleConfirmDelete"
|
||||
@cancel="showDeleteConfirm = false"
|
||||
:height="'auto'"
|
||||
>
|
||||
<p style="text-align: center; padding: 20px; color: var(--van-text-color-2)">
|
||||
删除后无法恢复,确定要删除吗?
|
||||
</p>
|
||||
</PopupContainer>
|
||||
<template #footer>
|
||||
<div style="display: flex; gap: 12px">
|
||||
<van-button
|
||||
plain
|
||||
style="flex: 1"
|
||||
@click="showDeleteConfirm = false"
|
||||
>
|
||||
取消
|
||||
</van-button>
|
||||
<van-button
|
||||
type="primary"
|
||||
style="flex: 1"
|
||||
@click="handleConfirmDelete"
|
||||
>
|
||||
确定
|
||||
</van-button>
|
||||
</div>
|
||||
</template>
|
||||
</PopupContainerV2>
|
||||
|
||||
<!-- 图标选择对话框 -->
|
||||
<IconSelector
|
||||
@@ -189,7 +232,7 @@ import { useRouter } from 'vue-router'
|
||||
import { showSuccessToast, showToast, showLoadingToast, closeToast } from 'vant'
|
||||
import Icon from '@/components/Icon.vue'
|
||||
import IconSelector from '@/components/IconSelector.vue'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
import {
|
||||
getCategoryList,
|
||||
createCategory,
|
||||
|
||||
@@ -71,12 +71,12 @@
|
||||
/>
|
||||
|
||||
<!-- 记录列表弹窗 -->
|
||||
<PopupContainer
|
||||
v-model="showRecordsList"
|
||||
<PopupContainerV2
|
||||
v-model:show="showRecordsList"
|
||||
title="交易记录列表"
|
||||
height="75%"
|
||||
:height="'75%'"
|
||||
>
|
||||
<div style="background: var(--van-background)">
|
||||
<div style="background: var(--van-background); padding: 0">
|
||||
<!-- 批量操作按钮 -->
|
||||
<div class="batch-actions">
|
||||
<van-button
|
||||
@@ -122,7 +122,7 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -133,7 +133,7 @@ import { showToast, showConfirmDialog } from 'vant'
|
||||
import { nlpAnalysis, batchUpdateClassify } from '@/api/transactionRecord'
|
||||
import BillListComponent from '@/components/Bill/BillListComponent.vue'
|
||||
import TransactionDetail from '@/components/TransactionDetail.vue'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const userInput = ref('')
|
||||
|
||||
@@ -73,23 +73,24 @@
|
||||
</van-pull-refresh>
|
||||
|
||||
<!-- 详情弹出层 -->
|
||||
<PopupContainer
|
||||
v-model="detailVisible"
|
||||
<PopupContainerV2
|
||||
v-model:show="detailVisible"
|
||||
:title="currentEmail ? currentEmail.Subject || currentEmail.subject || '(无主题)' : ''"
|
||||
height="75%"
|
||||
:height="'75%'"
|
||||
>
|
||||
<template #header-actions>
|
||||
<van-button
|
||||
size="small"
|
||||
type="primary"
|
||||
:loading="refreshingAnalysis"
|
||||
@click="handleRefreshAnalysis"
|
||||
>
|
||||
重新分析
|
||||
</van-button>
|
||||
</template>
|
||||
|
||||
<div v-if="currentEmail">
|
||||
<!-- 操作按钮栏 -->
|
||||
<div style="padding: 12px 16px; text-align: right; border-bottom: 1px solid var(--van-border-color)">
|
||||
<van-button
|
||||
size="small"
|
||||
type="primary"
|
||||
:loading="refreshingAnalysis"
|
||||
@click="handleRefreshAnalysis"
|
||||
>
|
||||
重新分析
|
||||
</van-button>
|
||||
</div>
|
||||
|
||||
<van-cell-group
|
||||
inset
|
||||
style="margin-top: 12px"
|
||||
@@ -140,13 +141,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
|
||||
<!-- 账单列表弹出层 -->
|
||||
<PopupContainer
|
||||
v-model="transactionListVisible"
|
||||
<PopupContainerV2
|
||||
v-model:show="transactionListVisible"
|
||||
title="关联账单列表"
|
||||
height="75%"
|
||||
:height="'75%'"
|
||||
>
|
||||
<BillListComponent
|
||||
data-source="custom"
|
||||
@@ -158,7 +159,7 @@
|
||||
@click="handleTransactionClick"
|
||||
@delete="handleTransactionDelete"
|
||||
/>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
|
||||
<!-- 账单详情编辑弹出层 -->
|
||||
<TransactionDetail
|
||||
@@ -184,7 +185,7 @@ import {
|
||||
import { getTransactionDetail } from '@/api/transactionRecord'
|
||||
import BillListComponent from '@/components/Bill/BillListComponent.vue'
|
||||
import TransactionDetail from '@/components/TransactionDetail.vue'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
|
||||
const emailList = ref([])
|
||||
const loading = ref(false)
|
||||
|
||||
@@ -71,22 +71,27 @@
|
||||
</van-pull-refresh>
|
||||
|
||||
<!-- 详情弹出层 -->
|
||||
<PopupContainer
|
||||
v-model="detailVisible"
|
||||
<PopupContainerV2
|
||||
v-model:show="detailVisible"
|
||||
:title="currentMessage.title"
|
||||
:subtitle="currentMessage.createTime"
|
||||
height="75%"
|
||||
:height="'75%'"
|
||||
>
|
||||
<div
|
||||
v-if="currentMessage.messageType === 2"
|
||||
class="detail-content rich-html-content"
|
||||
v-html="currentMessage.content"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="detail-content"
|
||||
>
|
||||
{{ currentMessage.content }}
|
||||
<div style="padding: 16px">
|
||||
<p style="color: #999; font-size: 14px; margin-bottom: 12px; margin-top: 0">
|
||||
{{ currentMessage.createTime }}
|
||||
</p>
|
||||
<div
|
||||
v-if="currentMessage.messageType === 2"
|
||||
class="rich-html-content"
|
||||
style="font-size: 14px; line-height: 1.6"
|
||||
v-html="currentMessage.content"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
style="font-size: 14px; line-height: 1.6; white-space: pre-wrap"
|
||||
>
|
||||
{{ currentMessage.content }}
|
||||
</div>
|
||||
</div>
|
||||
<template
|
||||
v-if="currentMessage.url && currentMessage.messageType === 1"
|
||||
@@ -101,7 +106,7 @@
|
||||
查看详情
|
||||
</van-button>
|
||||
</template>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -111,7 +116,7 @@ import { useRouter } from 'vue-router'
|
||||
import { showToast, showDialog } from 'vant'
|
||||
import { getMessageList, markAsRead, deleteMessage, markAllAsRead } from '@/api/message'
|
||||
import { useMessageStore } from '@/stores/message'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
|
||||
const messageStore = useMessageStore()
|
||||
const router = useRouter()
|
||||
@@ -325,22 +330,6 @@ defineExpose({
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.detail-time {
|
||||
color: var(--van-text-color-2);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
padding: 16px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: var(--van-text-color);
|
||||
}
|
||||
|
||||
.detail-content:not(.rich-html-content) {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
:deep(.van-pull-refresh) {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
|
||||
@@ -107,141 +107,143 @@
|
||||
</div>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<PopupContainer
|
||||
v-model="dialogVisible"
|
||||
<PopupContainerV2
|
||||
v-model:show="dialogVisible"
|
||||
:title="isEdit ? '编辑周期账单' : '新增周期账单'"
|
||||
height="75%"
|
||||
:height="'75%'"
|
||||
>
|
||||
<van-form>
|
||||
<van-cell-group
|
||||
inset
|
||||
title="周期设置"
|
||||
>
|
||||
<van-field
|
||||
v-model="form.periodicTypeText"
|
||||
is-link
|
||||
readonly
|
||||
name="periodicType"
|
||||
label="周期"
|
||||
placeholder="请选择周期类型"
|
||||
:rules="[{ required: true, message: '请选择周期类型' }]"
|
||||
@click="showPeriodicTypePicker = true"
|
||||
/>
|
||||
|
||||
<!-- 每周配置 -->
|
||||
<van-field
|
||||
v-if="form.periodicType === 1"
|
||||
v-model="form.weekdaysText"
|
||||
is-link
|
||||
readonly
|
||||
name="weekdays"
|
||||
label="星期"
|
||||
placeholder="请选择星期几"
|
||||
:rules="[{ required: true, message: '请选择星期几' }]"
|
||||
@click="showWeekdaysPicker = true"
|
||||
/>
|
||||
|
||||
<!-- 每月配置 -->
|
||||
<van-field
|
||||
v-if="form.periodicType === 2"
|
||||
v-model="form.monthDaysText"
|
||||
is-link
|
||||
readonly
|
||||
name="monthDays"
|
||||
label="日期"
|
||||
placeholder="请选择每月的日期"
|
||||
:rules="[{ required: true, message: '请选择日期' }]"
|
||||
@click="showMonthDaysPicker = true"
|
||||
/>
|
||||
|
||||
<!-- 每季度配置 -->
|
||||
<van-field
|
||||
v-if="form.periodicType === 3"
|
||||
v-model="form.quarterDay"
|
||||
name="quarterDay"
|
||||
label="季度第几天"
|
||||
placeholder="请输入季度开始后第几天"
|
||||
type="number"
|
||||
:rules="[{ required: true, message: '请输入季度开始后第几天' }]"
|
||||
/>
|
||||
|
||||
<!-- 每年配置 -->
|
||||
<van-field
|
||||
v-if="form.periodicType === 4"
|
||||
v-model="form.yearDay"
|
||||
name="yearDay"
|
||||
label="年第几天"
|
||||
placeholder="请输入年开始后第几天"
|
||||
type="number"
|
||||
:rules="[{ required: true, message: '请输入年开始后第几天' }]"
|
||||
/>
|
||||
</van-cell-group>
|
||||
|
||||
<van-cell-group
|
||||
inset
|
||||
title="基本信息"
|
||||
>
|
||||
<van-field
|
||||
v-model="form.reason"
|
||||
name="reason"
|
||||
label="摘要"
|
||||
placeholder="请输入交易摘要"
|
||||
type="textarea"
|
||||
rows="2"
|
||||
autosize
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
<van-field
|
||||
v-model="form.amount"
|
||||
name="amount"
|
||||
label="金额"
|
||||
placeholder="请输入金额"
|
||||
type="number"
|
||||
:rules="[{ required: true, message: '请输入金额' }]"
|
||||
/>
|
||||
<van-field
|
||||
v-model="form.type"
|
||||
name="type"
|
||||
label="类型"
|
||||
<div style="padding: 0">
|
||||
<van-form>
|
||||
<van-cell-group
|
||||
inset
|
||||
title="周期设置"
|
||||
>
|
||||
<template #input>
|
||||
<van-radio-group
|
||||
v-model="form.type"
|
||||
direction="horizontal"
|
||||
>
|
||||
<van-radio :value="0">
|
||||
支出
|
||||
</van-radio>
|
||||
<van-radio :value="1">
|
||||
收入
|
||||
</van-radio>
|
||||
<van-radio :value="2">
|
||||
不计
|
||||
</van-radio>
|
||||
</van-radio-group>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field
|
||||
name="classify"
|
||||
label="分类"
|
||||
>
|
||||
<template #input>
|
||||
<span
|
||||
v-if="!form.classify"
|
||||
style="color: var(--van-gray-5)"
|
||||
>请选择交易分类</span>
|
||||
<span v-else>{{ form.classify }}</span>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field
|
||||
v-model="form.periodicTypeText"
|
||||
is-link
|
||||
readonly
|
||||
name="periodicType"
|
||||
label="周期"
|
||||
placeholder="请选择周期类型"
|
||||
:rules="[{ required: true, message: '请选择周期类型' }]"
|
||||
@click="showPeriodicTypePicker = true"
|
||||
/>
|
||||
|
||||
<!-- 分类选择组件 -->
|
||||
<ClassifySelector
|
||||
v-model="form.classify"
|
||||
:type="form.type"
|
||||
/>
|
||||
</van-cell-group>
|
||||
</van-form>
|
||||
<!-- 每周配置 -->
|
||||
<van-field
|
||||
v-if="form.periodicType === 1"
|
||||
v-model="form.weekdaysText"
|
||||
is-link
|
||||
readonly
|
||||
name="weekdays"
|
||||
label="星期"
|
||||
placeholder="请选择星期几"
|
||||
:rules="[{ required: true, message: '请选择星期几' }]"
|
||||
@click="showWeekdaysPicker = true"
|
||||
/>
|
||||
|
||||
<!-- 每月配置 -->
|
||||
<van-field
|
||||
v-if="form.periodicType === 2"
|
||||
v-model="form.monthDaysText"
|
||||
is-link
|
||||
readonly
|
||||
name="monthDays"
|
||||
label="日期"
|
||||
placeholder="请选择每月的日期"
|
||||
:rules="[{ required: true, message: '请选择日期' }]"
|
||||
@click="showMonthDaysPicker = true"
|
||||
/>
|
||||
|
||||
<!-- 每季度配置 -->
|
||||
<van-field
|
||||
v-if="form.periodicType === 3"
|
||||
v-model="form.quarterDay"
|
||||
name="quarterDay"
|
||||
label="季度第几天"
|
||||
placeholder="请输入季度开始后第几天"
|
||||
type="number"
|
||||
:rules="[{ required: true, message: '请输入季度开始后第几天' }]"
|
||||
/>
|
||||
|
||||
<!-- 每年配置 -->
|
||||
<van-field
|
||||
v-if="form.periodicType === 4"
|
||||
v-model="form.yearDay"
|
||||
name="yearDay"
|
||||
label="年第几天"
|
||||
placeholder="请输入年开始后第几天"
|
||||
type="number"
|
||||
:rules="[{ required: true, message: '请输入年开始后第几天' }]"
|
||||
/>
|
||||
</van-cell-group>
|
||||
|
||||
<van-cell-group
|
||||
inset
|
||||
title="基本信息"
|
||||
>
|
||||
<van-field
|
||||
v-model="form.reason"
|
||||
name="reason"
|
||||
label="摘要"
|
||||
placeholder="请输入交易摘要"
|
||||
type="textarea"
|
||||
rows="2"
|
||||
autosize
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
<van-field
|
||||
v-model="form.amount"
|
||||
name="amount"
|
||||
label="金额"
|
||||
placeholder="请输入金额"
|
||||
type="number"
|
||||
:rules="[{ required: true, message: '请输入金额' }]"
|
||||
/>
|
||||
<van-field
|
||||
v-model="form.type"
|
||||
name="type"
|
||||
label="类型"
|
||||
>
|
||||
<template #input>
|
||||
<van-radio-group
|
||||
v-model="form.type"
|
||||
direction="horizontal"
|
||||
>
|
||||
<van-radio :value="0">
|
||||
支出
|
||||
</van-radio>
|
||||
<van-radio :value="1">
|
||||
收入
|
||||
</van-radio>
|
||||
<van-radio :value="2">
|
||||
不计
|
||||
</van-radio>
|
||||
</van-radio-group>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field
|
||||
name="classify"
|
||||
label="分类"
|
||||
>
|
||||
<template #input>
|
||||
<span
|
||||
v-if="!form.classify"
|
||||
style="color: var(--van-gray-5)"
|
||||
>请选择交易分类</span>
|
||||
<span v-else>{{ form.classify }}</span>
|
||||
</template>
|
||||
</van-field>
|
||||
|
||||
<!-- 分类选择组件 -->
|
||||
<ClassifySelector
|
||||
v-model="form.classify"
|
||||
:type="form.type"
|
||||
/>
|
||||
</van-cell-group>
|
||||
</van-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<van-button
|
||||
round
|
||||
@@ -253,7 +255,7 @@
|
||||
{{ isEdit ? '更新' : '确认添加' }}
|
||||
</van-button>
|
||||
</template>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
|
||||
<!-- 周期类型选择器 -->
|
||||
<van-popup
|
||||
@@ -310,7 +312,7 @@ import {
|
||||
createPeriodic,
|
||||
updatePeriodic
|
||||
} from '@/api/transactionPeriodic'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
import ClassifySelector from '@/components/ClassifySelector.vue'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
|
||||
@@ -151,183 +151,43 @@
|
||||
<!-- 储蓄配置弹窗 -->
|
||||
<SavingsConfigPopup
|
||||
ref="savingsConfigRef"
|
||||
@success="loadBudgetData"
|
||||
@change="loadBudgetData"
|
||||
/>
|
||||
|
||||
<!-- 预算明细列表弹窗 -->
|
||||
<PopupContainer
|
||||
v-model="showListPopup"
|
||||
:title="popupTitle"
|
||||
height="80%"
|
||||
>
|
||||
<template #header-actions>
|
||||
<van-icon
|
||||
name="plus"
|
||||
size="20"
|
||||
title="添加预算"
|
||||
@click="budgetEditRef.open({ category: activeTab })"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<van-pull-refresh
|
||||
v-model="refreshing"
|
||||
style="min-height: 100%"
|
||||
@refresh="onRefresh"
|
||||
>
|
||||
<div class="budget-list">
|
||||
<!-- 支出列表 -->
|
||||
<template v-if="activeTab === BudgetCategory.Expense && expenseBudgets?.length > 0">
|
||||
<van-swipe-cell
|
||||
v-for="budget in expenseBudgets"
|
||||
:key="budget.id"
|
||||
>
|
||||
<BudgetCard
|
||||
:budget="budget"
|
||||
:progress-color="getProgressColor(budget)"
|
||||
:percent-class="{ warning: budget.current / budget.limit > 0.8 }"
|
||||
:period-label="getPeriodLabel(budget.type)"
|
||||
@click="handleEdit(budget)"
|
||||
>
|
||||
<template #amount-info>
|
||||
<div class="info-item">
|
||||
<div class="label">
|
||||
已支出
|
||||
</div>
|
||||
<div class="value expense">
|
||||
¥{{ formatMoney(budget.current) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="label">
|
||||
预算
|
||||
</div>
|
||||
<div class="value">
|
||||
¥{{ formatMoney(budget.limit) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="label">
|
||||
余额
|
||||
</div>
|
||||
<div
|
||||
class="value"
|
||||
:class="budget.limit - budget.current >= 0 ? 'income' : 'expense'"
|
||||
>
|
||||
¥{{ formatMoney(budget.limit - budget.current) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</BudgetCard>
|
||||
<template #right>
|
||||
<van-button
|
||||
square
|
||||
text="删除"
|
||||
type="danger"
|
||||
class="delete-button"
|
||||
@click="handleDelete(budget)"
|
||||
/>
|
||||
</template>
|
||||
</van-swipe-cell>
|
||||
</template>
|
||||
|
||||
<!-- 收入列表 -->
|
||||
<template v-if="activeTab === BudgetCategory.Income && incomeBudgets?.length > 0">
|
||||
<van-swipe-cell
|
||||
v-for="budget in incomeBudgets"
|
||||
:key="budget.id"
|
||||
>
|
||||
<BudgetCard
|
||||
:budget="budget"
|
||||
:progress-color="getProgressColor(budget)"
|
||||
:percent-class="{ income: budget.current / budget.limit >= 1 }"
|
||||
:period-label="getPeriodLabel(budget.type)"
|
||||
@click="handleEdit(budget)"
|
||||
>
|
||||
<template #amount-info>
|
||||
<div class="info-item">
|
||||
<div class="label">
|
||||
已收入
|
||||
</div>
|
||||
<div class="value income">
|
||||
¥{{ formatMoney(budget.current) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="label">
|
||||
目标
|
||||
</div>
|
||||
<div class="value">
|
||||
¥{{ formatMoney(budget.limit) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="label">
|
||||
差额
|
||||
</div>
|
||||
<div
|
||||
class="value"
|
||||
:class="budget.current >= budget.limit ? 'income' : 'expense'"
|
||||
>
|
||||
¥{{ formatMoney(Math.abs(budget.limit - budget.current)) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</BudgetCard>
|
||||
<template #right>
|
||||
<van-button
|
||||
square
|
||||
text="删除"
|
||||
type="danger"
|
||||
class="delete-button"
|
||||
@click="handleDelete(budget)"
|
||||
/>
|
||||
</template>
|
||||
</van-swipe-cell>
|
||||
</template>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<van-empty
|
||||
v-if="
|
||||
activeTab !== BudgetCategory.Savings &&
|
||||
!loading &&
|
||||
!hasError &&
|
||||
((activeTab === BudgetCategory.Expense && expenseBudgets?.length === 0) ||
|
||||
(activeTab === BudgetCategory.Income && incomeBudgets?.length === 0))
|
||||
"
|
||||
:description="`暂无${activeTab === BudgetCategory.Expense ? '支出' : '收入'}预算`"
|
||||
/>
|
||||
</div>
|
||||
<div style="height: calc(95px + env(safe-area-inset-bottom, 0px))" />
|
||||
</van-pull-refresh>
|
||||
</PopupContainer>
|
||||
|
||||
<!-- 未覆盖分类弹窗 -->
|
||||
<PopupContainer
|
||||
v-model="showUncoveredDetails"
|
||||
<PopupContainerV2
|
||||
v-model:show="showUncoveredDetails"
|
||||
title="未覆盖预算的分类"
|
||||
:subtitle="`本月共 <b style='color:var(--van-primary-color)'>${uncoveredCategories.length}</b> 个分类未设置预算`"
|
||||
height="60%"
|
||||
:height="'60%'"
|
||||
>
|
||||
<div class="uncovered-list">
|
||||
<div style="padding: 0">
|
||||
<!-- subtitle 作为内容区域顶部 -->
|
||||
<div
|
||||
v-for="item in uncoveredCategories"
|
||||
:key="item.category"
|
||||
class="uncovered-item"
|
||||
>
|
||||
<div class="item-left">
|
||||
<div class="category-name">
|
||||
{{ item.category }}
|
||||
style="padding: 12px 16px; text-align: center; color: #999; font-size: 14px; border-bottom: 1px solid var(--van-border-color)"
|
||||
v-html="`本月共 <b style='color:var(--van-primary-color)'>${uncoveredCategories.length}</b> 个分类未设置预算`"
|
||||
/>
|
||||
|
||||
<div class="uncovered-list">
|
||||
<div
|
||||
v-for="item in uncoveredCategories"
|
||||
:key="item.category"
|
||||
class="uncovered-item"
|
||||
>
|
||||
<div class="item-left">
|
||||
<div class="category-name">
|
||||
{{ item.category }}
|
||||
</div>
|
||||
<div class="transaction-count">
|
||||
{{ item.transactionCount }} 笔记录
|
||||
</div>
|
||||
</div>
|
||||
<div class="transaction-count">
|
||||
{{ item.transactionCount }} 笔记录
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-right">
|
||||
<div
|
||||
class="item-amount"
|
||||
:class="activeTab === BudgetCategory.Expense ? 'expense' : 'income'"
|
||||
>
|
||||
¥{{ formatMoney(item.totalAmount) }}
|
||||
<div class="item-right">
|
||||
<div
|
||||
class="item-amount"
|
||||
:class="activeTab === BudgetCategory.Expense ? 'expense' : 'income'"
|
||||
>
|
||||
¥{{ formatMoney(item.totalAmount) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -343,25 +203,31 @@
|
||||
我知道了
|
||||
</van-button>
|
||||
</template>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
|
||||
<!-- 归档总结弹窗 -->
|
||||
<PopupContainer
|
||||
v-model="showSummaryPopup"
|
||||
<PopupContainerV2
|
||||
v-model:show="showSummaryPopup"
|
||||
title="月份归档总结"
|
||||
:subtitle="`${selectedDate.getFullYear()}年${selectedDate.getMonth() + 1}月`"
|
||||
height="70%"
|
||||
:height="'70%'"
|
||||
>
|
||||
<div style="padding: 16px">
|
||||
<div
|
||||
class="rich-html-content"
|
||||
v-html="
|
||||
archiveSummary ||
|
||||
'<p style=\'text-align:center;color:var(--van-text-color-3)\'>暂无总结</p>'
|
||||
"
|
||||
/>
|
||||
<div style="padding: 0">
|
||||
<!-- subtitle -->
|
||||
<div style="padding: 12px 16px; text-align: center; color: #999; font-size: 14px; border-bottom: 1px solid var(--van-border-color)">
|
||||
{{ selectedDate.getFullYear() }}年{{ selectedDate.getMonth() + 1 }}月
|
||||
</div>
|
||||
|
||||
<div style="padding: 16px">
|
||||
<div
|
||||
class="rich-html-content"
|
||||
v-html="
|
||||
archiveSummary ||
|
||||
'<p style=\'text-align:center;color:var(--van-text-color-3)\'>暂无总结</p>'
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
|
||||
<!-- 日期选择器 -->
|
||||
<van-popup
|
||||
@@ -401,7 +267,7 @@ import BudgetTypeTabs from '@/components/BudgetTypeTabs.vue'
|
||||
import BudgetCard from '@/components/Budget/BudgetCard.vue'
|
||||
import BudgetEditPopup from '@/components/Budget/BudgetEditPopup.vue'
|
||||
import SavingsConfigPopup from '@/components/Budget/SavingsConfigPopup.vue'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
import ExpenseBudgetContent from './modules/ExpenseBudgetContent.vue'
|
||||
import IncomeBudgetContent from './modules/IncomeBudgetContent.vue'
|
||||
import SavingsBudgetContent from './modules/SavingsBudgetContent.vue'
|
||||
|
||||
@@ -71,10 +71,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 计划存款明细弹窗 -->
|
||||
<PopupContainer
|
||||
v-model="showDetailPopup"
|
||||
<PopupContainerV2
|
||||
v-model:show="showDetailPopup"
|
||||
title="计划存款明细"
|
||||
height="80%"
|
||||
:height="'80%'"
|
||||
>
|
||||
<div class="popup-body">
|
||||
<div
|
||||
@@ -169,14 +169,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PopupContainer>
|
||||
</PopupContainerV2>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import BudgetCard from '@/components/Budget/BudgetCard.vue'
|
||||
import { BudgetPeriodType } from '@/constants/enums'
|
||||
import PopupContainer from '@/components/PopupContainer.vue'
|
||||
import PopupContainerV2 from '@/components/PopupContainerV2.vue'
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
|
||||
Reference in New Issue
Block a user