|
|
@@ -13,6 +13,7 @@ import IconField from 'primevue/iconfield'
|
|
|
import InputIcon from 'primevue/inputicon'
|
|
|
import InputText from 'primevue/inputtext'
|
|
|
import Message from 'primevue/message'
|
|
|
+import Textarea from 'primevue/textarea'
|
|
|
import { useConfirm } from 'primevue/useconfirm'
|
|
|
import { useToast } from 'primevue/usetoast'
|
|
|
import { computed, onMounted, ref, nextTick } from 'vue'
|
|
|
@@ -60,6 +61,43 @@ const recordFormResolver = computed(() => {
|
|
|
)
|
|
|
})
|
|
|
|
|
|
+// 计算属性:处理描述字段的显示值
|
|
|
+const displayDescription = computed({
|
|
|
+ get() {
|
|
|
+ return formatDescription(recordForm.value.description)
|
|
|
+ },
|
|
|
+ set(value) {
|
|
|
+ // 如果用户输入的是格式化后的文本,尝试转换回JSON格式
|
|
|
+ try {
|
|
|
+ const lines = value.split('\n').filter(line => line.trim())
|
|
|
+ const obj = {}
|
|
|
+ let isFormatted = true
|
|
|
+
|
|
|
+ for (const line of lines) {
|
|
|
+ const colonIndex = line.indexOf(':')
|
|
|
+ if (colonIndex === -1) {
|
|
|
+ isFormatted = false
|
|
|
+ break
|
|
|
+ }
|
|
|
+ const key = line.substring(0, colonIndex).trim()
|
|
|
+ const val = line.substring(colonIndex + 1).trim()
|
|
|
+ obj[key] = val
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isFormatted && Object.keys(obj).length > 0) {
|
|
|
+ // 如果看起来像是格式化后的JSON,转换为JSON字符串
|
|
|
+ recordForm.value.description = JSON.stringify(obj, null, 2)
|
|
|
+ } else {
|
|
|
+ // 否则直接保存原文本
|
|
|
+ recordForm.value.description = value
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ // 如果转换失败,直接保存原文本
|
|
|
+ recordForm.value.description = value
|
|
|
+ }
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
// 获取数据
|
|
|
const fetchData = async () => {
|
|
|
try {
|
|
|
@@ -99,6 +137,29 @@ function formatUrl(url) {
|
|
|
return url.slice(0, 15) + '...' + url.slice(-20)
|
|
|
}
|
|
|
|
|
|
+// 格式化描述内容
|
|
|
+function formatDescription(description) {
|
|
|
+ if (!description) return ''
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 尝试解析JSON
|
|
|
+ const parsed = JSON.parse(description)
|
|
|
+ if (typeof parsed === 'object' && parsed !== null) {
|
|
|
+ // 如果是JSON对象,按行展示
|
|
|
+ const formatted = Object.entries(parsed)
|
|
|
+ .map(([key, value]) => `${key}: ${value}`)
|
|
|
+ .join('\n')
|
|
|
+ console.log('JSON格式化结果:', formatted)
|
|
|
+ return formatted
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ // 如果不是JSON,返回原文本
|
|
|
+ console.log('非JSON文本:', description)
|
|
|
+ }
|
|
|
+
|
|
|
+ return description
|
|
|
+}
|
|
|
+
|
|
|
// 打开新增对话框
|
|
|
const openNewRecordDialog = () => {
|
|
|
recordForm.value = {
|
|
|
@@ -121,6 +182,8 @@ const openEditRecordDialog = async (record) => {
|
|
|
}
|
|
|
isEditMode.value = true
|
|
|
recordDialog.value = true
|
|
|
+ // 更新formKey来强制表单重新渲染
|
|
|
+ formKey.value++
|
|
|
} catch (error) {
|
|
|
toast.add({
|
|
|
severity: 'error',
|
|
|
@@ -333,8 +396,12 @@ onMounted(() => {
|
|
|
|
|
|
<Column field="description" header="描述" style="min-width: 200px">
|
|
|
<template #body="slotProps">
|
|
|
- <div class="max-w-xs truncate" :title="slotProps.data.description">
|
|
|
- {{ slotProps.data.description }}
|
|
|
+ <div
|
|
|
+ class="max-w-xs whitespace-pre-line text-sm"
|
|
|
+ :title="formatDescription(slotProps.data.description)"
|
|
|
+ style="max-height: 100px; overflow-y: auto;"
|
|
|
+ >
|
|
|
+ {{ formatDescription(slotProps.data.description) }}
|
|
|
</div>
|
|
|
</template>
|
|
|
</Column>
|
|
|
@@ -421,16 +488,15 @@ onMounted(() => {
|
|
|
|
|
|
<div class="field mt-4">
|
|
|
<FloatLabel variant="on">
|
|
|
- <IconField>
|
|
|
- <InputIcon class="pi pi-file-edit" />
|
|
|
- <InputText
|
|
|
- id="description"
|
|
|
- name="description"
|
|
|
- v-model="recordForm.description"
|
|
|
- autocomplete="off"
|
|
|
- fluid
|
|
|
- />
|
|
|
- </IconField>
|
|
|
+ <Textarea
|
|
|
+ id="description"
|
|
|
+ name="description"
|
|
|
+ v-model="displayDescription"
|
|
|
+ autocomplete="off"
|
|
|
+ fluid
|
|
|
+ rows="4"
|
|
|
+ autoResize
|
|
|
+ />
|
|
|
<label for="description">描述</label>
|
|
|
</FloatLabel>
|
|
|
<Message v-if="$form.description?.invalid" severity="error" size="small" variant="simple">
|