清除原先不正确的功能模块页面文件

This commit is contained in:
2026-01-19 15:06:21 +08:00
parent 1d5930dc4c
commit f3177db4e1
9 changed files with 0 additions and 1346 deletions

View File

@@ -2,152 +2,4 @@ import config from "@/config"
import http from "@/utils/request"
export default {
family: {
list: {
url: `${config.API_URL}account/families/index`,
name: "家庭列表",
get: async function(params){
return await http.get(this.url, params);
}
},
detail: {
url: `${config.API_URL}account/families/detail`,
name: "家庭详情",
get: async function(params){
return await http.get(this.url, params);
}
},
add: {
url: `${config.API_URL}account/families/add`,
name: "家庭添加",
post: async function(params){
return await http.post(this.url, params);
}
},
edit: {
url: `${config.API_URL}account/families/edit`,
name: "家庭编辑",
post: async function(params){
return await http.put(this.url, params);
}
},
delete: {
url: `${config.API_URL}account/families/delete`,
name: "家庭删除",
post: async function(params){
return await http.delete(this.url, params);
}
}
},
accounts: {
list: {
url: `${config.API_URL}account/accounts/index`,
name: "账户列表",
get: async function(params){
return await http.get(this.url, params);
}
},
detail: {
url: `${config.API_URL}account/accounts/detail`,
name: "账户详情",
get: async function(params){
return await http.get(this.url, params);
}
},
add: {
url: `${config.API_URL}account/accounts/add`,
name: "账户添加",
post: async function(params){
return await http.post(this.url, params);
}
},
edit: {
url: `${config.API_URL}account/accounts/edit`,
name: "账户编辑",
post: async function(params){
return await http.put(this.url, params);
}
},
delete: {
url: `${config.API_URL}account/accounts/delete`,
name: "账户删除",
post: async function(params){
return await http.delete(this.url, params);
}
}
},
members: {
list: {
url: `${config.API_URL}account/members/index`,
name: "成员列表",
get: async function(params){
return await http.get(this.url, params);
}
},
detail: {
url: `${config.API_URL}account/members/detail`,
name: "成员详情",
get: async function(params){
return await http.get(this.url, params);
}
},
add: {
url: `${config.API_URL}account/members/add`,
name: "成员添加",
post: async function(params){
return await http.post(this.url, params);
}
},
edit: {
url: `${config.API_URL}account/members/edit`,
name: "成员编辑",
post: async function(params){
return await http.put(this.url, params);
}
},
delete: {
url: `${config.API_URL}account/members/delete`,
name: "成员删除",
post: async function(params){
return await http.delete(this.url, params);
}
}
},
records: {
list: {
url: `${config.API_URL}account/records/index`,
name: "记录列表",
get: async function(params){
return await http.get(this.url, params);
}
},
detail: {
url: `${config.API_URL}account/records/detail`,
name: "记录详情",
get: async function(params){
return await http.get(this.url, params);
}
},
add: {
url: `${config.API_URL}account/records/add`,
name: "记录添加",
post: async function(params){
return await http.post(this.url, params);
}
},
edit: {
url: `${config.API_URL}account/records/edit`,
name: "记录编辑",
post: async function(params){
return await http.put(this.url, params);
}
},
delete: {
url: `${config.API_URL}account/records/delete`,
name: "记录删除",
post: async function(params){
return await http.delete(this.url, params);
}
}
}
}

View File

