2025-10-15
添加金融模块
This commit is contained in:
15
src/api/financial.js
Normal file
15
src/api/financial.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import {financialRequest} from '@/utils/api'
|
||||
|
||||
// 金融相关API
|
||||
export const financialAPI = {
|
||||
// 登录
|
||||
list: (params) => financialRequest.get('/financial/list', {params}),
|
||||
add: (data) => financialRequest.post('/financial', data),
|
||||
update: (data) => financialRequest.patch("/financial", data),
|
||||
delete: (id) => financialRequest.delete(`/financial/${id}`),
|
||||
getOne: (id) => financialRequest.get(`/financial/${id}`),
|
||||
}
|
||||
|
||||
export default {
|
||||
financialAPI
|
||||
}
|
||||
1
src/assets/svg/financial.svg
Normal file
1
src/assets/svg/financial.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.1 KiB |
@@ -115,7 +115,7 @@ const editorConfig = {
|
||||
fontFamily: {
|
||||
fontFamilyList: [
|
||||
'黑体', '仿宋', '楷体', '标楷体', '华文仿宋', '华文楷体', '宋体', '微软雅黑',
|
||||
'Arial', 'Tahoma', 'Verdana', 'Times New Roman', 'Courier New'
|
||||
// 'Arial', 'Tahoma', 'Verdana', 'Times New Roman', 'Courier New'
|
||||
]
|
||||
},
|
||||
// 配置颜色
|
||||
|
||||
@@ -3,131 +3,177 @@
|
||||
<!-- 侧边栏 -->
|
||||
<el-aside :width="isCollapse ? '64px' : '200px'" class="sidebar">
|
||||
<div class="logo">
|
||||
<img v-if="!isCollapse" src="/logo.svg" alt="Logo" class="logo-img" />
|
||||
<img v-if="!isCollapse" src="/logo.svg" alt="Logo" class="logo-img"/>
|
||||
<span v-if="!isCollapse" class="logo-text">项目后台管理</span>
|
||||
<el-icon v-else class="logo-icon"><Setting /></el-icon>
|
||||
<el-icon v-else class="logo-icon">
|
||||
<Setting/>
|
||||
</el-icon>
|
||||
</div>
|
||||
|
||||
|
||||
<el-menu
|
||||
:default-active="$route.path"
|
||||
:collapse="isCollapse"
|
||||
:unique-opened="true"
|
||||
router
|
||||
class="sidebar-menu"
|
||||
:default-active="$route.path"
|
||||
:collapse="isCollapse"
|
||||
:unique-opened="true"
|
||||
router
|
||||
class="sidebar-menu"
|
||||
>
|
||||
<el-menu-item index="/dashboard">
|
||||
<el-icon><Odometer /></el-icon>
|
||||
<el-icon>
|
||||
<Odometer/>
|
||||
</el-icon>
|
||||
<template #title>仪表盘</template>
|
||||
</el-menu-item>
|
||||
|
||||
|
||||
<el-menu-item v-if="userStore.isAdmin" index="/users">
|
||||
<el-icon><User /></el-icon>
|
||||
<el-icon>
|
||||
<User/>
|
||||
</el-icon>
|
||||
<template #title>用户管理</template>
|
||||
</el-menu-item>
|
||||
|
||||
|
||||
<el-menu-item v-if="userStore.isAdmin" index="/user-audit">
|
||||
<el-icon><DocumentChecked /></el-icon>
|
||||
<el-icon>
|
||||
<DocumentChecked/>
|
||||
</el-icon>
|
||||
<template #title>用户审核</template>
|
||||
</el-menu-item>
|
||||
|
||||
|
||||
<el-menu-item v-if="userStore.isAdmin" index="/points">
|
||||
<el-icon><Coin /></el-icon>
|
||||
<el-icon>
|
||||
<Coin/>
|
||||
</el-icon>
|
||||
<template #title>积分管理</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/program">
|
||||
<el-icon><ProgramSvg /></el-icon>
|
||||
<el-icon>
|
||||
<ProgramSvg/>
|
||||
</el-icon>
|
||||
<template #title>项目管理</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/financial">
|
||||
<el-icon>
|
||||
<FinancialSvg/>
|
||||
</el-icon>
|
||||
<template #title>金融</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/chat">
|
||||
<el-icon><ChatSvg /></el-icon>
|
||||
<el-icon>
|
||||
<ChatSvg/>
|
||||
</el-icon>
|
||||
<template #title>聊天室</template>
|
||||
</el-menu-item>
|
||||
|
||||
|
||||
<el-menu-item v-if="userStore.isAdmin" index="/transfers">
|
||||
<el-icon><Money /></el-icon>
|
||||
<el-icon>
|
||||
<Money/>
|
||||
</el-icon>
|
||||
<template #title>转账管理</template>
|
||||
</el-menu-item>
|
||||
|
||||
|
||||
<el-menu-item v-if="userStore.isAdmin" index="/beans">
|
||||
<el-icon><Promotion /></el-icon>
|
||||
<el-icon>
|
||||
<Promotion/>
|
||||
</el-icon>
|
||||
<template #title>融豆管理</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item v-if="userStore.isAdmin" index="/projects">
|
||||
<el-icon><Setting /></el-icon>
|
||||
<el-icon>
|
||||
<Setting/>
|
||||
</el-icon>
|
||||
<template #title>项目管理</template>
|
||||
</el-menu-item>
|
||||
|
||||
|
||||
<el-menu-item index="/profile">
|
||||
<el-icon><UserFilled /></el-icon>
|
||||
<el-icon>
|
||||
<UserFilled/>
|
||||
</el-icon>
|
||||
<template #title>个人资料</template>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<el-container>
|
||||
<!-- 顶部导航 -->
|
||||
<el-header class="header">
|
||||
<div class="header-left">
|
||||
<el-button
|
||||
type="text"
|
||||
@click="toggleCollapse"
|
||||
class="collapse-btn"
|
||||
type="text"
|
||||
@click="toggleCollapse"
|
||||
class="collapse-btn"
|
||||
>
|
||||
<el-icon><Expand v-if="isCollapse" /><Fold v-else /></el-icon>
|
||||
<el-icon>
|
||||
<Expand v-if="isCollapse"/>
|
||||
<Fold v-else/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
|
||||
|
||||
<el-breadcrumb separator="/" class="breadcrumb">
|
||||
<el-breadcrumb-item
|
||||
v-for="item in breadcrumbs"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
v-for="item in breadcrumbs"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
>
|
||||
{{ item.title }}
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="header-right">
|
||||
<!-- 全屏按钮 -->
|
||||
<el-tooltip content="全屏" placement="bottom">
|
||||
<el-button type="text" @click="toggleFullscreen" class="header-btn">
|
||||
<el-icon><FullScreen /></el-icon>
|
||||
<el-icon>
|
||||
<FullScreen/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
|
||||
<!-- 刷新按钮 -->
|
||||
<el-tooltip content="刷新" placement="bottom">
|
||||
<el-button type="text" @click="refresh" class="header-btn">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
<el-icon>
|
||||
<Refresh/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
|
||||
<!-- 用户菜单 -->
|
||||
<el-dropdown @command="handleCommand" class="user-dropdown">
|
||||
<div class="user-info">
|
||||
<el-avatar :size="32" :src="userStore.user?.avatar">
|
||||
<el-icon><UserFilled /></el-icon>
|
||||
<el-icon>
|
||||
<UserFilled/>
|
||||
</el-icon>
|
||||
</el-avatar>
|
||||
<span class="username">{{ userStore.user?.username }}</span>
|
||||
<el-icon class="dropdown-icon"><ArrowDown /></el-icon>
|
||||
<el-icon class="dropdown-icon">
|
||||
<ArrowDown/>
|
||||
</el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="profile">
|
||||
<el-icon><UserFilled /></el-icon>
|
||||
<el-icon>
|
||||
<UserFilled/>
|
||||
</el-icon>
|
||||
个人资料
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="changePassword">
|
||||
<el-icon><Lock /></el-icon>
|
||||
<el-icon>
|
||||
<Lock/>
|
||||
</el-icon>
|
||||
修改密码
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided command="logout">
|
||||
<el-icon><SwitchButton /></el-icon>
|
||||
<el-icon>
|
||||
<SwitchButton/>
|
||||
</el-icon>
|
||||
退出登录
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
@@ -135,51 +181,51 @@
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</el-header>
|
||||
|
||||
|
||||
<!-- 主内容 -->
|
||||
<el-main class="main-content">
|
||||
<transition name="fade" mode="out-in">
|
||||
<router-view />
|
||||
<router-view/>
|
||||
</transition>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
|
||||
|
||||
<!-- 修改密码对话框 -->
|
||||
<el-dialog
|
||||
v-model="passwordDialogVisible"
|
||||
title="修改密码"
|
||||
width="400px"
|
||||
:before-close="handlePasswordDialogClose"
|
||||
v-model="passwordDialogVisible"
|
||||
title="修改密码"
|
||||
width="400px"
|
||||
:before-close="handlePasswordDialogClose"
|
||||
>
|
||||
<el-form
|
||||
ref="passwordFormRef"
|
||||
:model="passwordForm"
|
||||
:rules="passwordRules"
|
||||
label-width="80px"
|
||||
ref="passwordFormRef"
|
||||
:model="passwordForm"
|
||||
:rules="passwordRules"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-form-item label="当前密码" prop="currentPassword">
|
||||
<el-input
|
||||
v-model="passwordForm.currentPassword"
|
||||
type="password"
|
||||
placeholder="请输入当前密码"
|
||||
show-password
|
||||
v-model="passwordForm.currentPassword"
|
||||
type="password"
|
||||
placeholder="请输入当前密码"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" prop="newPassword">
|
||||
<el-input
|
||||
v-model="passwordForm.newPassword"
|
||||
type="password"
|
||||
placeholder="请输入新密码"
|
||||
show-password
|
||||
v-model="passwordForm.newPassword"
|
||||
type="password"
|
||||
placeholder="请输入新密码"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="confirmPassword">
|
||||
<el-input
|
||||
v-model="passwordForm.confirmPassword"
|
||||
type="password"
|
||||
placeholder="请确认新密码"
|
||||
show-password
|
||||
v-model="passwordForm.confirmPassword"
|
||||
type="password"
|
||||
placeholder="请确认新密码"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -193,12 +239,13 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import {ref, computed, onMounted} from 'vue'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import {useUserStore} from '@/stores/user'
|
||||
import {ElMessage, ElMessageBox} from 'element-plus'
|
||||
import ProgramSvg from '@/assets/svg/program.svg'
|
||||
import ChatSvg from '@/assets/svg/chat.svg'
|
||||
import FinancialSvg from '@/assets/svg/financial.svg'
|
||||
import {
|
||||
Odometer,
|
||||
User,
|
||||
@@ -225,6 +272,7 @@ import {
|
||||
CreditCard,
|
||||
Bell,
|
||||
} from '@element-plus/icons-vue'
|
||||
import Financial from "@/views/Financial.vue";
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -252,11 +300,11 @@ const passwordForm = ref({
|
||||
// 密码验证规则
|
||||
const passwordRules = {
|
||||
currentPassword: [
|
||||
{ required: true, message: '请输入当前密码', trigger: 'blur' }
|
||||
{required: true, message: '请输入当前密码', trigger: 'blur'}
|
||||
],
|
||||
newPassword: [
|
||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||
{ min: 6, message: '密码长度不能少于6位', trigger: 'blur' },
|
||||
{required: true, message: '请输入新密码', trigger: 'blur'},
|
||||
{min: 6, message: '密码长度不能少于6位', trigger: 'blur'},
|
||||
{
|
||||
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d@$!%*?&]{6,}$/,
|
||||
message: '密码必须包含大小写字母和数字',
|
||||
@@ -264,7 +312,7 @@ const passwordRules = {
|
||||
}
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: '请确认新密码', trigger: 'blur' },
|
||||
{required: true, message: '请确认新密码', trigger: 'blur'},
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (value !== passwordForm.value.newPassword) {
|
||||
@@ -282,7 +330,7 @@ const passwordRules = {
|
||||
const breadcrumbs = computed(() => {
|
||||
const matched = route.matched.filter(item => item.meta && item.meta.title)
|
||||
const breadcrumbList = []
|
||||
|
||||
|
||||
matched.forEach(item => {
|
||||
if (item.path !== '/') {
|
||||
breadcrumbList.push({
|
||||
@@ -291,7 +339,7 @@ const breadcrumbs = computed(() => {
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
return breadcrumbList
|
||||
})
|
||||
|
||||
@@ -337,7 +385,7 @@ const handleLogout = async () => {
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
|
||||
|
||||
await userStore.logout()
|
||||
router.push('/login')
|
||||
} catch (error) {
|
||||
@@ -349,16 +397,16 @@ const handleLogout = async () => {
|
||||
const handleChangePassword = async () => {
|
||||
try {
|
||||
await passwordFormRef.value.validate()
|
||||
|
||||
|
||||
const result = await userStore.changePassword({
|
||||
currentPassword: passwordForm.value.currentPassword,
|
||||
newPassword: passwordForm.value.newPassword
|
||||
})
|
||||
|
||||
|
||||
if (result.success) {
|
||||
passwordDialogVisible.value = false
|
||||
resetPasswordForm()
|
||||
|
||||
|
||||
// 延迟跳转到登录页
|
||||
setTimeout(() => {
|
||||
router.push('/login')
|
||||
@@ -557,11 +605,11 @@ const resetPasswordForm = () => {
|
||||
z-index: 1000;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
|
||||
.breadcrumb {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.username {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -116,6 +116,15 @@ const routes = [
|
||||
title: '聊天 - 炬融圈',
|
||||
icon: 'ChatDotRound'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'financial',
|
||||
name: 'Financial',
|
||||
component: () => import('@/views/Financial.vue'),
|
||||
meta: {
|
||||
title: '金融 - 炬融圈',
|
||||
icon: 'financial'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -139,6 +139,7 @@ export const midRequest = createRequest(import.meta.env.VITE_UPLOAD_BASE_URL ||
|
||||
export const programRequest = createRequest(import.meta.env.VITE_UPLOAD_BASE_URL || '/program')
|
||||
export const groupRequest = createRequest(import.meta.env.VITE_UPLOAD_BASE_URL || '/group')
|
||||
export const messageRequest = createRequest(import.meta.env.VITE_UPLOAD_BASE_URL || '/message')
|
||||
export const financialRequest = createRequest(import.meta.env.VITE_UPLOAD_BASE_URL || '/financial')
|
||||
|
||||
let loadingInstance = null
|
||||
let requestCount = 0
|
||||
@@ -175,7 +176,7 @@ const api = {
|
||||
login: (data) => midRequest.post('/auth/login', data),
|
||||
getCap: () => midRequest.get('/captcha/generate'),
|
||||
register: (data) => midRequest.post('/auth/register', data),
|
||||
getCurrentUser: () => apiRequest.get('/auth/me'),
|
||||
getCurrentUser: () => midRequest.get('/auth/me'),
|
||||
changePassword: (data) => midRequest.put('/auth/change-password', data)
|
||||
},
|
||||
|
||||
|
||||
579
src/views/Financial.vue
Normal file
579
src/views/Financial.vue
Normal file
@@ -0,0 +1,579 @@
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<el-card class="page-header">
|
||||
<h2>金融管理</h2>
|
||||
<p>管理金融的信息</p>
|
||||
</el-card>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<!-- <el-card class="stats-card">-->
|
||||
<!-- <el-row :gutter="24" class="stats-row">-->
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <el-card class="stat-card">-->
|
||||
<!-- <div class="stat-content">-->
|
||||
<!-- <div class="stat-number">{{ stats.totalTransfers }}</div>-->
|
||||
<!-- <div class="stat-label">总转账数</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <el-icon class="stat-icon">-->
|
||||
<!-- <Money/>-->
|
||||
<!-- </el-icon>-->
|
||||
<!-- </el-card>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <el-card class="stat-card">-->
|
||||
<!-- <div class="stat-content">-->
|
||||
<!-- <div class="stat-number">{{ stats.confirmedTransfers }}</div>-->
|
||||
<!-- <div class="stat-label">已确认</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <el-icon class="stat-icon">-->
|
||||
<!-- <Check/>-->
|
||||
<!-- </el-icon>-->
|
||||
<!-- </el-card>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <el-card class="stat-card">-->
|
||||
<!-- <div class="stat-content">-->
|
||||
<!-- <div class="stat-number">¥{{ stats.totalAmount }}</div>-->
|
||||
<!-- <div class="stat-label">总欠额</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <el-icon class="stat-icon">-->
|
||||
<!-- <Wallet/>-->
|
||||
<!-- </el-icon>-->
|
||||
<!-- </el-card>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
<!-- </el-card>-->
|
||||
|
||||
<!-- 筛选和操作 -->
|
||||
<el-card class="filter-card">
|
||||
<el-row :gutter="20">
|
||||
<!-- <el-col :span="4">-->
|
||||
<!-- <el-select v-model="queryParams.status" placeholder="选择状态" clearable>-->
|
||||
<!-- <el-option label="全部" value=""/>-->
|
||||
<!-- <el-option label="待确认" value="pending"/>-->
|
||||
<!-- <el-option label="已确认" value="confirmed"/>-->
|
||||
<!-- <el-option label="已拒绝" value="rejected"/>-->
|
||||
<!-- <el-option label="已取消" value="cancelled"/>-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </el-col>-->
|
||||
|
||||
<el-col :span="4">
|
||||
<el-input v-model="queryParams.keyword" placeholder="搜索产品代码、名称或公司" clearable/>
|
||||
</el-col>
|
||||
|
||||
<!-- <el-col :span="4">-->
|
||||
<!-- <el-date-picker-->
|
||||
<!-- v-model="dateRange"-->
|
||||
<!-- type="daterange"-->
|
||||
<!-- range-separator="至"-->
|
||||
<!-- start-placeholder="开始日期"-->
|
||||
<!-- end-placeholder="结束日期"-->
|
||||
<!-- format="YYYY-MM-DD"-->
|
||||
<!-- value-format="YYYY-MM-DD"-->
|
||||
<!-- />-->
|
||||
<!-- </el-col>-->
|
||||
|
||||
<el-row style="margin-left: 50px;" :gutter="10">
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" @click="getList">搜索</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button @click="resetFilters">重置</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-row>
|
||||
|
||||
</el-card>
|
||||
|
||||
<!-- 数据列表 -->
|
||||
<el-card class="table-card">
|
||||
<el-row :gutter="10" class="mb8 mb-2">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
>新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
>修改
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="single"
|
||||
@click="handleDelete"
|
||||
>删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-table
|
||||
:data="dataList"
|
||||
v-loading="loading"
|
||||
@selection-change="handleSelectionChange"
|
||||
stripe
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center"/>
|
||||
<!-- <el-table-column prop="id" label="ID"/>-->
|
||||
<el-table-column prop="productCode" label="产品代码"/>
|
||||
<el-table-column prop="productName" label="产品名称"/>
|
||||
<el-table-column prop="productType" label="产品类型"/>
|
||||
<el-table-column prop="company" label="公司"/>
|
||||
<el-table-column label="产品负责人">
|
||||
<template #default="scope">
|
||||
<el-tag>{{ scope.row.user.username }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="riskLevel" label="风险等级">
|
||||
<template #default="scope">
|
||||
<el-tag type="danger">{{ scope.row.riskLevel }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="expectedReturnRate" label="预期收益率">
|
||||
<template #default="scope">
|
||||
<el-tag type="success">{{ scope.row.expectedReturnRate }}%</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createDate" label="创建时间"/>
|
||||
<el-table-column prop="introduction" label="产品描述">
|
||||
<template #default="scope">
|
||||
<el-button text type="primary" @click="handleDetail(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="300">
|
||||
<template #default="scope">
|
||||
<!-- <el-button link type="primary" icon="Document" @click="handleDetail(scope.row)">详情</el-button>-->
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.page"
|
||||
v-model:page-size="pagination.size"
|
||||
:page-sizes="[10,50,100]"
|
||||
:total="pagination.total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="getList"
|
||||
@current-change="getList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 表单 -->
|
||||
<el-dialog :title="title" v-model="open" width="60%" append-to-body top="1vh" @close="reset">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="产品代码" prop="productCode">
|
||||
<el-input v-model="form.productCode" placeholder="请输入产品代码"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品名称" prop="productName">
|
||||
<el-input v-model="form.productName" placeholder="请输入产品名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品类型" prop="productType">
|
||||
<el-select v-model="form.productType" placeholder="请选择产品类型">
|
||||
<el-option label="存款" value="存款"/>
|
||||
<el-option label="基金" value="基金"/>
|
||||
<el-option label="保险" value="保险"/>
|
||||
<el-option label="理财" value="理财"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公司名称" prop="company">
|
||||
<el-input v-model="form.company" placeholder="请输入公司名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="风险等级" prop="riskLevel">
|
||||
<el-select v-model="form.riskLevel" placeholder="请选择风险等级">
|
||||
<el-option label="R1" value="R1"/>
|
||||
<el-option label="R2" value="R2"/>
|
||||
<el-option label="R3" value="R3"/>
|
||||
<el-option label="R4" value="R4"/>
|
||||
<el-option label="R5" value="R5"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="预期收益" prop="expectedReturnRate">
|
||||
<el-input v-model="form.expectedReturnRate" placeholder="请输入预期收益"/>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="开始时间" prop="startDate">-->
|
||||
<!-- <el-input v-model="form.startDate" placeholder="请选择开始时间"/>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="结束时间" prop="endDate">-->
|
||||
<!-- <el-input v-model="form.endDate" placeholder="请选择结束时间"/>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="产品描述" prop="introduction" class="editor">
|
||||
<RichTextEditor v-model="form.introduction" class="w-100"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 详情 -->
|
||||
<el-dialog :title="detailTitle" v-model="openDetail" width="50%" class="h-75 overflow-hidden overflow-scroll">
|
||||
<div class="p-5 border editor-content-view">
|
||||
<div v-html="detailContent"></div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {getCurrentInstance, reactive, ref, shallowRef, toRefs} from "vue";
|
||||
import {Check, Money, Wallet} from "@element-plus/icons-vue";
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import {financialAPI} from "@/api/financial";
|
||||
import {useUserStore} from "@/stores/user";
|
||||
|
||||
import '@wangeditor/editor/dist/css/style.css' // 引入 css
|
||||
import '@/assets/wangeditor.css'
|
||||
import RichTextEditor from "@/components/RichTextEditor.vue";
|
||||
|
||||
const {proxy} = getCurrentInstance();
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 统计
|
||||
const stats = ref({
|
||||
totalTransfers: 10,
|
||||
confirmedTransfers: 10,
|
||||
totalAmount: 10
|
||||
})
|
||||
const dateRange = ref([])
|
||||
// 列表
|
||||
const dataList = ref([])
|
||||
const loading = ref(false)
|
||||
const single = ref(true);
|
||||
// 分页
|
||||
const pagination = ref({
|
||||
page: 1,
|
||||
size: 10,
|
||||
total: 0
|
||||
});
|
||||
// 表单
|
||||
const open = ref(false)
|
||||
const title = ref("");
|
||||
const data = reactive({
|
||||
form: {},
|
||||
queryParams: {
|
||||
page: 1,
|
||||
size: 10,
|
||||
name: undefined,
|
||||
status: undefined,
|
||||
keyword: undefined
|
||||
},
|
||||
rules: {
|
||||
productCode: [{required: true, message: "产品代码不能为空", trigger: "blur"}],
|
||||
productName: [{required: true, message: "产品名称不能为空", trigger: "blur"}],
|
||||
productType: [{required: true, message: "产品类型不能为空", trigger: "blur"}],
|
||||
company: [{required: true, message: "产品企业不能为空", trigger: "blur"}],
|
||||
riskLevel: [{required: true, message: "风险等级不能为空", trigger: "blur"}],
|
||||
expectedReturnRate: [{required: true, message: "预期收益率不能为空", trigger: "blur"}],
|
||||
introduction: [{required: true, message: "产品描述不能为空", trigger: "blur"}],
|
||||
},
|
||||
});
|
||||
const {queryParams, form, rules} = toRefs(data);
|
||||
|
||||
// TODO
|
||||
const getStats = () => {
|
||||
// financialAPI.getStats().then(res => {
|
||||
// stats.value = res.data.data.stats
|
||||
// })
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
// api请求
|
||||
queryParams.value.page = pagination.value.page;
|
||||
queryParams.value.size = pagination.value.size;
|
||||
financialAPI.list(queryParams.value).then(res => {
|
||||
// console.log(res)
|
||||
dataList.value = res.data.data.list
|
||||
pagination.value.total = res.data.data.total
|
||||
loading.value = false;
|
||||
})
|
||||
}
|
||||
|
||||
// 重置
|
||||
const resetFilters = () => {
|
||||
queryParams.value = {
|
||||
page: 1,
|
||||
keyword: undefined
|
||||
}
|
||||
dateRange.value = []
|
||||
getList()
|
||||
}
|
||||
|
||||
const handleSelectionChange = (selection) => {
|
||||
single.value = selection.length != 1;
|
||||
}
|
||||
|
||||
const handleAdd = () => {
|
||||
title.value = "新增产品"
|
||||
open.value = true;
|
||||
form.value = {}
|
||||
}
|
||||
|
||||
const handleUpdate = (row) => {
|
||||
financialAPI.getOne(row.id).then(res => {
|
||||
form.value = res.data.data
|
||||
title.value = "修改产品"
|
||||
open.value = true;
|
||||
})
|
||||
}
|
||||
|
||||
const handleDelete = (row) => {
|
||||
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
financialAPI.delete(row.id).then(res => {
|
||||
ElMessage.success('删除成功')
|
||||
getList()
|
||||
})
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消删除')
|
||||
})
|
||||
}
|
||||
|
||||
const openDetail = ref(false)
|
||||
const detailTitle = ref("")
|
||||
const detailContent = ref("")
|
||||
|
||||
// 查看详情
|
||||
const handleDetail = (row) => {
|
||||
detailTitle.value = row.name
|
||||
detailContent.value = row.introduction
|
||||
openDetail.value = true
|
||||
}
|
||||
|
||||
const submitForm = () => {
|
||||
proxy.$refs["formRef"].validate(valid => {
|
||||
if (valid) {
|
||||
form.value.managerId = userStore.user.id
|
||||
if (form.value.id != undefined) {
|
||||
delete form.value.user;
|
||||
financialAPI.update(form.value).then(res => {
|
||||
ElMessage.success('修改成功')
|
||||
open.value = false
|
||||
getList()
|
||||
})
|
||||
} else {
|
||||
financialAPI.add(form.value).then(res => {
|
||||
ElMessage.success('添加成功')
|
||||
open.value = false
|
||||
getList()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
const formRef = ref(null)
|
||||
const reset = () => {
|
||||
form.value = {
|
||||
managerId: undefined,
|
||||
startDate: undefined,
|
||||
endDate: undefined,
|
||||
name: undefined,
|
||||
company: undefined,
|
||||
address: undefined,
|
||||
introduction: undefined,
|
||||
paymentMethod: undefined
|
||||
}
|
||||
proxy.$refs["formRef"].resetFields()
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
open.value = false
|
||||
reset()
|
||||
}
|
||||
|
||||
// 获取统计
|
||||
getStats()
|
||||
// 获取列表
|
||||
getList();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.page-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-header h2 {
|
||||
margin: 0 0 8px 0;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
margin: 0;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.stats-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stats-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #409eff;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 40px;
|
||||
color: #e4e7ed;
|
||||
}
|
||||
|
||||
.stat-card.overdue {
|
||||
border-left: 4px solid #e6a23c;
|
||||
}
|
||||
|
||||
.stat-card.overdue .stat-icon {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.stat-card.overdue .stat-number {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.table-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-weight: bold;
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.amount-red {
|
||||
font-weight: bold;
|
||||
color: #ff4949;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.proof-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.proof-image {
|
||||
width: 60%;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.proof-section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.proof-section h4 {
|
||||
margin-bottom: 10px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.detail-container {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.detail-buttom {
|
||||
color: #409eff;
|
||||
cursor: pointer;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.detail-buttom:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.pay-detail-text {
|
||||
white-space: pre-line;
|
||||
word-break: break-word;
|
||||
padding: 10px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid #409eff;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.user-balance-info {
|
||||
margin-top: 5px;
|
||||
padding: 5px 10px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid #409eff;
|
||||
}
|
||||
|
||||
.real-name {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
//::v-deep .editor{
|
||||
// .el-form-item__content{
|
||||
// border: 1px solid #000;
|
||||
// }
|
||||
//}
|
||||
</style>
|
||||
@@ -170,7 +170,7 @@
|
||||
</el-card>
|
||||
|
||||
<!-- 表单 -->
|
||||
<el-dialog :title="title" v-model="open" width="60%" append-to-body top="1vh">
|
||||
<el-dialog :title="title" v-model="open" width="60%" append-to-body top="1vh" @close="reset">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="项目名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入项目名称"/>
|
||||
@@ -388,7 +388,7 @@ const reset = () => {
|
||||
introduction: undefined,
|
||||
paymentMethod: undefined
|
||||
}
|
||||
formRef.value.resetForm()
|
||||
proxy.$refs["formRef"].resetFields()
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
|
||||
@@ -47,6 +47,11 @@ export default defineConfig({
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/message/, '')
|
||||
},
|
||||
'/financial':{
|
||||
target: 'http://192.168.0.15:3008',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/financial/, '')
|
||||
},
|
||||
'/uploads': {
|
||||
target: 'https://test.zrbjr.com',
|
||||
changeOrigin: true
|
||||
|
||||
Reference in New Issue
Block a user