AI-accounting-soft-uniApp/pages/category/category.vue

429 lines
9.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container">
<view class="header">
<text class="title">分类管理</text>
<button class="add-btn" @click="showAddModal = true">+ 添加</button>
</view>
<view class="tabs">
<view class="tab" :class="{ active: activeType === 1 }" @click="activeType = 1">
支出
</view>
<view class="tab" :class="{ active: activeType === 2 }" @click="activeType = 2">
收入
</view>
</view>
<view class="category-list">
<view v-for="category in filteredCategories" :key="category.id" class="category-item">
<text class="category-icon">{{ category.icon }}</text>
<text class="category-name">{{ category.name }}</text>
<view v-if="!category.userId" class="tag">系统</view>
<view v-else class="actions">
<text class="action-btn" @click="editCategory(category)">编辑</text>
<text class="action-btn delete" @click="deleteCategory(category.id)">删除</text>
</view>
</view>
</view>
<!-- 添加/编辑弹窗 -->
<view v-if="showAddModal || editingCategory" class="modal" @click="closeModal">
<view class="modal-content" @click.stop>
<view class="modal-header">
<text class="modal-title">{{ editingCategory ? '编辑分类' : '添加分类' }}</text>
<text class="close-btn" @click="closeModal">×</text>
</view>
<view class="modal-body">
<view class="form-item">
<text class="label">分类名称</text>
<input v-model="form.name" placeholder="请输入分类名称" class="input" />
</view>
<view class="form-item">
<text class="label">图标</text>
<input v-model="form.icon" placeholder="请输入图标(如:🍔)" class="input" />
</view>
<button class="submit-btn" @click="submitCategory">保存</button>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { getCategories, createCategory, updateCategory, deleteCategory as deleteCategoryApi } from '../../api/category'
const activeType = ref(1)
const categories = ref([])
const showAddModal = ref(false)
const editingCategory = ref(null)
const form = ref({
name: '',
icon: '📦',
type: 1
})
const filteredCategories = computed(() => {
return categories.value.filter(cat => cat.type === activeType.value)
})
const loadCategories = async () => {
try {
const data = await getCategories()
categories.value = data || []
} catch (error) {
console.error('加载分类失败', error)
}
}
const editCategory = (category) => {
editingCategory.value = category
form.value = {
name: category.name,
icon: category.icon,
type: category.type
}
}
const deleteCategory = async (id) => {
uni.showModal({
title: '确认删除',
content: '确定要删除这个分类吗?',
success: async (res) => {
if (res.confirm) {
try {
await deleteCategoryApi(id)
uni.showToast({
title: '删除成功',
icon: 'success'
})
loadCategories()
} catch (error) {
uni.showToast({
title: '删除失败',
icon: 'none'
})
}
}
}
})
}
const submitCategory = async () => {
if (!form.value.name) {
uni.showToast({
title: '请输入分类名称',
icon: 'none'
})
return
}
try {
form.value.type = activeType.value
if (editingCategory.value) {
await updateCategory(editingCategory.value.id, form.value)
uni.showToast({
title: '更新成功',
icon: 'success'
})
} else {
await createCategory(form.value)
uni.showToast({
title: '添加成功',
icon: 'success'
})
}
closeModal()
loadCategories()
} catch (error) {
uni.showToast({
title: editingCategory.value ? '更新失败' : '添加失败',
icon: 'none'
})
}
}
const closeModal = () => {
showAddModal.value = false
editingCategory.value = null
form.value = {
name: '',
icon: '📦',
type: 1
}
}
onMounted(() => {
loadCategories()
})
</script>
<style scoped>
.container {
min-height: 100vh;
background: linear-gradient(135deg, #FFE5CC 0%, #FFD4A8 50%, #FFC08A 100%);
padding-bottom: calc(50rpx + env(safe-area-inset-bottom));
}
.header {
background: linear-gradient(135deg, #C8956E, #F5D59E, #FFD700);
padding: 40rpx 30rpx;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 3rpx solid #5D4037;
box-shadow: 0 8rpx 20rpx rgba(93, 64, 55, 0.15);
}
.title {
font-size: 40rpx;
font-weight: bold;
color: #5D4037;
text-shadow: 2rpx 2rpx 0 rgba(255, 215, 0, 0.3);
}
.add-btn {
background: rgba(255, 255, 255, 0.9);
color: #5D4037;
padding: 15rpx 30rpx;
border-radius: 50rpx;
font-size: 26rpx;
font-weight: bold;
border: 3rpx solid #5D4037;
box-shadow: 0 4rpx 15rpx rgba(93, 64, 55, 0.2);
}
.tabs {
display: flex;
background: rgba(255, 255, 255, 0.9);
margin: 20rpx;
padding: 10rpx;
border-radius: 20rpx;
border: 3rpx solid #C8956E;
box-shadow: 0 8rpx 20rpx rgba(200, 149, 110, 0.15);
}
.tab {
flex: 1;
text-align: center;
padding: 20rpx;
border-radius: 15rpx;
color: #8D6E63;
font-weight: 600;
transition: all 0.3s;
}
.tab.active {
background: linear-gradient(135deg, #C8956E, #F5D59E);
color: #5D4037;
box-shadow: 0 4rpx 15rpx rgba(200, 149, 110, 0.3);
}
.category-list {
padding: 20rpx;
}
.category-item {
background: rgba(255, 255, 255, 0.95);
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
display: flex;
align-items: center;
border: 3rpx solid #C8956E;
box-shadow: 0 8rpx 20rpx rgba(200, 149, 110, 0.15);
transition: all 0.3s;
}
.category-item:active {
transform: scale(0.98);
box-shadow: 0 4rpx 10rpx rgba(200, 149, 110, 0.1);
}
.category-icon {
font-size: 52rpx;
margin-right: 25rpx;
filter: drop-shadow(0 3rpx 6rpx rgba(0, 0, 0, 0.15));
}
.category-name {
flex: 1;
font-size: 30rpx;
color: #5D4037;
font-weight: 600;
}
.tag {
background: linear-gradient(135deg, rgba(200, 149, 110, 0.2), rgba(255, 215, 0, 0.2));
color: #8D6E63;
padding: 8rpx 20rpx;
border-radius: 50rpx;
font-size: 22rpx;
margin-right: 20rpx;
font-weight: 600;
border: 2rpx solid #C8956E;
}
.actions {
display: flex;
gap: 25rpx;
}
.action-btn {
color: #FFD700;
font-size: 26rpx;
font-weight: 600;
padding: 8rpx 15rpx;
border-radius: 10rpx;
background: rgba(255, 215, 0, 0.1);
}
.action-btn.delete {
color: #FA3534;
background: rgba(250, 53, 52, 0.1);
}
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(93, 64, 55, 0.7);
backdrop-filter: blur(10rpx);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.modal-content {
background: rgba(255, 255, 255, 0.98);
border-radius: 25rpx;
width: 85%;
max-width: 600rpx;
border: 3rpx solid #C8956E;
box-shadow: 0 20rpx 60rpx rgba(93, 64, 55, 0.3);
animation: slideUp 0.3s ease-out;
}
@keyframes slideUp {
from {
transform: translateY(100rpx);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 35rpx 30rpx;
background: linear-gradient(135deg, #C8956E, #F5D59E);
border-bottom: 3rpx solid #5D4037;
border-radius: 22rpx 22rpx 0 0;
}
.modal-title {
font-size: 34rpx;
font-weight: bold;
color: #5D4037;
}
.close-btn {
font-size: 50rpx;
color: #5D4037;
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.8);
border-radius: 50%;
border: 2rpx solid #5D4037;
transition: all 0.3s;
}
.close-btn:active {
transform: scale(0.9);
background: #fff;
}
.modal-body {
padding: 40rpx 30rpx;
}
.form-item {
margin-bottom: 35rpx;
}
.label {
display: block;
font-size: 28rpx;
color: #5D4037;
margin-bottom: 20rpx;
font-weight: 600;
}
.input {
width: 100%;
padding: 25rpx;
background: rgba(255, 255, 255, 0.9);
border-radius: 15rpx;
font-size: 28rpx;
box-sizing: border-box;
border: 3rpx solid #C8956E;
outline: none;
color: #5D4037;
box-shadow: 0 4rpx 15rpx rgba(200, 149, 110, 0.1);
transition: all 0.3s;
}
.input:focus {
border-color: #FFD700;
box-shadow: 0 0 0 4rpx rgba(255, 215, 0, 0.3), 0 8rpx 25rpx rgba(255, 215, 0, 0.2);
}
.input::placeholder {
color: #BCAAA4;
}
.submit-btn {
width: 100%;
height: 96rpx;
background: linear-gradient(135deg, #C8956E, #F5D59E, #FFD700);
color: #5D4037;
border-radius: 50rpx;
font-size: 32rpx;
font-weight: bold;
border: 4rpx solid #5D4037;
box-shadow: 0 10rpx 30rpx rgba(200, 149, 110, 0.4);
transition: all 0.3s;
}
.submit-btn:active {
transform: scale(0.98);
box-shadow: 0 5rpx 15rpx rgba(200, 149, 110, 0.3);
}
</style>