@@ -1,146 +0,0 @@
<template>
<el-container>
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="add">添加账户</el-button>
</div>
<div class="right-panel">
<div class="right-panel-search">
<el-input v-model="search.name" placeholder="账户名称" clearable style="width: 150px;"></el-input>
<el-select v-model="search.type" placeholder="账户类型" clearable style="width: 120px;">
<el-option label="现金" value="cash"></el-option>
<el-option label="银行卡" value="bank_card"></el-option>
<el-option label="信用卡" value="credit_card"></el-option>
<el-option label="支付宝" value="alipay"></el-option>
<el-option label="微信" value="wechat"></el-option>
<el-option label="其他" value="other"></el-option>
</el-select>
<el-button type="primary" icon="el-icon-search" @click="upsearch">搜索</el-button>
</div>
</div>
</el-header>
<el-main class="nopadding">
<scTable ref="table" :apiObj="list.apiObj" :column="list.column" row-key="id" @selection-change="selectionChange" :params="search">
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="name" label="账户名称" min-width="150" />
<el-table-column label="账户类型" width="100">
<template #default="scope">
<el-tag :type="getTypeColor(scope.row.type)">{{ getTypeName(scope.row.type) }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="balance" label="余额" width="120" align="right">
<template #default="scope">
<span :style="{color: scope.row.balance >= 0 ? '#67C23A' : '#F56C6C'}">
{{ scope.row.balance.toFixed(2) }}
</span>
</template>
</el-table-column>
<el-table-column prop="family_name" label="所属家庭" width="120" />
<el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip />
<el-table-column prop="created_at" label="创建时间" width="180" />
<el-table-column prop="updated_at" label="更新时间" width="180" />
<el-table-column label="操作" width="180" fixed="right">
<template #default="scope">
<el-button-group>
<el-button type="primary" size="small" @click="edit(scope.row)">编辑</el-button>
<el-button type="primary" size="small" @click="show(scope.row)">查看</el-button>
<el-popconfirm title="确定删除吗?" @confirm="handleDelete(scope.row)">
<template #reference>
<el-button type="danger" size="small">删除</el-button>
</template>
</el-popconfirm>
</el-button-group>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
<save ref="saveBox" v-if="dialog.save" @success="upsearch" @closed="dialog.save=false" />
</template>
<script>
import save from './save.vue'
export default {
name: 'account.accounts',
components: { save },
data(){
return {
dialog: {
save: false
},
list: {
apiObj: this.$API.account.accounts.list,
column: []
},
search: {
name: '',
type: ''
},
selection: []
}
},
methods:{
getTypeName(type){
const map = {
'cash': '现金',
'bank_card': '银行卡',
'credit_card': '信用卡',
'alipay': '支付宝',
'wechat': '微信',
'other': '其他'
}
return map[type] || type
},
getTypeColor(type){
const map = {
'cash': 'success',
'bank_card': 'primary',
'credit_card': 'warning',
'alipay': 'success',
'wechat': 'success',
'other': 'info'
}
return map[type] || 'info'
},
upsearch(){
this.$refs.table.reload(this.search);
},
selectionChange(selection){
this.selection = selection;
},
add(){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveBox.open('add').setData({})
})
},
edit(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveBox.open('edit').setData(row)
})
},
show(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveBox.open('show').setData(row)
})
},
async handleDelete(row){
const res = await this.$API.account.accounts.delete.post({id: row.id})
if(res.code === 1){
this.$message.success('删除成功')
this.upsearch()
}else{
this.$message.error(res.message || '删除失败')
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,142 +0,0 @@
<template>
<el-drawer v-model="visible" :title="mode === 'show' ? '查看账户' : (mode === 'add' ? '添加账户' : '编辑账户')" size="600px" destroy-on-close>
<el-form :model="form" :rules="rules" ref="formRef" label-width="100px" :disabled="mode === 'show'">
<el-form-item label="账户名称" prop="name">
<el-input v-model="form.name" placeholder="请输入账户名称" maxlength="50" show-word-limit />
</el-form-item>
<el-form-item label="账户类型" prop="type">
<el-select v-model="form.type" placeholder="请选择账户类型" style="width: 100%;">
<el-option label="现金" value="cash"></el-option>
<el-option label="银行卡" value="bank_card"></el-option>
<el-option label="信用卡" value="credit_card"></el-option>
<el-option label="支付宝" value="alipay"></el-option>
<el-option label="微信" value="wechat"></el-option>
<el-option label="其他" value="other"></el-option>
</el-select>
</el-form-item>
<el-form-item label="所属家庭" prop="family_id">
<el-select v-model="form.family_id" placeholder="请选择所属家庭" style="width: 100%;">
<el-option
v-for="family in families"
:key="family.id"
:label="family.name"
:value="family.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="初始余额" prop="initial_balance">
<el-input-number v-model="form.initial_balance" :precision="2" :step="0.01" :min="0" style="width: 100%;" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" maxlength="200" show-word-limit />
</el-form-item>
<el-form-item label="创建者" v-if="form.creator_name">
<el-input v-model="form.creator_name" disabled />
</el-form-item>
<el-form-item label="创建时间" v-if="form.created_at">
<el-input v-model="form.created_at" disabled />
</el-form-item>
<el-form-item label="更新时间" v-if="form.updated_at">
<el-input v-model="form.updated_at" disabled />
</el-form-item>
</el-form>
<template #footer>
<div style="flex: auto">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="submit" v-if="mode !== 'show'">确定</el-button>
</div>
</template>
</el-drawer>
</template>
<script>
export default {
name: 'account.accounts.save',
data(){
return {
visible: false,
mode: 'add',
form: {
id: 0,
name: '',
type: 'cash',
family_id: null,
initial_balance: 0,
remark: '',
creator_name: '',
created_at: '',
updated_at: ''
},
rules: {
name: [
{ required: true, message: '请输入账户名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
type: [
{ required: true, message: '请选择账户类型', trigger: 'change' }
],
family_id: [
{ required: true, message: '请选择所属家庭', trigger: 'change' }
]
},
families: []
}
},
methods:{
open(mode){
this.mode = mode
this.visible = true
if(mode !== 'show'){
this.loadFamilies()
}
return this
},
setData(data){
this.form = {
id: data.id || 0,
name: data.name || '',
type: data.type || 'cash',
family_id: data.family_id || null,
initial_balance: data.initial_balance || 0,
remark: data.remark || '',
creator_name: data.creator_name || '',
created_at: data.created_at || '',
updated_at: data.updated_at || ''
}
return this
},
async loadFamilies(){
const res = await this.$API.account.family.list.get({})
if(res.code === 1){
this.families = res.data.list || []
}
},
async submit(){
await this.$refs.formRef.validate(async (valid) => {
if(valid){
const api = this.mode === 'add' ? this.$API.account.accounts.add : this.$API.account.accounts.edit
const res = await api.post(this.form)
if(res.code === 1){
this.$message.success(this.mode === 'add' ? '添加成功' : '编辑成功')
this.visible = false
this.$emit('success')
}else{
this.$message.error(res.message || '操作失败')
}
}
})
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,120 +0,0 @@
<template>
<el-container>
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="add">添加家庭</el-button>
</div>
<div class="right-panel">
<div class="right-panel-search">
<el-input v-model="search.name" placeholder="家庭名称" clearable></el-input>
<el-button type="primary" icon="el-icon-search" @click="upsearch">搜索</el-button>
</div>
</div>
</el-header>
<el-main class="nopadding">
<scTable ref="table" :apiObj="list.apiObj" :column="list.column" row-key="id" @selection-change="selectionChange" :params="search">
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="name" label="家庭名称" min-width="150" />
<el-table-column prop="description" label="描述" min-width="200" />
<el-table-column label="头像" width="80">
<template #default="scope">
<el-avatar v-if="scope.row.avatar" :src="scope.row.avatar" :size="40"></el-avatar>
<el-avatar v-else :size="40"></el-avatar>
</template>
</el-table-column>
<el-table-column label="成员数量" width="100">
<template #default="scope">
<el-tag type="primary">{{ scope.row.members_count || 0 }}</el-tag>
</template>
</el-table-column>
<el-table-column label="账户数量" width="100">
<template #default="scope">
<el-tag type="success">{{ scope.row.accounts_count || 0 }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="created_at" label="创建时间" width="180" />
<el-table-column prop="updated_at" label="更新时间" width="180" />
<el-table-column label="操作" width="180" fixed="right">
<template #default="scope">
<el-button-group>
<el-button type="primary" size="small" @click="edit(scope.row)">编辑</el-button>
<el-button type="primary" size="small" @click="show(scope.row)">查看</el-button>
<el-popconfirm title="确定删除吗?" @confirm="handleDelete(scope.row)">
<template #reference>
<el-button type="danger" size="small">删除</el-button>
</template>
</el-popconfirm>
</el-button-group>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
<save ref="saveBox" v-if="dialog.save" @success="upsearch" @closed="dialog.save=false" />
</template>
<script>
import save from './save.vue'
export default {
name: 'account.families',
components: { save },
data(){
return {
dialog: {
save: false
},
list: {
apiObj: this.$API.account.family.list,
column: []
},
search: {
name: ''
},
selection: []
}
},
mounted(){
},
methods:{
upsearch(){
this.$refs.table.reload(this.search);
},
selectionChange(selection){
this.selection = selection;
},
add(){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveBox.open('add').setData({})
})
},
edit(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveBox.open('edit').setData(row)
})
},
show(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveBox.open('show').setData(row)
})
},
async handleDelete(row){
const res = await this.$API.account.family.delete.post({id: row.id})
if(res.code === 1){
this.$message.success('删除成功')
this.upsearch()
}else{
this.$message.error(res.message || '删除失败')
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,175 +0,0 @@
<template>
<el-drawer v-model="visible" :title="mode === 'show' ? '查看家庭' : (mode === 'add' ? '添加家庭' : '编辑家庭')" size="600px" destroy-on-close>
<el-form :model="form" :rules="rules" ref="formRef" label-width="100px" :disabled="mode === 'show'">
<el-form-item label="家庭名称" prop="name">
<el-input v-model="form.name" placeholder="请输入家庭名称" maxlength="50" show-word-limit />
</el-form-item>
<el-form-item label="家庭描述" prop="description">
<el-input v-model="form.description" type="textarea" :rows="3" placeholder="请输入家庭描述" maxlength="200" show-word-limit />
</el-form-item>
<el-form-item label="家庭头像" prop="avatar">
<el-upload
class="avatar-uploader"
:action="uploadUrl"
:headers="uploadHeaders"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
v-if="mode !== 'show'">
<img v-if="form.avatar" :src="form.avatar" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
<el-avatar v-else-if="form.avatar" :src="form.avatar" :size="100"></el-avatar>
<el-avatar v-else :size="100"></el-avatar>
</el-form-item>
<el-form-item label="创建者" prop="creator_name" v-if="form.creator_name">
<el-input v-model="form.creator_name" disabled />
</el-form-item>
<el-form-item label="创建时间" v-if="form.created_at">
<el-input v-model="form.created_at" disabled />
</el-form-item>
<el-form-item label="更新时间" v-if="form.updated_at">
<el-input v-model="form.updated_at" disabled />
</el-form-item>
</el-form>
<template #footer>
<div style="flex: auto">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="submit" v-if="mode !== 'show'">确定</el-button>
</div>
</template>
</el-drawer>
</template>
<script>
import { Plus } from '@element-plus/icons-vue'
export default {
name: 'account.family.save',
components: { Plus },
data(){
return {
visible: false,
mode: 'add',
form: {
id: 0,
name: '',
description: '',
avatar: '',
creator_name: '',
created_at: '',
updated_at: ''
},
rules: {
name: [
{ required: true, message: '请输入家庭名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
]
},
uploadUrl: '',
uploadHeaders: {}
}
},
mounted(){
// 设置上传地址
const token = localStorage.getItem('token')
this.uploadHeaders = {
'Authorization': `Bearer ${token}`
}
// 这里需要根据实际情况配置上传接口
this.uploadUrl = '/api/upload/image'
},
methods:{
open(mode){
this.mode = mode
this.visible = true
return this
},
setData(data){
this.form = {
id: data.id || 0,
name: data.name || '',
description: data.description || '',
avatar: data.avatar || '',
creator_name: data.creator_name || '',
created_at: data.created_at || '',
updated_at: data.updated_at || ''
}
return this
},
handleAvatarSuccess(response){
if(response.code === 1){
this.form.avatar = response.data.url
this.$message.success('上传成功')
}else{
this.$message.error(response.message || '上传失败')
}
},
beforeAvatarUpload(file){
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png'
const isLt2M = file.size / 1024 / 1024 < 2
if (!isJPG) {
this.$message.error('上传头像图片只能是 JPG/PNG 格式!')
return false
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 2MB!')
return false
}
return true
},
async submit(){
await this.$refs.formRef.validate(async (valid) => {
if(valid){
const api = this.mode === 'add' ? this.$API.account.family.add : this.$API.account.family.edit
const res = await api.post(this.form)
if(res.code === 1){
this.$message.success(this.mode === 'add' ? '添加成功' : '编辑成功')
this.visible = false
this.$emit('success')
}else{
this.$message.error(res.message || '操作失败')
}
}
})
}
}
}
</script>
<style scoped>
.avatar-uploader {
width: 100px;
height: 100px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.avatar-uploader:hover {
border-color: #409EFF;
}
.avatar {
width: 100%;
height: 100%;
object-fit: cover;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
}
</style>

View File

@@ -1,129 +0,0 @@
<template>
<el-container>
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="add">添加成员</el-button>
</div>
<div class="right-panel">
<div class="right-panel-search">
<el-input v-model="search.nickname" placeholder="成员昵称" clearable style="width: 150px;"></el-input>
<el-select v-model="search.role" placeholder="成员角色" clearable style="width: 120px;">
<el-option label="家庭主" value="owner"></el-option>
<el-option label="管理员" value="admin"></el-option>
<el-option label="成员" value="member"></el-option>
</el-select>
<el-button type="primary" icon="el-icon-search" @click="upsearch">搜索</el-button>
</div>
</div>
</el-header>
<el-main class="nopadding">
<scTable ref="table" :apiObj="list.apiObj" :column="list.column" row-key="id" @selection-change="selectionChange" :params="search">
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="user.nickname" label="成员昵称" min-width="120" />
<el-table-column prop="user.phone" label="手机号" width="120" />
<el-table-column prop="family.name" label="所属家庭" width="150" />
<el-table-column label="角色" width="100">
<template #default="scope">
<el-tag :type="getRoleColor(scope.row.role)">{{ getRoleName(scope.row.role) }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="joined_at" label="加入时间" width="180" />
<el-table-column label="操作" width="180" fixed="right">
<template #default="scope">
<el-button-group>
<el-button type="primary" size="small" @click="edit(scope.row)">编辑</el-button>
<el-button type="primary" size="small" @click="show(scope.row)">查看</el-button>
<el-popconfirm title="确定移除该成员吗?" @confirm="handleDelete(scope.row)">
<template #reference>
<el-button type="danger" size="small">移除</el-button>
</template>
</el-popconfirm>
</el-button-group>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
<save ref="saveBox" v-if="dialog.save" @success="upsearch" @closed="dialog.save=false" />
</template>
<script>
import save from './save.vue'
export default {
name: 'account.members',
components: { save },
data(){
return {
dialog: {
save: false
},
list: {
apiObj: this.$API.account.members.list,
column: []
},
search: {
nickname: '',
role: ''
},
selection: []
}
},
methods:{
getRoleName(role){
const map = {
'owner': '家庭主',
'admin': '管理员',
'member': '成员'
}
return map[role] || role
},
getRoleColor(role){
const map = {
'owner': 'danger',
'admin': 'warning',
'member': 'primary'
}
return map[role] || 'info'
},
upsearch(){
this.$refs.table.reload(this.search);
},
selectionChange(selection){
this.selection = selection;
},
add(){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveBox.open('add').setData({})
})
},
edit(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveBox.open('edit').setData(row)
})
},
show(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveBox.open('show').setData(row)
})
},
async handleDelete(row){
const res = await this.$API.account.members.delete.post({id: row.id})
if(res.code === 1){
this.$message.success('移除成功')
this.upsearch()
}else{
this.$message.error(res.message || '移除失败')
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,128 +0,0 @@
<template>
<el-drawer v-model="visible" :title="mode === 'show' ? '查看成员' : (mode === 'add' ? '添加成员' : '编辑成员')" size="600px" destroy-on-close>
<el-form :model="form" :rules="rules" ref="formRef" label-width="100px" :disabled="mode === 'show'">
<el-form-item label="所属家庭" prop="family_id">
<el-select v-model="form.family_id" placeholder="请选择所属家庭" style="width: 100%;" :disabled="mode === 'edit'">
<el-option
v-for="family in families"
:key="family.id"
:label="family.name"
:value="family.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="用户手机号" prop="user_phone">
<el-input v-model="form.user_phone" placeholder="请输入用户手机号" :disabled="mode === 'edit'" />
</el-form-item>
<el-form-item label="角色" prop="role">
<el-select v-model="form.role" placeholder="请选择角色" style="width: 100%;">
<el-option label="家庭主" value="owner"></el-option>
<el-option label="管理员" value="admin"></el-option>
<el-option label="成员" value="member"></el-option>
</el-select>
</el-form-item>
<el-form-item label="加入时间" v-if="form.joined_at">
<el-input v-model="form.joined_at" disabled />
</el-form-item>
<el-form-item label="用户信息" v-if="form.user">
<el-descriptions :column="1" border>
<el-descriptions-item label="用户ID">{{ form.user.id }}</el-descriptions-item>
<el-descriptions-item label="昵称">{{ form.user.nickname }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ form.user.phone }}</el-descriptions-item>
<el-descriptions-item label="邮箱">{{ form.user.email || '-' }}</el-descriptions-item>
</el-descriptions>
</el-form-item>
</el-form>
<template #footer>
<div style="flex: auto">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="submit" v-if="mode !== 'show'">确定</el-button>
</div>
</template>
</el-drawer>
</template>
<script>
export default {
name: 'account.members.save',
data(){
return {
visible: false,
mode: 'add',
form: {
id: 0,
family_id: null,
user_id: null,
user_phone: '',
role: 'member',
joined_at: '',
user: null
},
rules: {
family_id: [
{ required: true, message: '请选择所属家庭', trigger: 'change' }
],
user_phone: [
{ required: true, message: '请输入用户手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
],
role: [
{ required: true, message: '请选择角色', trigger: 'change' }
]
},
families: []
}
},
methods:{
open(mode){
this.mode = mode
this.visible = true
if(mode !== 'show'){
this.loadFamilies()
}
return this
},
setData(data){
this.form = {
id: data.id || 0,
family_id: data.family_id || null,
user_id: data.user_id || null,
user_phone: data.user?.phone || '',
role: data.role || 'member',
joined_at: data.joined_at || '',
user: data.user || null
}
return this
},
async loadFamilies(){
const res = await this.$API.account.family.list.get({})
if(res.code === 1){
this.families = res.data.list || []
}
},
async submit(){
await this.$refs.formRef.validate(async (valid) => {
if(valid){
const api = this.mode === 'add' ? this.$API.account.members.add : this.$API.account.members.edit
const res = await api.post(this.form)
if(res.code === 1){
this.$message.success(this.mode === 'add' ? '添加成功' : '编辑成功')
this.visible = false
this.$emit('success')
}else{
this.$message.error(res.message || '操作失败')
}
}
})
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,142 +0,0 @@
<template>
<el-container>
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="add">添加记录</el-button>
</div>
<div class="right-panel">
<div class="right-panel-search">
<el-input v-model="search.keyword" placeholder="搜索关键词" clearable style="width: 150px;"></el-input>
<el-select v-model="search.type" placeholder="记录类型" clearable style="width: 120px;">
<el-option label="收入" value="income"></el-option>
<el-option label="支出" value="expense"></el-option>
</el-select>
<el-date-picker
v-model="search.date_range"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
style="width: 260px;">
</el-date-picker>
<el-button type="primary" icon="el-icon-search" @click="upsearch">搜索</el-button>
</div>
</div>
</el-header>
<el-main class="nopadding">
<scTable ref="table" :apiObj="list.apiObj" :column="list.column" row-key="id" @selection-change="selectionChange" :params="searchParams">
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="ID" width="80" />
<el-table-column label="类型" width="80">
<template #default="scope">
<el-tag :type="scope.row.type === 'income' ? 'success' : 'danger'">
{{ scope.row.type === 'income' ? '收入' : '支出' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="account.name" label="账户" width="120" />
<el-table-column prop="category.name" label="分类" width="120" />
<el-table-column prop="amount" label="金额" width="120" align="right">
<template #default="scope">
<span :style="{color: scope.row.type === 'income' ? '#67C23A' : '#F56C6C', fontSize: '16px', fontWeight: 'bold'}">
{{ scope.row.type === 'income' ? '+' : '-' }}{{ scope.row.amount.toFixed(2) }}
</span>
</template>
</el-table-column>
<el-table-column prop="date" label="日期" width="120" />
<el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip />
<el-table-column prop="created_at" label="创建时间" width="180" />
<el-table-column label="操作" width="180" fixed="right">
<template #default="scope">
<el-button-group>
<el-button type="primary" size="small" @click="edit(scope.row)">编辑</el-button>
<el-button type="primary" size="small" @click="show(scope.row)">查看</el-button>
<el-popconfirm title="确定删除吗?" @confirm="handleDelete(scope.row)">
<template #reference>
<el-button type="danger" size="small">删除</el-button>
</template>
</el-popconfirm>
</el-button-group>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
<save ref="saveBox" v-if="dialog.save" @success="upsearch" @closed="dialog.save=false" />
</template>
<script>
import save from './save.vue'
export default {
name: 'account.records',
components: { save },
data(){
return {
dialog: {
save: false
},
list: {
apiObj: this.$API.account.records.list,
column: []
},
search: {
keyword: '',
type: '',
date_range: []
},
selection: []
}
},
computed: {
searchParams(){
const params = {...this.search}
if(params.date_range && params.date_range.length === 2){
params.start_date = params.date_range[0]
params.end_date = params.date_range[1]
}
delete params.date_range
return params
}
},
methods:{
upsearch(){
this.$refs.table.reload(this.searchParams);
},
selectionChange(selection){
this.selection = selection;
},
add(){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveBox.open('add').setData({})
})
},
edit(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveBox.open('edit').setData(row)
})
},
show(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveBox.open('show').setData(row)
})
},
async handleDelete(row){
const res = await this.$API.account.records.delete.post({id: row.id})
if(res.code === 1){
this.$message.success('删除成功')
this.upsearch()
}else{
this.$message.error(res.message || '删除失败')
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,216 +0,0 @@
<template>
<el-drawer v-model="visible" :title="mode === 'show' ? '查看记录' : (mode === 'add' ? '添加记录' : '编辑记录')" size="600px" destroy-on-close>
<el-form :model="form" :rules="rules" ref="formRef" label-width="100px" :disabled="mode === 'show'">
<el-form-item label="记录类型" prop="type">
<el-radio-group v-model="form.type">
<el-radio label="income">收入</el-radio>
<el-radio label="expense">支出</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="所属家庭" prop="family_id">
<el-select v-model="form.family_id" placeholder="请选择所属家庭" style="width: 100%;" @change="handleFamilyChange">
<el-option
v-for="family in families"
:key="family.id"
:label="family.name"
:value="family.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="账户" prop="account_id">
<el-select v-model="form.account_id" placeholder="请选择账户" style="width: 100%;">
<el-option
v-for="account in accounts"
:key="account.id"
:label="account.name"
:value="account.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="分类" prop="category_id">
<el-select v-model="form.category_id" placeholder="请选择分类" style="width: 100%;">
<el-option
v-for="category in filteredCategories"
:key="category.id"
:label="category.name"
:value="category.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="金额" prop="amount">
<el-input-number v-model="form.amount" :precision="2" :step="0.01" :min="0.01" style="width: 100%;" />
</el-form-item>
<el-form-item label="日期" prop="date">
<el-date-picker
v-model="form.date"
type="date"
placeholder="选择日期"
style="width: 100%;">
</el-date-picker>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" maxlength="200" show-word-limit />
</el-form-item>
<el-form-item label="创建者" v-if="form.creator_name">
<el-input v-model="form.creator_name" disabled />
</el-form-item>
<el-form-item label="创建时间" v-if="form.created_at">
<el-input v-model="form.created_at" disabled />
</el-form-item>
<el-form-item label="更新时间" v-if="form.updated_at">
<el-input v-model="form.updated_at" disabled />
</el-form-item>
</el-form>
<template #footer>
<div style="flex: auto">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="submit" v-if="mode !== 'show'">确定</el-button>
</div>
</template>
</el-drawer>
</template>
<script>
export default {
name: 'account.records.save',
data(){
return {
visible: false,
mode: 'add',
form: {
id: 0,
type: 'expense',
family_id: null,
account_id: null,
category_id: null,
amount: 0,
date: '',
remark: '',
creator_name: '',
created_at: '',
updated_at: ''
},
rules: {
type: [
{ required: true, message: '请选择记录类型', trigger: 'change' }
],
family_id: [
{ required: true, message: '请选择所属家庭', trigger: 'change' }
],
account_id: [
{ required: true, message: '请选择账户', trigger: 'change' }
],
category_id: [
{ required: true, message: '请选择分类', trigger: 'change' }
],
amount: [
{ required: true, message: '请输入金额', trigger: 'blur' },
{ type: 'number', min: 0.01, message: '金额必须大于0', trigger: 'blur' }
],
date: [
{ required: true, message: '请选择日期', trigger: 'change' }
]
},
families: [],
accounts: [],
categories: []
}
},
computed: {
filteredCategories(){
return this.categories.filter(cat => cat.type === this.form.type)
}
},
methods:{
open(mode){
this.mode = mode
this.visible = true
if(mode !== 'show'){
this.loadFamilies()
this.loadCategories()
}
return this
},
setData(data){
this.form = {
id: data.id || 0,
type: data.type || 'expense',
family_id: data.family_id || null,
account_id: data.account_id || null,
category_id: data.category_id || null,
amount: data.amount || 0,
date: data.date || '',
remark: data.remark || '',
creator_name: data.creator_name || '',
created_at: data.created_at || '',
updated_at: data.updated_at || ''
}
if(data.family_id){
this.loadAccounts(data.family_id)
}
return this
},
async loadFamilies(){
const res = await this.$API.account.family.list.get({})
if(res.code === 1){
this.families = res.data.list || []
}
},
async loadAccounts(familyId){
const res = await this.$API.account.accounts.list.get({family_id: familyId})
if(res.code === 1){
this.accounts = res.data.list || []
}
},
async loadCategories(){
// 这里需要根据实际的分类接口进行调整
// 暂时使用模拟数据
this.categories = [
{id: 1, name: '餐饮', type: 'expense'},
{id: 2, name: '购物', type: 'expense'},
{id: 3, name: '交通', type: 'expense'},
{id: 4, name: '娱乐', type: 'expense'},
{id: 5, name: '工资', type: 'income'},
{id: 6, name: '奖金', type: 'income'},
{id: 7, name: '投资', type: 'income'}
]
},
handleFamilyChange(){
this.form.account_id = null
if(this.form.family_id){
this.loadAccounts(this.form.family_id)
}else{
this.accounts = []
}
},
async submit(){
await this.$refs.formRef.validate(async (valid) => {
if(valid){
const api = this.mode === 'add' ? this.$API.account.records.add : this.$API.account.records.edit
const res = await api.post(this.form)
if(res.code === 1){
this.$message.success(this.mode === 'add' ? '添加成功' : '编辑成功')
this.visible = false
this.$emit('success')
}else{
this.$message.error(res.message || '操作失败')
}
}
})
}
}
}
</script>
<style scoped>
</style>