项目初始化。

This commit is contained in:
tianyongbao
2026-02-04 17:14:16 +08:00
parent f6a89c632d
commit 016996b5d5
715 changed files with 142166 additions and 71 deletions

View File

@@ -0,0 +1,587 @@
<template>
<view class="container" style="padding-bottom:1rpx;">
<u-navbar
leftIconSize="40rpx"
leftIconColor="#333333"
:title="title"
>
</u-navbar>
<view class="section">
<view class="section-title">公告信息</view>
<view class="form-view">
<u--form labelPosition="left" :model="form" ref="uForm">
<u-form-item label="公告标题" prop="noticeTitle" borderBottom required>
<u--input v-model="form.noticeTitle" placeholder="请输入公告标题" border="none"></u--input>
</u-form-item>
<u-form-item label="公告类型" prop="noticeType" borderBottom required>
<u-radio-group v-model="form.noticeType" placement="row">
<u-radio v-for="item in noticeTypeList" :key="item.dictValue" :name="item.dictValue" :label="item.dictLabel">
</u-radio>
</u-radio-group>
</u-form-item>
<u-form-item label="状态" prop="status" borderBottom>
<u-radio-group v-model="form.status" placement="row">
<u-radio v-for="item in statusList" :key="item.dictValue" :name="item.dictValue" :label="item.dictLabel">
</u-radio>
</u-radio-group>
</u-form-item>
<u-form-item label="内容" prop="noticeContent" labelPosition="top">
<view class="editor-container">
<view class="editor-toolbar">
<view class="toolbar-item" @click="format('bold')">
<text class="iconfont">B</text>
</view>
<view class="toolbar-item" @click="format('italic')">
<text class="iconfont italic">I</text>
</view>
<view class="toolbar-item" @click="format('underline')">
<text class="iconfont underline">U</text>
</view>
<view class="toolbar-divider"></view>
<view class="toolbar-item" @click="insertImage">
<uni-icons type="image" size="16" color="#333333"></uni-icons>
</view>
<view class="toolbar-divider"></view>
<view class="toolbar-item" @click="undo">
<text class="iconfont"></text>
</view>
<view class="toolbar-item" @click="redo">
<text class="iconfont"></text>
</view>
</view>
<editor
id="editor"
class="editor-content"
:placeholder="'请输入公告内容'"
@ready="onEditorReady"
@input="onEditorInput"
@focus="editorFocus"
@blur="editorBlur"
:show-img-size="true"
:show-img-toolbar="true"
:show-img-resize="true"
></editor>
<view class="editor-counter">
<text>{{ contentLength }}/20000</text>
</view>
</view>
</u-form-item>
</u--form>
<view class="form-btn">
<u-button type="primary" text="保存" @click="submitForm"></u-button>
<!-- 调试用的测试按钮 -->
<u-button type="warning" text="测试保存" @click="testSave" style="margin-top: 20rpx;"></u-button>
</view>
</view>
</view>
<u-toast ref="uToast"></u-toast>
</view>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick } from 'vue'
import { getNotice, addNotice, updateNotice } from '@/api/system/notice'
import { getDicts } from "@/api/system/dict/data"
import { onLoad, onReady } from "@dcloudio/uni-app"
import modal from '@/plugins/modal'
const title = ref('新增公告')
const noticeId = ref(undefined)
const noticeTypeList = ref([])
const statusList = ref([])
const editorCtx = ref(null)
const contentLength = ref(0)
const form = reactive({
noticeId: undefined,
noticeTitle: '',
noticeType: '',
noticeContent: '',
status: '0'
})
onLoad((options) => {
console.log('页面加载参数:', options)
if (options.id) {
noticeId.value = options.id
title.value = '修改公告'
console.log('加载公告详情ID:', noticeId.value)
getNotice(noticeId.value).then(response => {
console.log('获取到的公告数据:', response.data)
Object.assign(form, response.data)
console.log('表单数据初始化完成:', form)
// 如果编辑器已经初始化,设置内容
if (editorCtx.value && form.noticeContent) {
console.log('设置编辑器初始内容:', form.noticeContent)
editorCtx.value.setContents({
html: form.noticeContent
})
}
}).catch(error => {
console.error('获取公告详情失败:', error)
})
}
// 获取字典数据
console.log('开始获取字典数据')
getDicts('sys_notice_type').then(res => {
noticeTypeList.value = res.data
console.log('公告类型字典:', res.data)
}).catch(error => {
console.error('获取公告类型字典失败:', error)
})
getDicts('sys_notice_status').then(res => {
statusList.value = res.data
console.log('公告状态字典:', res.data)
}).catch(error => {
console.error('获取公告状态字典失败:', error)
})
})
onReady(() => {
// 页面准备完成后的一些初始化操作
})
// 编辑器初始化完成
function onEditorReady() {
console.log('编辑器初始化完成')
uni.createSelectorQuery().select('#editor').context((res) => {
editorCtx.value = res.context
console.log('编辑器上下文获取成功:', editorCtx.value)
// 如果是编辑模式,设置初始内容
if (form.noticeContent) {
console.log('设置初始内容到编辑器:', form.noticeContent)
editorCtx.value.setContents({
html: form.noticeContent,
success: () => {
console.log('编辑器内容设置成功')
},
fail: (error) => {
console.error('编辑器内容设置失败:', error)
}
})
} else {
console.log('没有初始内容需要设置')
}
}).exec()
}
// 编辑器内容变化
function onEditorInput(e) {
// 获取HTML内容
editorCtx.value.getContents({
success: (res) => {
// 使用 Vue 的响应式更新方式
form.noticeContent = res.html
// 计算内容长度去除HTML标签
const text = res.text || ''
contentLength.value = text.length
console.log('编辑器内容已更新:', res.html)
}
})
}
// 编辑器获得焦点
function editorFocus() {
// 可以添加焦点样式
}
// 编辑器失去焦点
function editorBlur() {
// 保存内容
if (editorCtx.value) {
editorCtx.value.getContents({
success: (res) => {
form.noticeContent = res.html
console.log('失去焦点时保存的内容:', res.html)
},
fail: (error) => {
console.error('失去焦点时获取内容失败:', error)
}
})
}
}
// 格式化文本
function format(name, value) {
if (!editorCtx.value) return
editorCtx.value.format(name, value)
}
// 撤销
function undo() {
if (!editorCtx.value) return
editorCtx.value.undo()
}
// 重做
function redo() {
if (!editorCtx.value) return
editorCtx.value.redo()
}
// 插入图片
function insertImage() {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePath = res.tempFilePaths[0]
// 显示加载提示
uni.showLoading({
title: '上传中...'
})
// 这里需要根据实际的上传接口进行调整
// 示例代码,实际使用时需要替换为真实的上传逻辑
uni.uploadFile({
url: '/common/upload', // 替换为实际的上传地址
filePath: tempFilePath,
name: 'file',
success: (uploadRes) => {
uni.hideLoading()
try {
const data = JSON.parse(uploadRes.data)
if (data.code === 200) {
// 获取图片URL - 需要根据实际返回结构调整
const imageUrl = data.url || data.data || data.fileName
// 插入图片到编辑器
if (editorCtx.value) {
editorCtx.value.insertImage({
src: imageUrl,
width: '100%',
height: 'auto',
success: () => {
uni.showToast({
title: '插入成功',
icon: 'success'
})
},
fail: (err) => {
console.error('插入图片失败', err)
uni.showToast({
title: '插入失败',
icon: 'none'
})
}
})
}
} else {
uni.showToast({
title: data.msg || '上传失败',
icon: 'none'
})
}
} catch (e) {
console.error('解析上传结果失败', e)
uni.showToast({
title: '上传失败',
icon: 'none'
})
}
},
fail: (err) => {
uni.hideLoading()
console.error('上传失败', err)
uni.showToast({
title: '上传失败',
icon: 'none'
})
}
})
},
fail: (err) => {
console.error('选择图片失败', err)
}
})
}
// 提交表单
async function submitForm() {
try {
// 提交前确保获取最新的编辑器内容
if (editorCtx.value) {
const content = await new Promise((resolve, reject) => {
editorCtx.value.getContents({
success: (res) => {
form.noticeContent = res.html
console.log('提交前获取到的内容:', res.html)
resolve(res.html)
},
fail: reject
})
})
// 确保内容已更新到表单
await nextTick()
}
doSubmit()
} catch (error) {
console.error('获取编辑器内容失败:', error)
modal.msgError('获取内容失败,请重试')
}
}
function doSubmit() {
console.log('准备提交的表单数据:', form)
if (!form.noticeTitle) {
modal.msgError('请输入公告标题')
return
}
if (!form.noticeType) {
modal.msgError('请选择公告类型')
return
}
if (!form.noticeContent || form.noticeContent.trim() === '') {
modal.msgError('请输入公告内容')
return
}
const submitForm = { ...form }
console.log('最终提交的数据:', submitForm)
// 检查必要字段
console.log('检查字段:')
console.log('- 标题:', submitForm.noticeTitle)
console.log('- 类型:', submitForm.noticeType)
console.log('- 内容长度:', submitForm.noticeContent.length)
console.log('- 状态:', submitForm.status)
console.log('- ID:', submitForm.noticeId)
if (noticeId.value) {
console.log('执行修改操作')
updateNotice(submitForm).then(response => {
console.log('修改成功响应:', response)
modal.msgSuccess('修改成功')
setTimeout(() => {
uni.navigateBack()
}, 1000)
}).catch(error => {
console.error('修改失败:', error)
console.log('错误详情:', {
message: error.message,
response: error.response,
data: error.response?.data
})
modal.msgError('修改失败: ' + (error.message || '未知错误'))
})
} else {
console.log('执行新增操作')
addNotice(submitForm).then(response => {
console.log('新增成功响应:', response)
modal.msgSuccess('新增成功')
setTimeout(() => {
uni.navigateBack()
}, 1000)
}).catch(error => {
console.error('新增失败:', error)
console.log('错误详情:', {
message: error.message,
response: error.response,
data: error.response?.data
})
modal.msgError('新增失败: ' + (error.message || '未知错误'))
})
}
}
// 测试函数 - 用于验证基本功能
function testSave() {
console.log('=== 开始测试保存功能 ===')
console.log('当前表单状态:', form)
console.log('编辑器上下文:', editorCtx.value)
console.log('内容长度:', form.noticeContent?.length || 0)
// 强制获取编辑器内容进行测试
if (editorCtx.value) {
editorCtx.value.getContents({
success: (res) => {
console.log('测试获取的内容:', res)
console.log('HTML内容:', res.html)
console.log('纯文本内容:', res.text)
console.log('字符数:', res.text?.length || 0)
// 尝试直接保存测试数据
const testData = {
noticeTitle: form.noticeTitle || '测试标题',
noticeType: form.noticeType || '1',
noticeContent: res.html || '<p>测试内容</p>',
status: form.status || '0'
}
console.log('测试数据:', testData)
// 直接调用API测试
addNotice(testData).then(response => {
console.log('测试保存成功:', response)
modal.msgSuccess('测试保存成功!')
}).catch(error => {
console.error('测试保存失败:', error)
modal.msgError('测试保存失败: ' + error.message)
})
},
fail: (error) => {
console.error('测试获取内容失败:', error)
modal.msgError('无法获取编辑器内容')
}
})
} else {
console.log('编辑器未初始化')
modal.msgError('编辑器未初始化')
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: #f5f5f5;
}
.section {
margin: 24rpx;
padding: 0;
background-color: #fff;
border-radius: 16rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
overflow: hidden;
.section-title {
padding: 16rpx 24rpx;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
line-height: 1.2;
font-size: 28rpx;
font-weight: 600;
display: flex;
align-items: center;
&::before {
content: '';
width: 6rpx;
height: 28rpx;
background: #ffffff;
border-radius: 3rpx;
margin-right: 12rpx;
}
}
.form-view {
padding: 24rpx;
.form-btn {
padding-top: 16rpx;
}
}
}
.editor-container {
width: 100%;
border: 2rpx solid #e8edf3;
border-radius: 12rpx;
overflow: hidden;
background: #ffffff;
box-sizing: border-box;
.editor-toolbar {
display: flex;
align-items: center;
padding: 12rpx;
background: #f8f9fa;
border-bottom: 2rpx solid #e8edf3;
flex-wrap: wrap;
gap: 8rpx;
.toolbar-item {
display: flex;
align-items: center;
justify-content: center;
width: 56rpx;
height: 56rpx;
background: #ffffff;
border-radius: 8rpx;
border: 2rpx solid #e8edf3;
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
background: #667eea;
border-color: #667eea;
.iconfont {
color: #ffffff;
}
uni-icons {
color: #ffffff;
}
}
.iconfont {
font-size: 28rpx;
font-weight: 600;
color: #333333;
&.italic {
font-style: italic;
}
&.underline {
text-decoration: underline;
}
}
}
.toolbar-divider {
width: 2rpx;
height: 40rpx;
background: #e8edf3;
margin: 0 4rpx;
}
}
.editor-content {
width: 100%;
min-height: 500rpx;
padding: 20rpx;
font-size: 28rpx;
line-height: 1.6;
background: #ffffff;
box-sizing: border-box;
}
.editor-counter {
padding: 12rpx 20rpx;
background: #f8f9fa;
border-top: 2rpx solid #e8edf3;
text-align: right;
text {
font-size: 24rpx;
color: #999999;
}
}
}
</style>
<style lang="scss">
.form-btn .u-button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
border: none !important;
border-radius: 24rpx !important;
height: 80rpx !important;
box-shadow: 0 4rpx 16rpx rgba(102, 126, 234, 0.4) !important;
}
.form-btn .u-button__text {
font-size: 30rpx !important;
font-weight: 500 !important;
letter-spacing: 2rpx !important;
}
</style>