Compare commits

...

4 Commits

Author SHA1 Message Date
014479b8d1 2025-10-14
发送图片
2025-10-14 13:25:54 +08:00
3995dd8cdd 2025-10-14
注释统计
2025-10-14 11:21:25 +08:00
0e9ea78296 2025-10-14
项目简介富文本图片/视频上传
2025-10-14 11:19:49 +08:00
3dd2502e31 2025-10-14
聊天添加刷新
2025-10-14 09:13:04 +08:00
4 changed files with 250 additions and 99 deletions

52
src/assets/wangeditor.css Normal file
View File

@@ -0,0 +1,52 @@
.editor-content-view {
border: 3px solid #ccc;
border-radius: 5px;
padding: 0 10px;
margin-top: 20px;
overflow-x: auto;
}
.editor-content-view p,
.editor-content-view li {
white-space: pre-wrap; /* 保留空格 */
}
.editor-content-view blockquote {
border-left: 8px solid #d0e5f2;
padding: 10px 10px;
margin: 10px 0;
background-color: #f1f1f1;
}
.editor-content-view code {
font-family: monospace;
background-color: #eee;
padding: 3px;
border-radius: 3px;
}
.editor-content-view pre>code {
display: block;
padding: 10px;
}
.editor-content-view table {
border-collapse: collapse;
}
.editor-content-view td,
.editor-content-view th {
border: 1px solid #ccc;
min-width: 50px;
height: 20px;
}
.editor-content-view th {
background-color: #f1f1f1;
}
.editor-content-view ul,
.editor-content-view ol {
padding-left: 20px;
}
.editor-content-view input[type="checkbox"] {
margin-right: 5px;
}

View File

