|
@@ -228,38 +228,80 @@
|
|
|
<div class="report-editor">
|
|
|
<div class="report-title">报告内容:</div>
|
|
|
<div style="border: 1px solid #d9d9d9; margin-top: 10px">
|
|
|
- <Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" mode="default" />
|
|
|
+ <!-- <Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" mode="simple" /> -->
|
|
|
<Editor style="height: 500px; overflow-y: hidden" v-model="valueHtml" :defaultConfig="editorConfig" @onCreated="handleCreated" />
|
|
|
</div>
|
|
|
</div>
|
|
|
<div style="margin-top: 20px">
|
|
|
<div class="report-title">格式选择:</div>
|
|
|
- <el-select style="width: 300px; margin-top: 10px" v-model="fileType" clearable placeholder="请选择">
|
|
|
- <el-option label="pdf" :value="1"></el-option>
|
|
|
- <el-option label="word" :value="2"></el-option>
|
|
|
+ <el-select style="width: 300px; margin-top: 10px" v-model="reportForm.type" clearable placeholder="请选择">
|
|
|
+ <el-option label="pdf" value="pdf"></el-option>
|
|
|
+ <el-option label="word" value="docx"></el-option>
|
|
|
</el-select>
|
|
|
</div>
|
|
|
- <div>
|
|
|
- <el-button color="#2AB55C" style="color: #fff; margin-top: 20px; margin-bottom: 30px" @click="htmlToPdfFn">导出报告</el-button>
|
|
|
+ <div style="margin-top: 20px">
|
|
|
+ <el-button color="#2AB55C" style="color: #fff; margin-bottom: 20px" @click="dialogVisible = true">导出报告</el-button>
|
|
|
</div>
|
|
|
+ <el-dialog v-model="dialogVisible" title="导出信息" width="500">
|
|
|
+ <el-form ref="reportFormRef" :model="reportForm" label-width="90px" label-suffix=":">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="期数" prop="seq" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]">
|
|
|
+ <el-input v-model="reportForm.seq" placeholder="请输入" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="发送范围" prop="fw" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]">
|
|
|
+ <el-input v-model="reportForm.fw" placeholder="请输入" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="备案号" prop="ba" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]">
|
|
|
+ <el-input v-model="reportForm.ba" placeholder="请输入" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="值班员" prop="zby" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]">
|
|
|
+ <el-input v-model="reportForm.zby" placeholder="请输入" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="审核" prop="sh" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]">
|
|
|
+ <el-input v-model="reportForm.sh" placeholder="请输入" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="签发" prop="qf" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]">
|
|
|
+ <el-input v-model="reportForm.qf" placeholder="请输入" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button type="primary" :loading="downLoading" @click="htmlToPdfFn">确 认</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
</template>
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
-<script lang="ts" setup>
|
|
|
+<script setup lang="ts">
|
|
|
import { Search } from '@element-plus/icons-vue';
|
|
|
import BaseChart from '@/components/BaseChart/index.vue';
|
|
|
import monitorIcon from '@/assets/images/monitor.svg';
|
|
|
import DPlayer from 'dplayer';
|
|
|
import '@wangeditor/editor/dist/css/style.css'; // 引入 css
|
|
|
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
|
|
|
-import exportUtils from '@/utils/exportUtils';
|
|
|
+import { downloadFile } from '@/utils/download.js';
|
|
|
import { listEvent, generateReport, updateEvent } from '@/api/system/event';
|
|
|
import { dateFormat } from '@/utils/index';
|
|
|
import { ElLoading } from 'element-plus';
|
|
|
-const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
+const { proxy } = getCurrentInstance();
|
|
|
const bdmap = ref(null);
|
|
|
+const downLoading = ref(false);
|
|
|
const marks = ref([
|
|
|
{
|
|
|
longitude: 118.84651017058795,
|
|
@@ -283,18 +325,8 @@ const valueHtml = ref('');
|
|
|
const handleCreated = (editor) => {
|
|
|
editorRef.value = editor; // 记录 editor 实例,重要!
|
|
|
};
|
|
|
-const fileType = ref(1);
|
|
|
const showReport = ref(false);
|
|
|
-const htmlToPdfFn = async () => {
|
|
|
- const dom = document.querySelector('.w-e-text-container');
|
|
|
- const type = fileType.value;
|
|
|
- if (type == 1) {
|
|
|
- exportUtils.exportPDF(dom, '报告.pdf');
|
|
|
- } else if (type == 2) {
|
|
|
- exportUtils.exportWord(valueHtml.value, '报告.docx');
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
+const reportFormRef = ref(null);
|
|
|
const queryParams = ref({
|
|
|
pageNum: 1,
|
|
|
pageSize: 15,
|
|
@@ -414,6 +446,24 @@ const pieOptions = computed(() => {
|
|
|
});
|
|
|
const eventList = ref([]);
|
|
|
const total = ref(0);
|
|
|
+const dialogVisible = ref(false);
|
|
|
+const reportForm = reactive({
|
|
|
+ 'year': '',
|
|
|
+ 'seq': 1,
|
|
|
+ 'title': '',
|
|
|
+ 'month': '',
|
|
|
+ 'day': '',
|
|
|
+ 'content': '',
|
|
|
+ 'fw': '市委、市政府总值班室',
|
|
|
+ 'qf': '朱劲',
|
|
|
+ 'sh': '宗仁',
|
|
|
+ 'ps': '',
|
|
|
+ 'ba': 'KB608',
|
|
|
+ 'zby': '83194295',
|
|
|
+ 'tpl': 'sg.docx',
|
|
|
+ 'type': 'pdf'
|
|
|
+});
|
|
|
+const reportSeq = ref(0);
|
|
|
onMounted(() => {
|
|
|
const map = new BMapGL.Map('map'); // 创建地图实例
|
|
|
const point = new BMapGL.Point(118.879999, 32.016216); // 创建点坐标
|
|
@@ -624,8 +674,8 @@ const showDetails = (row) => {
|
|
|
let imgList = [];
|
|
|
let videoList = [];
|
|
|
if (row.ext1 && row.ext1.length) {
|
|
|
- videoList = row.ext1.filter((item) => item.includes('mp4')).map((item) => `http://jtjai.xt.wenhq.top:8083/api/oss/local/upload/${item}`);
|
|
|
- imgList = row.ext1.filter((item) => !item.includes('mp4')).map((item) => `http://jtjai.xt.wenhq.top:8083/api/oss/local/upload/${item}`);
|
|
|
+ videoList = row.ext1.filter((item) => item.includes('mp4')).map((item) => `${import.meta.env.VITE_APP_BASE_HOST}/api/oss/local/upload/${item}`);
|
|
|
+ imgList = row.ext1.filter((item) => !item.includes('mp4')).map((item) => `${import.meta.env.VITE_APP_BASE_HOST}/api/oss/local/upload/${item}`);
|
|
|
}
|
|
|
Object.assign(form.value, JSON.parse(JSON.stringify(row)), { imgList, videoList });
|
|
|
nextTick(() => {
|
|
@@ -643,22 +693,67 @@ const showDetails = (row) => {
|
|
|
const generateClick = () => {
|
|
|
dialog.loading = ElLoading.service({
|
|
|
lock: true,
|
|
|
- text: '正在生成报告,请稍候...',
|
|
|
+ text: '正在生成AI报告...',
|
|
|
fullscreen: false,
|
|
|
target: '.dialog-loading-warp',
|
|
|
background: 'rgba(255, 255, 255, 0.6)'
|
|
|
});
|
|
|
- generateReport(form.value.id).then(({ code, msg }) => {
|
|
|
+ generateReport(form.value.id).then(async ({ code, msg }) => {
|
|
|
dialog.loading.close();
|
|
|
if (code == 200 && msg) {
|
|
|
showReport.value = true;
|
|
|
- valueHtml.value = JSON.parse(msg).data.outputs.report;
|
|
|
+ const { report, time } = JSON.parse(msg).data.outputs;
|
|
|
+ const res = await proxy?.getConfigKey('report_seq');
|
|
|
+ reportSeq.value = Number(res.data);
|
|
|
+ valueHtml.value = report.replace(/```/g, '').replace(/html\n/, '');
|
|
|
+ const [year, month, day] = time.split(' ')[0].split('-');
|
|
|
+ reportForm.year = year;
|
|
|
+ reportForm.month = month;
|
|
|
+ reportForm.day = day;
|
|
|
+ reportForm.seq = reportSeq.value;
|
|
|
proxy?.$modal.msgSuccess('报告生成成功');
|
|
|
} else {
|
|
|
proxy?.$modal.msgError('报告生成失败');
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
+const extractTitleAndContents = (htmlString) => {
|
|
|
+ // 提取标题(从 <h2><strong> 中获取)
|
|
|
+ const titleMatch = htmlString.match(/<h2[^>]*>.*?<strong>(.*?)<\/strong>.*?<\/h2>/is);
|
|
|
+ const title = titleMatch ? titleMatch[1].trim() : '';
|
|
|
+
|
|
|
+ // 提取所有 <p> 内容(从 <p><span> 中获取)
|
|
|
+ const contentMatches = [...htmlString.matchAll(/<p[^>]*>.*?<span[^>]*>(.*?)<\/span>.*?<\/p>/gis)];
|
|
|
+ const contents = contentMatches.map((match) => match[1].trim());
|
|
|
+
|
|
|
+ return {
|
|
|
+ title,
|
|
|
+ contents
|
|
|
+ };
|
|
|
+};
|
|
|
+const htmlToPdfFn = async () => {
|
|
|
+ reportFormRef.value.validate((valid) => {
|
|
|
+ if (valid) {
|
|
|
+ downLoading.value = true;
|
|
|
+ const { title, contents } = extractTitleAndContents(editorRef.value.getHtml());
|
|
|
+ reportForm.title = title;
|
|
|
+ reportForm.content = contents.join('');
|
|
|
+ downloadFile(`loadFile/generate`, {
|
|
|
+ method: 'POST',
|
|
|
+ data: reportForm,
|
|
|
+ filename: `${reportForm.title}.${reportForm.type}`,
|
|
|
+ onProgress: (percent) => {
|
|
|
+ console.log(percent);
|
|
|
+ if (percent == 100) {
|
|
|
+ downLoading.value = false;
|
|
|
+ proxy?.updateConfigByKey('report_seq', reportSeq.value++);
|
|
|
+ proxy?.$modal.msgSuccess('报告导出成功');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
const dialogClose = () => {
|
|
|
showReport.value = false;
|
|
|
dialog.loading && dialog.loading.close();
|
|
@@ -721,6 +816,7 @@ const formSubmit = async (field) => {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
+
|
|
|
.expandOrFold {
|
|
|
position: absolute;
|
|
|
top: 50%;
|
|
@@ -728,6 +824,7 @@ const formSubmit = async (field) => {
|
|
|
right: 550px;
|
|
|
cursor: pointer;
|
|
|
z-index: 10;
|
|
|
+
|
|
|
img {
|
|
|
height: 50px;
|
|
|
}
|