正在显示
4 个修改的文件
包含
322 行增加
和
166 行删除
| @@ -16,22 +16,22 @@ export const sendMessage = async (params: { questionText: string }) => { | @@ -16,22 +16,22 @@ export const sendMessage = async (params: { questionText: string }) => { | ||
| 16 | const res = await defHttp.get({ | 16 | const res = await defHttp.get({ |
| 17 | url: Api.send, | 17 | url: Api.send, |
| 18 | params, | 18 | params, |
| 19 | + timeout: 60000 | ||
| 19 | }); | 20 | }); |
| 20 | - return res; | 21 | + console.log("res...",res) |
| 22 | + // 确保返回的数据结构正确 | ||
| 23 | + if (res ) { | ||
| 24 | + return { | ||
| 25 | + answer: res.answer || '', | ||
| 26 | + similarity: res.similarity || 0, | ||
| 27 | + fileName: res.fileName || '', | ||
| 28 | + fileBase64: res.fileBase64 || null | ||
| 29 | + }; | ||
| 30 | + } | ||
| 31 | + return null; | ||
| 21 | } catch (error) { | 32 | } catch (error) { |
| 22 | console.error("Error sending message:", error); | 33 | console.error("Error sending message:", error); |
| 23 | createMessage.error('发送消息失败'); | 34 | createMessage.error('发送消息失败'); |
| 24 | return null; | 35 | return null; |
| 25 | } | 36 | } |
| 26 | }; | 37 | }; |
| 27 | - | ||
| 28 | -// export const sendMessage = (params) => { | ||
| 29 | -// return defHttp.get({ url: Api.send, params }, { isTransformResponse: false }); | ||
| 30 | -// }; | ||
| 31 | - | ||
| 32 | -// 保留其他API方法,但聊天界面可能不需要它们 | ||
| 33 | -export const list = async () => ({ records: [] }); | ||
| 34 | -export const deleteOne = async () => {}; | ||
| 35 | -export const batchDelete = async () => {}; | ||
| 36 | -export const getImportUrl = Api.send; | ||
| 37 | -export const getExportUrl = Api.send; |
| 1 | <template> | 1 | <template> |
| 2 | <div class="chat-container"> | 2 | <div class="chat-container"> |
| 3 | + <div class="chat-layout"> | ||
| 3 | <!-- 聊天消息区域 --> | 4 | <!-- 聊天消息区域 --> |
| 4 | <div class="chat-messages" ref="messagesContainer"> | 5 | <div class="chat-messages" ref="messagesContainer"> |
| 5 | <div v-for="(message, index) in messages" :key="index" :class="['message', message.type]"> | 6 | <div v-for="(message, index) in messages" :key="index" :class="['message', message.type]"> |
| 6 | <div class="message-content"> | 7 | <div class="message-content"> |
| 7 | - <div class="message-text">{{ message.text }}</div> | 8 | + <div class="message-text" v-html="message.text"></div> |
| 8 | <div v-if="message.type === 'assistant' && message.similarity" class="message-meta"> | 9 | <div v-if="message.type === 'assistant' && message.similarity" class="message-meta"> |
| 9 | 相似度: {{ (message.similarity * 100).toFixed(2) }}% | 10 | 相似度: {{ (message.similarity * 100).toFixed(2) }}% |
| 11 | + <a v-if="message.fileBase64" @click="showPreview(message)">预览文件</a> | ||
| 10 | </div> | 12 | </div> |
| 11 | </div> | 13 | </div> |
| 12 | </div> | 14 | </div> |
| @@ -17,6 +19,30 @@ | @@ -17,6 +19,30 @@ | ||
| 17 | </div> | 19 | </div> |
| 18 | </div> | 20 | </div> |
| 19 | 21 | ||
| 22 | + <!-- 文件预览区域 - 右侧 --> | ||
| 23 | + <div v-if="previewContent" class="file-preview"> | ||
| 24 | + <div class="preview-header"> | ||
| 25 | + <span>文件预览</span> | ||
| 26 | + <a-button type="text" @click="closePreview"> | ||
| 27 | + <template #icon><CloseOutlined /></template> | ||
| 28 | + </a-button> | ||
| 29 | + </div> | ||
| 30 | + <div class="preview-content"> | ||
| 31 | + <div v-if="previewType === 'txt' || previewType === 'md'" class="text-preview"> | ||
| 32 | + <pre>{{ previewContent }}</pre> | ||
| 33 | + </div> | ||
| 34 | + <div v-else-if="previewType === 'doc' || previewType === 'docx'" class="doc-preview"> | ||
| 35 | + <!-- 使用Blob URL直接预览 --> | ||
| 36 | + <iframe | ||
| 37 | + :src="previewUrl" | ||
| 38 | + frameborder="0" | ||
| 39 | + style="width:100%; height:100%;" | ||
| 40 | + ></iframe> | ||
| 41 | + </div> | ||
| 42 | + </div> | ||
| 43 | + </div> | ||
| 44 | + </div> | ||
| 45 | + | ||
| 20 | <!-- 输入区域 --> | 46 | <!-- 输入区域 --> |
| 21 | <div class="chat-input"> | 47 | <div class="chat-input"> |
| 22 | <a-input | 48 | <a-input |
| @@ -39,9 +65,9 @@ | @@ -39,9 +65,9 @@ | ||
| 39 | 65 | ||
| 40 | <script lang="ts" name="zdy-rag-chat" setup> | 66 | <script lang="ts" name="zdy-rag-chat" setup> |
| 41 | import { ref, reactive, nextTick } from 'vue'; | 67 | import { ref, reactive, nextTick } from 'vue'; |
| 42 | -import { list, sendMessage as apiSendMessage } from './ZdyRag.api'; | ||
| 43 | -import { BasicTable, useTable } from '/@/components/Table'; | 68 | +import { sendMessage as apiSendMessage } from './ZdyRag.api'; |
| 44 | import { useMessage } from '/@/hooks/web/useMessage'; | 69 | import { useMessage } from '/@/hooks/web/useMessage'; |
| 70 | +import { CloseOutlined } from '@ant-design/icons-vue'; | ||
| 45 | 71 | ||
| 46 | const { createMessage } = useMessage(); | 72 | const { createMessage } = useMessage(); |
| 47 | 73 | ||
| @@ -50,13 +76,17 @@ const messages = reactive([ | @@ -50,13 +76,17 @@ const messages = reactive([ | ||
| 50 | { | 76 | { |
| 51 | type: 'assistant', | 77 | type: 'assistant', |
| 52 | text: '您好!我是智能助手,请问有什么可以帮您?', | 78 | text: '您好!我是智能助手,请问有什么可以帮您?', |
| 53 | - similarity: null | 79 | + similarity: null, |
| 80 | + fileBase64: null | ||
| 54 | } | 81 | } |
| 55 | ]); | 82 | ]); |
| 56 | 83 | ||
| 57 | const inputMessage = ref(''); | 84 | const inputMessage = ref(''); |
| 58 | const loading = ref(false); | 85 | const loading = ref(false); |
| 59 | const messagesContainer = ref<HTMLElement>(); | 86 | const messagesContainer = ref<HTMLElement>(); |
| 87 | +const previewContent = ref(''); | ||
| 88 | +const previewType = ref(''); | ||
| 89 | +const previewUrl = ref(''); | ||
| 60 | 90 | ||
| 61 | // 发送消息 | 91 | // 发送消息 |
| 62 | const sendMessage = async () => { | 92 | const sendMessage = async () => { |
| @@ -67,34 +97,102 @@ const sendMessage = async () => { | @@ -67,34 +97,102 @@ const sendMessage = async () => { | ||
| 67 | messages.push({ | 97 | messages.push({ |
| 68 | type: 'user', | 98 | type: 'user', |
| 69 | text: text, | 99 | text: text, |
| 70 | - similarity: null | 100 | + similarity: null, |
| 101 | + fileBase64: null | ||
| 71 | }); | 102 | }); |
| 72 | 103 | ||
| 73 | inputMessage.value = ''; | 104 | inputMessage.value = ''; |
| 74 | loading.value = true; | 105 | loading.value = true; |
| 106 | + closePreview(); | ||
| 75 | 107 | ||
| 76 | try { | 108 | try { |
| 77 | // 调用API发送消息 | 109 | // 调用API发送消息 |
| 78 | const res = await apiSendMessage({ questionText: text }); | 110 | const res = await apiSendMessage({ questionText: text }); |
| 79 | - console.log("res....",res) | ||
| 80 | - if (res?.answer) { | ||
| 81 | - messages.push({ | 111 | + console.log('API响应:', res); |
| 112 | + | ||
| 113 | + if (res) { | ||
| 114 | + const newMessage = { | ||
| 82 | type: 'assistant', | 115 | type: 'assistant', |
| 83 | - text: res.answer, | ||
| 84 | - similarity: res.similarity || null | ||
| 85 | - }); | 116 | + text: formatAnswer(res.answer), |
| 117 | + similarity: res.similarity || null, | ||
| 118 | + fileBase64: res.fileBase64 || null, | ||
| 119 | + fileName: res.fileName || '' | ||
| 120 | + }; | ||
| 121 | + messages.push(newMessage); | ||
| 122 | + | ||
| 123 | + // 如果有文件内容,自动显示预览 | ||
| 124 | + if (res.fileBase64 && res.fileName) { | ||
| 125 | + showPreview(newMessage); | ||
| 126 | + } | ||
| 86 | } else { | 127 | } else { |
| 87 | - createMessage.error('获取回答失败'); | 128 | + createMessage.error('获取回答失败: 返回数据为空'); |
| 88 | } | 129 | } |
| 89 | } catch (error) { | 130 | } catch (error) { |
| 90 | console.error('发送消息失败:', error); | 131 | console.error('发送消息失败:', error); |
| 91 | - createMessage.error('发送消息失败'); | 132 | + createMessage.error('发送消息失败: ' + (error as Error).message); |
| 92 | } finally { | 133 | } finally { |
| 93 | loading.value = false; | 134 | loading.value = false; |
| 94 | scrollToBottom(); | 135 | scrollToBottom(); |
| 95 | } | 136 | } |
| 96 | }; | 137 | }; |
| 97 | 138 | ||
| 139 | +// 显示文件预览 | ||
| 140 | +const showPreview = (message) => { | ||
| 141 | + if (!message.fileBase64 || !message.fileName) { | ||
| 142 | + console.warn('无法预览: 缺少fileBase64或fileName'); | ||
| 143 | + return; | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + try { | ||
| 147 | + const fileExt = message.fileName.split('.').pop()?.toLowerCase() || ''; | ||
| 148 | + previewType.value = fileExt; | ||
| 149 | + | ||
| 150 | + if (fileExt === 'txt' || fileExt === 'md') { | ||
| 151 | + // 对于文本文件,直接解码显示 | ||
| 152 | + previewContent.value = atob(message.fileBase64); | ||
| 153 | + } else if (fileExt === 'doc' || fileExt === 'docx') { | ||
| 154 | + // 对于Word文档,创建Blob对象并生成URL | ||
| 155 | + const byteCharacters = atob(message.fileBase64); | ||
| 156 | + const byteNumbers = new Array(byteCharacters.length); | ||
| 157 | + for (let i = 0; i < byteCharacters.length; i++) { | ||
| 158 | + byteNumbers[i] = byteCharacters.charCodeAt(i); | ||
| 159 | + } | ||
| 160 | + const byteArray = new Uint8Array(byteNumbers); | ||
| 161 | + const blob = new Blob([byteArray], { | ||
| 162 | + type: fileExt === 'docx' ? | ||
| 163 | + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' : | ||
| 164 | + 'application/msword' | ||
| 165 | + }); | ||
| 166 | + | ||
| 167 | + // 直接使用Blob URL | ||
| 168 | + previewUrl.value = URL.createObjectURL(blob); | ||
| 169 | + previewContent.value = 'doc-preview'; | ||
| 170 | + } else { | ||
| 171 | + console.warn('不支持的文件类型:', fileExt); | ||
| 172 | + createMessage.warning(`不支持预览 ${fileExt} 格式的文件`); | ||
| 173 | + } | ||
| 174 | + } catch (error) { | ||
| 175 | + console.error('文件预览失败:', error); | ||
| 176 | + createMessage.error('文件预览失败'); | ||
| 177 | + } | ||
| 178 | +}; | ||
| 179 | + | ||
| 180 | +// 关闭预览 | ||
| 181 | +const closePreview = () => { | ||
| 182 | + previewContent.value = ''; | ||
| 183 | + previewType.value = ''; | ||
| 184 | + if (previewUrl.value) { | ||
| 185 | + URL.revokeObjectURL(previewUrl.value); | ||
| 186 | + previewUrl.value = ''; | ||
| 187 | + } | ||
| 188 | +}; | ||
| 189 | + | ||
| 190 | +// 格式化回答内容 | ||
| 191 | +const formatAnswer = (answer: string) => { | ||
| 192 | + // 替换[n]为换行符,然后将换行符转换为<br>标签 | ||
| 193 | + return answer.replace(/\[n\]/g, '\n').replace(/\n/g, '<br>'); | ||
| 194 | +}; | ||
| 195 | + | ||
| 98 | // 滚动到底部 | 196 | // 滚动到底部 |
| 99 | const scrollToBottom = () => { | 197 | const scrollToBottom = () => { |
| 100 | nextTick(() => { | 198 | nextTick(() => { |
| @@ -110,23 +208,71 @@ const scrollToBottom = () => { | @@ -110,23 +208,71 @@ const scrollToBottom = () => { | ||
| 110 | display: flex; | 208 | display: flex; |
| 111 | flex-direction: column; | 209 | flex-direction: column; |
| 112 | height: calc(100vh - 120px); | 210 | height: calc(100vh - 120px); |
| 113 | - max-width: 800px; | 211 | + max-width: 1200px; /* 增加宽度以容纳两个区域 */ |
| 114 | margin: 0 auto; | 212 | margin: 0 auto; |
| 115 | padding: 20px; | 213 | padding: 20px; |
| 116 | background-color: #f5f5f5; | 214 | background-color: #f5f5f5; |
| 117 | border-radius: 8px; | 215 | border-radius: 8px; |
| 118 | } | 216 | } |
| 119 | 217 | ||
| 120 | -.chat-messages { | 218 | +.chat-layout { |
| 219 | + display: flex; | ||
| 121 | flex: 1; | 220 | flex: 1; |
| 221 | + gap: 20px; | ||
| 222 | + overflow: hidden; | ||
| 223 | + margin-bottom: 20px; | ||
| 224 | +} | ||
| 225 | + | ||
| 226 | +.chat-messages { | ||
| 227 | + flex: 2; | ||
| 122 | overflow-y: auto; | 228 | overflow-y: auto; |
| 123 | padding: 10px; | 229 | padding: 10px; |
| 124 | - margin-bottom: 20px; | ||
| 125 | background-color: white; | 230 | background-color: white; |
| 126 | border-radius: 8px; | 231 | border-radius: 8px; |
| 127 | box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); | 232 | box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); |
| 128 | } | 233 | } |
| 129 | 234 | ||
| 235 | +.file-preview { | ||
| 236 | + flex: 1; | ||
| 237 | + min-width: 350px; /* 确保预览区域有最小宽度 */ | ||
| 238 | + background-color: white; | ||
| 239 | + border-radius: 8px; | ||
| 240 | + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); | ||
| 241 | + overflow: hidden; | ||
| 242 | + display: flex; | ||
| 243 | + flex-direction: column; | ||
| 244 | + | ||
| 245 | + .preview-header { | ||
| 246 | + display: flex; | ||
| 247 | + justify-content: space-between; | ||
| 248 | + align-items: center; | ||
| 249 | + padding: 10px 15px; | ||
| 250 | + background-color: #f0f0f0; | ||
| 251 | + border-bottom: 1px solid #e8e8e8; | ||
| 252 | + } | ||
| 253 | + | ||
| 254 | + .preview-content { | ||
| 255 | + flex: 1; | ||
| 256 | + overflow: auto; | ||
| 257 | + | ||
| 258 | + .text-preview { | ||
| 259 | + padding: 15px; | ||
| 260 | + white-space: pre-wrap; | ||
| 261 | + font-family: monospace; | ||
| 262 | + } | ||
| 263 | + | ||
| 264 | + .doc-preview { | ||
| 265 | + height: 100%; | ||
| 266 | + | ||
| 267 | + iframe { | ||
| 268 | + width: 100%; | ||
| 269 | + height: 100%; | ||
| 270 | + border: none; | ||
| 271 | + } | ||
| 272 | + } | ||
| 273 | + } | ||
| 274 | +} | ||
| 275 | + | ||
| 130 | .message { | 276 | .message { |
| 131 | margin-bottom: 15px; | 277 | margin-bottom: 15px; |
| 132 | display: flex; | 278 | display: flex; |
| @@ -163,6 +309,16 @@ const scrollToBottom = () => { | @@ -163,6 +309,16 @@ const scrollToBottom = () => { | ||
| 163 | color: #666; | 309 | color: #666; |
| 164 | margin-top: 5px; | 310 | margin-top: 5px; |
| 165 | text-align: right; | 311 | text-align: right; |
| 312 | + | ||
| 313 | + a { | ||
| 314 | + margin-left: 10px; | ||
| 315 | + color: #1890ff; | ||
| 316 | + cursor: pointer; | ||
| 317 | + | ||
| 318 | + &:hover { | ||
| 319 | + text-decoration: underline; | ||
| 320 | + } | ||
| 321 | + } | ||
| 166 | } | 322 | } |
| 167 | 323 | ||
| 168 | .chat-input { | 324 | .chat-input { |
| 1 | -<template> | ||
| 2 | - <div style="min-height: 400px"> | ||
| 3 | - <BasicForm @register="registerForm"></BasicForm> | ||
| 4 | - <div style="width: 100%;text-align: center" v-if="!formDisabled"> | ||
| 5 | - <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 交</a-button> | ||
| 6 | - </div> | ||
| 7 | - </div> | ||
| 8 | -</template> | 1 | +<!--<template>--> |
| 2 | +<!-- <div style="min-height: 400px">--> | ||
| 3 | +<!-- <BasicForm @register="registerForm"></BasicForm>--> | ||
| 4 | +<!-- <div style="width: 100%;text-align: center" v-if="!formDisabled">--> | ||
| 5 | +<!-- <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 交</a-button>--> | ||
| 6 | +<!-- </div>--> | ||
| 7 | +<!-- </div>--> | ||
| 8 | +<!--</template>--> | ||
| 9 | 9 | ||
| 10 | -<script lang="ts"> | ||
| 11 | - import {BasicForm, useForm} from '/@/components/Form/index'; | ||
| 12 | - import {computed, defineComponent} from 'vue'; | ||
| 13 | - import {defHttp} from '/@/utils/http/axios'; | ||
| 14 | - import { propTypes } from '/@/utils/propTypes'; | ||
| 15 | - import {getBpmFormSchema} from '../ZdyRag.data'; | ||
| 16 | - import {saveOrUpdate} from '../ZdyRag.api'; | 10 | +<!--<script lang="ts">--> |
| 11 | +<!-- import {BasicForm, useForm} from '/@/components/Form/index';--> | ||
| 12 | +<!-- import {computed, defineComponent} from 'vue';--> | ||
| 13 | +<!-- import {defHttp} from '/@/utils/http/axios';--> | ||
| 14 | +<!-- import { propTypes } from '/@/utils/propTypes';--> | ||
| 15 | +<!-- import {getBpmFormSchema} from '../ZdyRag.data';--> | ||
| 16 | +<!-- import {saveOrUpdate} from '../ZdyRag.api';--> | ||
| 17 | +<!-- --> | ||
| 18 | +<!-- export default defineComponent({--> | ||
| 19 | +<!-- name: "TestForm",--> | ||
| 20 | +<!-- components:{--> | ||
| 21 | +<!-- BasicForm--> | ||
| 22 | +<!-- },--> | ||
| 23 | +<!-- props:{--> | ||
| 24 | +<!-- formData: propTypes.object.def({}),--> | ||
| 25 | +<!-- formBpm: propTypes.bool.def(true),--> | ||
| 26 | +<!-- },--> | ||
| 27 | +<!-- setup(props){--> | ||
| 28 | +<!-- const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({--> | ||
| 29 | +<!-- labelWidth: 150,--> | ||
| 30 | +<!-- schemas: getBpmFormSchema(props.formData),--> | ||
| 31 | +<!-- showActionButtonGroup: false,--> | ||
| 32 | +<!-- baseColProps: {span: 24}--> | ||
| 33 | +<!-- });--> | ||
| 17 | 34 | ||
| 18 | - export default defineComponent({ | ||
| 19 | - name: "TestForm", | ||
| 20 | - components:{ | ||
| 21 | - BasicForm | ||
| 22 | - }, | ||
| 23 | - props:{ | ||
| 24 | - formData: propTypes.object.def({}), | ||
| 25 | - formBpm: propTypes.bool.def(true), | ||
| 26 | - }, | ||
| 27 | - setup(props){ | ||
| 28 | - const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({ | ||
| 29 | - labelWidth: 150, | ||
| 30 | - schemas: getBpmFormSchema(props.formData), | ||
| 31 | - showActionButtonGroup: false, | ||
| 32 | - baseColProps: {span: 24} | ||
| 33 | - }); | 35 | +<!-- const formDisabled = computed(()=>{--> |
| 36 | +<!-- if(props.formData.disabled === false){--> | ||
| 37 | +<!-- return false;--> | ||
| 38 | +<!-- }--> | ||
| 39 | +<!-- return true;--> | ||
| 40 | +<!-- });--> | ||
| 34 | 41 | ||
| 35 | - const formDisabled = computed(()=>{ | ||
| 36 | - if(props.formData.disabled === false){ | ||
| 37 | - return false; | ||
| 38 | - } | ||
| 39 | - return true; | ||
| 40 | - }); | 42 | +<!-- let formData = {};--> |
| 43 | +<!-- const queryByIdUrl = '/test/test/queryById';--> | ||
| 44 | +<!-- async function initFormData(){--> | ||
| 45 | +<!-- let params = {id: props.formData.dataId};--> | ||
| 46 | +<!-- const data = await defHttp.get({url: queryByIdUrl, params});--> | ||
| 47 | +<!-- formData = {...data}--> | ||
| 48 | +<!-- //设置表单的值--> | ||
| 49 | +<!-- await setFieldsValue(formData);--> | ||
| 50 | +<!-- //默认是禁用--> | ||
| 51 | +<!-- await setProps({disabled: formDisabled.value})--> | ||
| 52 | +<!-- }--> | ||
| 41 | 53 | ||
| 42 | - let formData = {}; | ||
| 43 | - const queryByIdUrl = '/test/test/queryById'; | ||
| 44 | - async function initFormData(){ | ||
| 45 | - let params = {id: props.formData.dataId}; | ||
| 46 | - const data = await defHttp.get({url: queryByIdUrl, params}); | ||
| 47 | - formData = {...data} | ||
| 48 | - //设置表单的值 | ||
| 49 | - await setFieldsValue(formData); | ||
| 50 | - //默认是禁用 | ||
| 51 | - await setProps({disabled: formDisabled.value}) | ||
| 52 | - } | 54 | +<!-- async function submitForm() {--> |
| 55 | +<!-- let data = getFieldsValue();--> | ||
| 56 | +<!-- let params = Object.assign({}, formData, data);--> | ||
| 57 | +<!-- console.log('表单数据', params)--> | ||
| 58 | +<!-- await saveOrUpdate(params, true)--> | ||
| 59 | +<!-- }--> | ||
| 53 | 60 | ||
| 54 | - async function submitForm() { | ||
| 55 | - let data = getFieldsValue(); | ||
| 56 | - let params = Object.assign({}, formData, data); | ||
| 57 | - console.log('表单数据', params) | ||
| 58 | - await saveOrUpdate(params, true) | ||
| 59 | - } | ||
| 60 | - | ||
| 61 | - initFormData(); | ||
| 62 | - | ||
| 63 | - return { | ||
| 64 | - registerForm, | ||
| 65 | - formDisabled, | ||
| 66 | - submitForm, | ||
| 67 | - } | ||
| 68 | - } | ||
| 69 | - }); | ||
| 70 | -</script> | 61 | +<!-- initFormData();--> |
| 62 | +<!-- --> | ||
| 63 | +<!-- return {--> | ||
| 64 | +<!-- registerForm,--> | ||
| 65 | +<!-- formDisabled,--> | ||
| 66 | +<!-- submitForm,--> | ||
| 67 | +<!-- }--> | ||
| 68 | +<!-- }--> | ||
| 69 | +<!-- });--> | ||
| 70 | +<!--</script>--> |
| 1 | -<template> | ||
| 2 | - <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit"> | ||
| 3 | - <BasicForm @register="registerForm" name="TestForm" /> | ||
| 4 | - </BasicModal> | ||
| 5 | -</template> | 1 | +<!--<template>--> |
| 2 | +<!-- <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">--> | ||
| 3 | +<!-- <BasicForm @register="registerForm" name="TestForm" />--> | ||
| 4 | +<!-- </BasicModal>--> | ||
| 5 | +<!--</template>--> | ||
| 6 | 6 | ||
| 7 | -<script lang="ts" setup> | ||
| 8 | - import {ref, computed, unref} from 'vue'; | ||
| 9 | - import {BasicModal, useModalInner} from '/@/components/Modal'; | ||
| 10 | - import {BasicForm, useForm} from '/@/components/Form/index'; | ||
| 11 | - import {formSchema} from '../ZdyRag.data'; | ||
| 12 | - import {saveOrUpdate} from '../ZdyRag.api'; | ||
| 13 | - // Emits声明 | ||
| 14 | - const emit = defineEmits(['register','success']); | ||
| 15 | - const isUpdate = ref(true); | ||
| 16 | - const isDetail = ref(false); | ||
| 17 | - //表单配置 | ||
| 18 | - const [registerForm, { setProps,resetFields, setFieldsValue, validate, scrollToField }] = useForm({ | ||
| 19 | - labelWidth: 150, | ||
| 20 | - schemas: formSchema, | ||
| 21 | - showActionButtonGroup: false, | ||
| 22 | - baseColProps: {span: 24} | ||
| 23 | - }); | ||
| 24 | - //表单赋值 | ||
| 25 | - const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => { | ||
| 26 | - //重置表单 | ||
| 27 | - await resetFields(); | ||
| 28 | - setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter}); | ||
| 29 | - isUpdate.value = !!data?.isUpdate; | ||
| 30 | - isDetail.value = !!data?.showFooter; | ||
| 31 | - if (unref(isUpdate)) { | ||
| 32 | - //表单赋值 | ||
| 33 | - await setFieldsValue({ | ||
| 34 | - ...data.record, | ||
| 35 | - }); | ||
| 36 | - } | ||
| 37 | - // 隐藏底部时禁用整个表单 | ||
| 38 | - setProps({ disabled: !data?.showFooter }) | ||
| 39 | - }); | ||
| 40 | - //设置标题 | ||
| 41 | - const title = computed(() => (!unref(isUpdate) ? '新增' : !unref(isDetail) ? '详情' : '编辑')); | ||
| 42 | - //表单提交事件 | ||
| 43 | - async function handleSubmit(v) { | ||
| 44 | - try { | ||
| 45 | - let values = await validate(); | ||
| 46 | - setModalProps({confirmLoading: true}); | ||
| 47 | - //提交表单 | ||
| 48 | - await saveOrUpdate(values, isUpdate.value); | ||
| 49 | - //关闭弹窗 | ||
| 50 | - closeModal(); | ||
| 51 | - //刷新列表 | ||
| 52 | - emit('success'); | ||
| 53 | - } catch ({ errorFields }) { | ||
| 54 | - if (errorFields) { | ||
| 55 | - const firstField = errorFields[0]; | ||
| 56 | - if (firstField) { | ||
| 57 | - scrollToField(firstField.name, { behavior: 'smooth', block: 'center' }); | ||
| 58 | - } | ||
| 59 | - } | ||
| 60 | - return Promise.reject(errorFields); | ||
| 61 | - } finally { | ||
| 62 | - setModalProps({confirmLoading: false}); | ||
| 63 | - } | ||
| 64 | - } | ||
| 65 | -</script> | 7 | +<!--<script lang="ts" setup>--> |
| 8 | +<!-- import {ref, computed, unref} from 'vue';--> | ||
| 9 | +<!-- import {BasicModal, useModalInner} from '/@/components/Modal';--> | ||
| 10 | +<!-- import {BasicForm, useForm} from '/@/components/Form/index';--> | ||
| 11 | +<!-- import {formSchema} from '../ZdyRag.data';--> | ||
| 12 | +<!-- import {saveOrUpdate} from '../ZdyRag.api';--> | ||
| 13 | +<!-- // Emits声明--> | ||
| 14 | +<!-- const emit = defineEmits(['register','success']);--> | ||
| 15 | +<!-- const isUpdate = ref(true);--> | ||
| 16 | +<!-- const isDetail = ref(false);--> | ||
| 17 | +<!-- //表单配置--> | ||
| 18 | +<!-- const [registerForm, { setProps,resetFields, setFieldsValue, validate, scrollToField }] = useForm({--> | ||
| 19 | +<!-- labelWidth: 150,--> | ||
| 20 | +<!-- schemas: formSchema,--> | ||
| 21 | +<!-- showActionButtonGroup: false,--> | ||
| 22 | +<!-- baseColProps: {span: 24}--> | ||
| 23 | +<!-- });--> | ||
| 24 | +<!-- //表单赋值--> | ||
| 25 | +<!-- const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {--> | ||
| 26 | +<!-- //重置表单--> | ||
| 27 | +<!-- await resetFields();--> | ||
| 28 | +<!-- setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});--> | ||
| 29 | +<!-- isUpdate.value = !!data?.isUpdate;--> | ||
| 30 | +<!-- isDetail.value = !!data?.showFooter;--> | ||
| 31 | +<!-- if (unref(isUpdate)) {--> | ||
| 32 | +<!-- //表单赋值--> | ||
| 33 | +<!-- await setFieldsValue({--> | ||
| 34 | +<!-- ...data.record,--> | ||
| 35 | +<!-- });--> | ||
| 36 | +<!-- }--> | ||
| 37 | +<!-- // 隐藏底部时禁用整个表单--> | ||
| 38 | +<!-- setProps({ disabled: !data?.showFooter })--> | ||
| 39 | +<!-- });--> | ||
| 40 | +<!-- //设置标题--> | ||
| 41 | +<!-- const title = computed(() => (!unref(isUpdate) ? '新增' : !unref(isDetail) ? '详情' : '编辑'));--> | ||
| 42 | +<!-- //表单提交事件--> | ||
| 43 | +<!-- async function handleSubmit(v) {--> | ||
| 44 | +<!-- try {--> | ||
| 45 | +<!-- let values = await validate();--> | ||
| 46 | +<!-- setModalProps({confirmLoading: true});--> | ||
| 47 | +<!-- //提交表单--> | ||
| 48 | +<!-- await saveOrUpdate(values, isUpdate.value);--> | ||
| 49 | +<!-- //关闭弹窗--> | ||
| 50 | +<!-- closeModal();--> | ||
| 51 | +<!-- //刷新列表--> | ||
| 52 | +<!-- emit('success');--> | ||
| 53 | +<!-- } catch ({ errorFields }) {--> | ||
| 54 | +<!-- if (errorFields) {--> | ||
| 55 | +<!-- const firstField = errorFields[0];--> | ||
| 56 | +<!-- if (firstField) {--> | ||
| 57 | +<!-- scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });--> | ||
| 58 | +<!-- }--> | ||
| 59 | +<!-- }--> | ||
| 60 | +<!-- return Promise.reject(errorFields);--> | ||
| 61 | +<!-- } finally {--> | ||
| 62 | +<!-- setModalProps({confirmLoading: false});--> | ||
| 63 | +<!-- }--> | ||
| 64 | +<!-- }--> | ||
| 65 | +<!--</script>--> | ||
| 66 | 66 | ||
| 67 | -<style lang="less" scoped> | ||
| 68 | - /** 时间和数字输入框样式 */ | ||
| 69 | - :deep(.ant-input-number) { | ||
| 70 | - width: 100%; | ||
| 71 | - } | 67 | +<!--<style lang="less" scoped>--> |
| 68 | +<!-- /** 时间和数字输入框样式 */--> | ||
| 69 | +<!-- :deep(.ant-input-number) {--> | ||
| 70 | +<!-- width: 100%;--> | ||
| 71 | +<!-- }--> | ||
| 72 | 72 | ||
| 73 | - :deep(.ant-calendar-picker) { | ||
| 74 | - width: 100%; | ||
| 75 | - } | ||
| 76 | -</style> | 73 | +<!-- :deep(.ant-calendar-picker) {--> |
| 74 | +<!-- width: 100%;--> | ||
| 75 | +<!-- }--> | ||
| 76 | +<!--</style>--> |
-
请 注册 或 登录 后发表评论