fix: 独立窗口数据回显

This commit is contained in:
陆光LG
2025-08-13 08:41:00 +08:00
parent cf1db42bb0
commit 816fce233a
18 changed files with 2731 additions and 704 deletions

View File

@@ -1,7 +1,11 @@
<template>
<div class="edit-dialog">
<Dialog v-model="dialogVisible" :title="dialogTitle" width="85%" top="10vh">
<el-scrollbar>
<!-- 独立窗口模式 -->
<div v-if="isIndependent" class="independent-form">
<div class="independent-header">
<h2>{{ dialogTitle }}</h2>
</div>
<div class="independent-content">
<el-scrollbar height="calc(100vh - 150px)">
<div class="main">
<el-form
ref="formRef"
@@ -270,12 +274,13 @@
</div>
</div>
</el-scrollbar>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
<div class="independent-footer">
<el-button @click="handleCancel"> </el-button>
<el-button type="primary" @click="submitForm" :loading="formLoading"> </el-button>
</div>
</div>
</div>
<!-- 表单弹窗添加/修改 -->
<FileForm ref="FileRef" @success="handleUploadSuccess" />
@@ -348,10 +353,28 @@ import { cloneDeep } from 'lodash-es'
import FileForm from './components/FileForm.vue'
defineOptions({ name: 'WpsXlsxFrom' })
// 定义Tree接口
interface Tree {
functions?: string
chineseName?: string
parameter?: string
[key: string]: any
}
// 定义组件 props
interface Props {
isIndependent?: boolean
}
const props = withDefaults(defineProps<Props>(), {
isIndependent: false
})
const xlsxPointsList = ref<Tree[]>([]) // 树形结构
const xlsxPointsInfoList = ref<Tree[]>([]) // 树形结构
const list = ref([]) // 列表的数
const list = ref<any[]>([]) // 列表的数
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const loading = ref(false) // 列表的加载中
@@ -404,11 +427,11 @@ const formData = ref({
]
})
let xlsxPointsInfosList: (typeof xlsxPoints)[] = []
let xlsxPointsInfosList: any[] = []
const removePoint = (index: number) => {
list.value.splice(index, 1)
}
function fileTypeFormatter(row, column, cellValue) {
function fileTypeFormatter(_row, _column, cellValue) {
if (cellValue === '0') return '素材文件(上传ZIP)'
if (cellValue === '1') return '考试文件'
if (cellValue === '2') return '结果文件'
@@ -439,22 +462,23 @@ const dialogFormVisibleXlsxInfos = ref(false)
const titles = ref('')
const filePath = ref('')
const handleCheckChange = (data: Tree, checked: boolean, indeterminate: boolean) => {
const handleCheckChange = (data: Tree, _checked: boolean, _indeterminate: boolean) => {
console.log(data)
const xlsxPoints = {
firstName: '',
index: '',
function: '',
examName: '',
examCode: ''
examCode: '',
method: ''
}
if (data.functions != null && data.functions != "") {
if (data.functions != null && data.functions != '') {
xlsxPoints.firstName = chineseName.value
xlsxPoints.index = textIndex.value
xlsxPoints.function = data.functions
xlsxPoints.examName = data.chineseName
xlsxPoints.function = data.functions || ''
xlsxPoints.examName = data.chineseName || ''
xlsxPoints.examCode = '111'
xlsxPoints.method = data.parameter
xlsxPoints.method = data.parameter || ''
xlsxPointsInfosList.push(cloneDeep(xlsxPoints))
}
}
@@ -462,7 +486,7 @@ const file = ref()
// 获取xlsx文件并使用文件流进行解析
const getXlsxDataInfo = async () => {
const fileInput = document.getElementById('xlsxFile') as HTMLInputElement
if (fileInput != null) {
if (fileInput != null && fileInput.files && fileInput.files.length > 0) {
file.value = fileInput.files[0]
const res = await XlsxApi.getXlsxDataInfo({ file: file.value })
xlsxPointsList.value = []
@@ -487,7 +511,7 @@ const handleNodelClick = async (row: any) => {
// 获取名称
chineseName.value = '【' + row.name + '】'
textIndex.value = row.index
const res = await XlsxApi.getXlsxByNameList(row.type)
const res = await XlsxApi.getSlideByNameList(row.type)
xlsxPointsInfoList.value = []
xlsxPointsInfoList.value.push(...handleTree(res))
dialogFormVisibleXlsxInfos.value = true
@@ -532,7 +556,7 @@ const formRef = ref() // 表单 Ref
const leftActiveName = ref('desc')
// 右侧tab
const rightActiveName = ref('annex')
const rightHandleClick = (tab, e) => {
const rightHandleClick = (tab, _e) => {
rightActiveName.value = tab.paneName.value
}
const selections = ref([])
@@ -620,6 +644,48 @@ const open = async (queryParams: any, type: string, id?: number) => {
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 独立窗口模式下的数据加载函数 */
const loadFormData = async (queryParams: any, type: string, id?: number) => {
console.log('WpsXlsx loadFormData called with:', { queryParams, type, id })
formType.value = type
// 修改时,设置数据
if (id) {
console.log('Loading existing Excel question with ID:', id)
formLoading.value = true
try {
const res = await QuestionApi.getQuestion(id)
console.log('Excel question data loaded:', res)
formData.value = res
console.log(formData.value)
list.value = formData.value.answerList
documentList.value = res.fileUploads
console.log('Excel form data set:', {
formData: formData.value,
list: list.value,
documentList: documentList.value
})
} catch (error) {
console.error('Failed to load question data:', error)
message.error('加载题目数据失败')
} finally {
formLoading.value = false
}
} else {
console.log('Creating new Excel question with params:', queryParams)
resetForm()
formData.value.specialtyName = queryParams.specialtyName
formData.value.courseName = queryParams.courseName
formData.value.subjectName = queryParams.subjectName
formData.value.pointNames = queryParams.pointNamesVo
formData.value.pointNamesVo = queryParams.pointNames
formData.value.chapteridDictText = queryParams.chapteridDictTextVo
formData.value.chapteridDictTextVo = queryParams.chapteridDictText
console.log('New Excel form data set:', formData.value)
}
}
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
@@ -642,17 +708,46 @@ const submitForm = async () => {
await QuestionApi.editQuestion(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
// 如果是独立窗口模式通过Tauri事件通信
if (props.isIndependent) {
const { emit: tauriEmit } = await import('@tauri-apps/api/event')
await tauriEmit('excel-form-success', { message: 'Form submitted successfully' })
// 关闭独立窗口
const { getCurrentWindow } = await import('@tauri-apps/api/window')
await getCurrentWindow().close()
} else {
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
}
} finally {
formLoading.value = false
}
}
/** 取消操作 */
const handleCancel = async () => {
if (props.isIndependent) {
// 独立窗口模式通过Tauri事件通信并关闭窗口
const { emit: tauriEmit } = await import('@tauri-apps/api/event')
await tauriEmit('excel-form-cancel', { message: 'Form cancelled' })
// 关闭独立窗口
const { getCurrentWindow } = await import('@tauri-apps/api/window')
await getCurrentWindow().close()
} else {
// 对话框模式:关闭对话框
dialogVisible.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
pointNamesVo: '',
chapteridDictTextVo: '',
content: '',
specialtyName: '',
courseName: '',
@@ -733,7 +828,7 @@ const handleTreeWithLevel = (list, level = 1) => {
// 只允许点击第三级节点
const treeRef = ref() // 引用 el-tree
const handleNodeClick = (data, node) => {
const handleNodeClick = (data, _node) => {
if (data.level === 3) {
formData.value.pointNames = data.id
formData.value.pointNamesVo = data.name
@@ -765,8 +860,283 @@ const openPoints = async () => {
await getTree()
dialogVisiblePoints.value = true
}
// 独立窗口模式下的初始化
onMounted(async () => {
if (props.isIndependent) {
try {
const { listen } = await import('@tauri-apps/api/event')
// 监听表单初始化事件
await listen('init-excel-form', (event: any) => {
const { queryParams, type, id, theme } = event.payload
console.log('Received init-excel-form event:', { queryParams, type, id, theme })
// 应用主题
if (theme) {
applyTheme(theme)
}
// 独立窗口模式下直接加载数据,不设置 dialog 状态
loadFormData(queryParams, type, id)
})
} catch (error) {
console.error('Failed to setup Tauri event listeners:', error)
}
}
})
// 应用主题函数
const applyTheme = (theme: any) => {
try {
const setCssVar = (prop: string, val: any) => {
document.documentElement.style.setProperty(prop, val)
}
// 应用主题变量
if (theme.elColorPrimary) {
setCssVar('--el-color-primary', theme.elColorPrimary)
setCssVar('--el-color-primary-light-3', lightenColor(theme.elColorPrimary, 0.3))
setCssVar('--el-color-primary-light-9', lightenColor(theme.elColorPrimary, 0.9))
}
// 设置其他主题变量
setCssVar('--app-content-bg-color', '#f5f7f9')
setCssVar('--el-bg-color', '#ffffff')
setCssVar('--el-text-color-primary', '#303133')
setCssVar('--el-border-color', '#e4e7ed')
setCssVar('--el-border-color-lighter', '#f0f0f0')
setCssVar('--el-fill-color-light', '#f5f7fa')
setCssVar('--el-fill-color-lighter', '#fafafa')
setCssVar('--el-box-shadow', '0 2px 8px rgba(0, 0, 0, 0.1)')
setCssVar('--el-box-shadow-light', '0 2px 4px rgba(0, 0, 0, 0.1)')
} catch (error) {
console.error('Failed to apply theme:', error)
}
}
// 颜色工具函数
const lightenColor = (color: string, amount: number) => {
const num = parseInt(color.replace('#', ''), 16)
const amt = Math.round(2.55 * amount * 100)
const R = (num >> 16) + amt
const G = ((num >> 8) & 0x00ff) + amt
const B = (num & 0x0000ff) + amt
return (
'#' +
(
0x1000000 +
(R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 +
(G < 255 ? (G < 1 ? 0 : G) : 255) * 0x100 +
(B < 255 ? (B < 1 ? 0 : B) : 255)
)
.toString(16)
.slice(1)
)
}
</script>
<style lang="scss" scoped>
// 独立窗口模式样式
.independent-form {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
background-color: var(--app-content-bg-color);
overflow: hidden;
.independent-header {
background: var(--el-bg-color);
border-bottom: 1px solid var(--el-border-color);
padding: 16px 24px;
box-shadow: var(--el-box-shadow-light);
h2 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: var(--el-text-color-primary);
}
}
.independent-content {
flex: 1;
overflow: hidden;
background: var(--el-bg-color);
margin: 16px;
border-radius: 8px;
box-shadow: var(--el-box-shadow);
padding: 20px; // 主要内容区域样式
:deep(.el-form) {
.el-row {
margin-bottom: 20px;
.el-col {
padding: 0 8px;
}
}
.el-form-item {
margin-bottom: 20px;
.el-form-item__label {
font-weight: 600;
color: var(--el-text-color-primary);
line-height: 1.6;
}
.el-input,
.el-select,
.el-textarea {
.el-input__inner,
.el-select__inner,
.el-textarea__inner {
border-radius: 6px;
border: 1px solid var(--el-border-color);
transition: border-color 0.3s;
&:focus {
border-color: var(--el-color-primary);
}
}
}
}
}
// 标签页和表格样式
:deep(.el-tabs) {
height: 100%;
.el-tabs__header {
margin-bottom: 20px;
.el-tabs__item {
padding: 0 20px;
height: 40px;
line-height: 40px;
font-weight: 500;
&.is-active {
color: var(--el-color-primary);
font-weight: 600;
}
}
.el-tabs__active-bar {
height: 3px;
border-radius: 2px;
}
}
.el-tabs__content {
height: calc(100% - 60px);
overflow-y: auto;
padding-right: 8px;
.block {
padding: 16px;
background: var(--el-fill-color-lighter);
border-radius: 6px;
border: 1px solid var(--el-border-color);
margin-bottom: 16px;
.tip {
color: #8a6d3b;
background-color: #fcf8e3;
border: 1px solid #faebcc;
padding: 16px;
border-radius: 6px;
margin-bottom: 16px;
line-height: 1.6;
p {
margin: 0;
}
}
.btn-line {
display: flex;
gap: 12px;
margin: 16px 0;
flex-wrap: wrap;
}
.flex {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
flex-wrap: wrap;
}
}
}
.custom-tabs-label {
display: flex;
align-items: center;
.setting_icon {
width: 16px;
height: 16px;
background: url('@/assets/icon/setting_blue.png') no-repeat center;
background-size: 100%;
margin-left: 8px;
opacity: 0;
transition: opacity 0.3s;
}
}
.el-tabs__item.is-active {
.custom-tabs-label .setting_icon {
opacity: 1;
}
}
}
}
.independent-footer {
background: var(--el-bg-color);
border-top: 1px solid var(--el-border-color);
padding: 16px 24px;
display: flex;
justify-content: flex-end;
gap: 12px;
box-shadow: var(--el-box-shadow-light);
.el-button {
border-radius: 6px;
font-weight: 500;
padding: 8px 16px;
&.el-button--primary {
background: var(--el-color-primary);
border-color: var(--el-color-primary);
color: #ffffff !important;
&:hover {
background: var(--el-color-primary-light-3);
border-color: var(--el-color-primary-light-3);
color: #ffffff !important;
}
}
&.el-button--default {
background: #ffffff;
border-color: var(--el-border-color);
color: var(--el-text-color-primary);
&:hover {
background: var(--el-fill-color-lighter);
border-color: var(--el-color-primary);
color: var(--el-color-primary);
}
}
}
}
}
// Dialog模式样式保持原有样式
.edit-dialog {
:deep(.el-dialog) {
display: flex;
@@ -889,23 +1259,215 @@ const openPoints = async () => {
}
}
:deep(.tox-tinymce) {
.tox-statusbar {
display: none;
// 全局样式优化
:deep(.el-form-item) {
margin-bottom: 20px;
.el-form-item__label {
font-weight: 600;
color: var(--el-text-color-primary);
line-height: 1.6;
}
.el-input,
.el-select,
.el-textarea {
.el-input__inner,
.el-select__inner,
.el-textarea__inner {
border-radius: 6px;
border: 1px solid var(--el-border-color);
transition: border-color 0.3s;
&:focus {
border-color: var(--el-color-primary);
}
}
}
}
:deep(.el-table) {
border-radius: 6px;
overflow: hidden;
.el-table__header-wrapper {
.el-table__header {
thead {
tr {
th {
background: #ebebeb;
background: var(--el-fill-color-light);
color: var(--el-text-color-primary);
font-weight: 600;
border-bottom: 1px solid var(--el-border-color);
}
}
}
}
}
.el-table__body-wrapper {
.el-table__body {
tbody {
tr {
td {
border-bottom: 1px solid var(--el-border-color-lighter);
padding: 12px 0;
}
&:hover {
background-color: var(--el-fill-color-lighter);
}
}
}
}
}
}
:deep(.el-tabs) {
.el-tabs__item {
padding: 0 20px;
height: 40px;
line-height: 40px;
font-weight: 500;
&.is-active {
color: var(--el-color-primary);
font-weight: 600;
}
}
.el-tabs__active-bar {
height: 3px;
border-radius: 2px;
}
}
:deep(.el-checkbox) {
margin-right: 24px;
.el-checkbox__label {
font-weight: 500;
color: var(--el-text-color-primary);
}
}
:deep(.el-radio) {
margin-right: 24px;
.el-radio__label {
font-weight: 500;
color: var(--el-text-color-primary);
}
}
:deep(.el-button) {
border-radius: 6px;
font-weight: 500;
padding: 8px 16px;
&.el-button--primary {
background: var(--el-color-primary);
border-color: var(--el-color-primary);
color: #ffffff; // 确保文字颜色为白色
&:hover {
background: var(--el-color-primary-light-3);
border-color: var(--el-color-primary-light-3);
color: #ffffff; // 悬停时文字也保持白色
}
}
&.el-button--default {
background: #ffffff;
border-color: var(--el-border-color);
color: var(--el-text-color-primary);
&:hover {
background: var(--el-fill-color-lighter);
border-color: var(--el-color-primary);
color: var(--el-color-primary);
}
}
}
:deep(.tox-tinymce) {
.tox-statusbar {
display: none;
}
}
// 上传组件样式
:deep(.el-upload) {
.el-upload-dragger {
border-radius: 6px;
border: 2px dashed var(--el-border-color);
transition: border-color 0.3s;
&:hover {
border-color: var(--el-color-primary);
}
}
}
// 树形控件样式
:deep(.el-tree) {
.el-tree-node {
.el-tree-node__content {
height: 32px;
border-radius: 4px;
&:hover {
background-color: var(--el-fill-color-lighter);
}
}
&.is-current > .el-tree-node__content {
background-color: var(--el-color-primary-light-9);
color: var(--el-color-primary);
}
}
}
// 对话框样式
:deep(.el-dialog) {
border-radius: 8px;
.el-dialog__header {
padding: 16px 20px;
border-bottom: 1px solid var(--el-border-color);
.el-dialog__title {
font-size: 16px;
font-weight: 600;
}
}
.el-dialog__body {
padding: 20px;
}
.el-dialog__footer {
padding: 16px 20px;
border-top: 1px solid var(--el-border-color);
}
}
// 按钮样式修复
:deep(.el-button) {
&.el-button--primary {
color: #ffffff !important;
background-color: var(--el-color-primary) !important;
border-color: var(--el-color-primary) !important;
&:hover {
background-color: var(--el-color-primary-light-3) !important;
border-color: var(--el-color-primary-light-3) !important;
}
&:active {
background-color: var(--el-color-primary-dark-2) !important;
border-color: var(--el-color-primary-dark-2) !important;
}
}
}
</style>