Files
jizhi/admin-web/src/views/SysCategories.vue
T
2026-07-24 23:11:20 +08:00

122 lines
5.8 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.
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { api } from '../api'
import { message } from 'ant-design-vue'
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons-vue'
interface Cat { id: number; name: string; iconKey: string; type: string; sortOrder: number; isDeleted: boolean }
const list = ref<Cat[]>([])
const loading = ref(true)
const modalVisible = ref(false)
const editing = ref<Cat | null>(null)
const form = ref({ name: '', iconKey: 'tag', type: 'expense' })
const iconOptions = [
{ value: 'food', label: '🍜 餐饮' }, { value: 'cup', label: '🥤 饮品' },
{ value: 'cart', label: '🛒 购物' }, { value: 'metro', label: '🚇 交通' },
{ value: 'house', label: '🏠 住房' }, { value: 'game', label: '🎮 娱乐' },
{ value: 'pill', label: '💊 医疗' }, { value: 'book', label: '📖 学习' },
{ value: 'shirt', label: '👕 服饰' }, { value: 'gift', label: '🎁 人情' },
{ value: 'plane', label: '✈️ 旅行' }, { value: 'tag', label: '🏷️ 其他' },
{ value: 'money', label: '💰 工资' }, { value: 'briefcase', label: '💼 兼职' },
{ value: 'chart', label: '📈 理财' }, { value: 'card', label: '💳 报销' },
{ value: 'sparkle', label: '✨ 奖金' },
]
const expenseCats = () => list.value.filter(c => c.type === 'Expense')
const incomeCats = () => list.value.filter(c => c.type === 'Income')
onMounted(refresh)
async function refresh() { loading.value = true; try { list.value = await api.sysCategories() } finally { loading.value = false } }
function openNew() { editing.value = null; form.value = { name: '', iconKey: 'tag', type: 'expense' }; modalVisible.value = true }
function openEdit(c: Cat) {
editing.value = c
form.value = { name: c.name, iconKey: c.iconKey, type: c.type === 'Income' ? 'income' : 'expense' }
modalVisible.value = true
}
async function save() {
const d = { name: form.value.name, iconKey: form.value.iconKey, type: form.value.type }
if (editing.value) await api.updateSysCategory(editing.value.id, d)
else await api.createSysCategory(d)
message.success(editing.value ? '已更新' : '已创建')
modalVisible.value = false
refresh()
}
async function del(id: number) { await api.deleteSysCategory(id); message.success('已删除(软删)'); refresh() }
</script>
<template>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px">
<h2>系统默认分类</h2>
<a-button type="primary" @click="openNew"><PlusOutlined /> 新建分类</a-button>
</div>
<p style="color:#999;font-size:12px;margin-bottom:14px">
此处管理新用户注册时获得的默认分类删除后已分配用户的分类不受影响软删除仅对新用户隐藏)。
</p>
<a-row :gutter="16">
<a-col :span="12">
<a-card title="💸 支出分类" size="small">
<a-table :columns="[
{ title: '排序', dataIndex: 'sortOrder', width: 55 },
{ title: '图标', key: 'icon', width: 55 },
{ title: '名称', dataIndex: 'name', key: 'name' },
{ title: '', key: 'act', width: 90 },
]" :dataSource="expenseCats()" :loading="loading" rowKey="id" size="small" :pagination="{ pageSize: 10 }">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'icon'">
<span style="font-size:18px">{{ iconOptions.find(i=>i.value===record.iconKey)?.label?.split(' ')[0] || '🏷️' }}</span>
</template>
<template v-if="column.key === 'act'">
<a-button size="small" type="link" @click="openEdit(record)"><EditOutlined /></a-button>
<a-popconfirm title="确定软删除?" @confirm="del(record.id)">
<a-button size="small" type="link" danger><DeleteOutlined /></a-button>
</a-popconfirm>
</template>
</template>
</a-table>
</a-card>
</a-col>
<a-col :span="12">
<a-card title="💰 收入分类" size="small">
<a-table :columns="[
{ title: '排序', dataIndex: 'sortOrder', width: 55 },
{ title: '图标', key: 'icon', width: 55 },
{ title: '名称', dataIndex: 'name', key: 'name' },
{ title: '', key: 'act', width: 90 },
]" :dataSource="incomeCats()" :loading="loading" rowKey="id" size="small" :pagination="{ pageSize: 10 }">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'icon'">
<span style="font-size:18px">{{ iconOptions.find(i=>i.value===record.iconKey)?.label?.split(' ')[0] || '🏷️' }}</span>
</template>
<template v-if="column.key === 'act'">
<a-button size="small" type="link" @click="openEdit(record)"><EditOutlined /></a-button>
<a-popconfirm title="确定软删除?" @confirm="del(record.id)">
<a-button size="small" type="link" danger><DeleteOutlined /></a-button>
</a-popconfirm>
</template>
</template>
</a-table>
</a-card>
</a-col>
</a-row>
<a-modal v-model:open="modalVisible" :title="editing ? '编辑分类' : '新建系统分类'" @ok="save" :width="400">
<a-form layout="vertical" style="margin-top:8px">
<a-form-item label="分类名称"><a-input v-model:value="form.name" placeholder="如:宠物" /></a-form-item>
<a-form-item label="图标">
<a-select v-model:value="form.iconKey" :options="iconOptions" />
</a-form-item>
<a-form-item label="类型">
<a-radio-group v-model:value="form.type">
<a-radio value="expense">💸 支出</a-radio>
<a-radio value="income">💰 收入</a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</a-modal>
</template>