@@ -52,7 +52,7 @@ const mode = 'default' // 或 'simple'
// 工具栏配置
const toolbarConfig = {
excludeKeys: [
'group-video', // 排除视频
// 'group-video', // 排除视频
'fullScreen' // 排除全屏
]
}
@@ -63,26 +63,47 @@ const editorConfig = {
MENU_CONF: {
// 配置上传图片
uploadImage: {
server: '/api/upload/image',
server: '/mid/upload/image',
fieldName: 'file',
meta: {
token: localStorage.getItem('token') || ''
},
metaWithUrl: false,
headers: {
'Authorization': `Bearer ${localStorage.getItem('token') || ''}`
'Authorization': `Bearer ${localStorage.getItem('admin_token') || ''}`
},
maxFileSize: 5 * 1024 * 1024, // 5M
allowedFileTypes: ['image/*'],
customInsert(res, insertFn) {
// 自定义插入图片
if (res.code === 200) {
if (res.success) {
insertFn(res.data.url, res.data.alt || '', res.data.url)
} else {
console.error('图片上传失败:', res.message)
}
}
},
uploadVideo: {
server: '/mid/upload',
fieldName: 'file',
meta: {
token: localStorage.getItem('token') || ''
},
metaWithUrl: false,
headers: {
'Authorization': `Bearer ${localStorage.getItem('admin_token') || ''}`
},
maxFileSize: 20 * 1024 * 1024,
allowedFileTypes: ['video/*'],
customInsert(res, insertFn) {
// 自定义插入视频
if (res.success) {
insertFn(res.data.url, res.data.alt || '', res.data.url)
} else {
console.error('视频上传失败:', res.message)
}
}
},
// 配置字体
fontSize: {
fontSizeList: [

View File

@@ -6,43 +6,43 @@
</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="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">
@@ -87,7 +87,7 @@
<!-- 数据列表 -->
<el-card class="table-card">
<el-row :gutter="10" class="mb8">
<el-row :gutter="10" class="mb8 mb-2">
<!-- <el-col :span="1.5">-->
<!-- <el-button-->
<!-- type="primary"-->
@@ -107,16 +107,25 @@
<!-- >修改-->
<!-- </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-col :span="1.5">-->
<!-- <el-button-->
<!-- type="danger"-->
<!-- plain-->
<!-- icon="Delete"-->
<!-- :disabled="single"-->
<!-- @click="handleDelete"-->
<!-- >删除-->
<!-- </el-button>-->
<!-- </el-col>-->
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Refresh"
@click="handleRefresh"
>刷新
</el-button>
</el-col>
</el-row>
<el-table
@@ -159,7 +168,7 @@
<!-- <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="inChatSvg" @click="handleChat(scope.row)">进入聊天</el-button>
<!-- <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>-->
<!-- <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>-->
</template>
</el-table-column>
</el-table>
@@ -270,7 +279,23 @@
:rows="3"
placeholder="请输入消息..."
></el-input>
<el-row :gutter="24">
<el-col :span="12">
<el-upload
action="/mid/upload/image"
:headers="headers"
:on-success="uploadSuccess"
:on-error="uploadError"
:show-file-list="false"
accept="image/png, image/jpeg"
:limit="1">
<el-button type="primary">发送图片</el-button>
</el-upload>
</el-col>
<el-col :span="12">
<el-button type="primary" @click="handleSendMsg">发送</el-button>
</el-col>
</el-row>
</div>
</div>
</el-dialog>
@@ -293,6 +318,9 @@ import {io} from 'socket.io-client';
const {proxy} = getCurrentInstance();
const userStore = useUserStore()
const headers = {
Authorization: `Bearer ${userStore.token}`
}
// 统计
const stats = ref({
@@ -548,6 +576,20 @@ const handleSendMsg = () => {
inputText.value = ''
}
const uploadSuccess = (res) => {
socket.value.emit('clientMsg', {
groupId: params.value.groupId,
createId: userStore.user.id,
content: res.data.url,
type: 'img'
})
}
const uploadError = (res) => {
const result = JSON.parse(res.message)
ElMessage.error(result.message)
}
// 头像字
const getRoleInitial = (createId) => {
if (messageUserId.value === createId) {
@@ -561,6 +603,10 @@ const getRoleInitial = (createId) => {
}
};
const handleRefresh = () => {
getList()
}
onMounted(() => {
// 获取统计
getStats()

View File

@@ -6,43 +6,43 @@
</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="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">
@@ -87,7 +87,7 @@
<!-- 数据列表 -->
<el-card class="table-card">
<el-row :gutter="10" class="mb8">
<el-row :gutter="10" class="mb8 mb-2">
<el-col :span="1.5">
<el-button
type="primary"
@@ -140,7 +140,11 @@
<el-table-column prop="startDate" label="开始时间"/>
<el-table-column prop="endDate" label="结束时间"/>
<el-table-column prop="createDate" label="创建时间"/>
<el-table-column prop="introduction" 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">
@@ -166,7 +170,7 @@
</el-card>
<!-- 表单 -->
<el-dialog :title="title" v-model="open" width="800px" append-to-body>
<el-dialog :title="title" v-model="open" width="60%" append-to-body top="1vh">
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="项目名称" prop="name">
<el-input v-model="form.name" placeholder="请输入项目名称"/>
@@ -196,7 +200,7 @@
<!-- <el-input v-model="form.endDate" placeholder="请选择结束时间"/>-->
<!-- </el-form-item>-->
<el-form-item label="项目描述" prop="introduction" class="editor">
<RichTextEditor height="300px" />
<RichTextEditor v-model="form.introduction" class="w-100"/>
</el-form-item>
</el-form>
<template #footer>
@@ -207,10 +211,17 @@
</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 lang="ts">
<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";
@@ -218,8 +229,7 @@ import {programAPI} from "@/api/program";
import {useUserStore} from "@/stores/user";
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import {IToolbarConfig} from "@wangeditor/editor";
import '@/assets/wangeditor.css'
import RichTextEditor from "@/components/RichTextEditor.vue";
const {proxy} = getCurrentInstance();
@@ -330,9 +340,15 @@ const handleDelete = (row) => {
})
}
const openDetail = ref(false)
const detailTitle = ref("")
const detailContent = ref("")
// 查看详情
const handleDetail = (row) => {
ElMessage.info('查看详情')
detailTitle.value = row.name
detailContent.value = row.introduction
openDetail.value = true
}
const submitForm = () => {
@@ -359,9 +375,25 @@ const submitForm = () => {
}
})
}
const formRef = ref(null)
const reset = () => {
form.value = {
linkmanId: undefined,
startDate: undefined,
endDate: undefined,
dataRange: [],
name: undefined,
company: undefined,
address: undefined,
introduction: undefined,
paymentMethod: undefined
}
formRef.value.resetForm()
}
const cancel = () => {
open.value = false
reset()
}
// 获取统计