fix: 独立窗口数据回显
This commit is contained in:
121
docs/paper-question-independent-windows.md
Normal file
121
docs/paper-question-independent-windows.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Paper/Question 页面独立窗口功能使用说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
完善了 paper/question 页面的独立窗口功能,支持编程题和表格题通过独立窗口进行新增、编辑和修改操作。
|
||||
|
||||
## 主要改进
|
||||
|
||||
### 1. 独立窗口支持
|
||||
- **编程题**: 点击新增/修改时会打开独立窗口
|
||||
- **表格题**: 点击新增/修改时会打开独立窗口
|
||||
- **其他题型**: 继续使用传统对话框模式
|
||||
|
||||
### 2. 路由配置
|
||||
添加了两个新的独立窗口路由:
|
||||
- `/cdesign-independent` - 编程题独立窗口
|
||||
- `/excel-independent` - 表格题独立窗口
|
||||
|
||||
### 3. 事件通信
|
||||
使用 Tauri 事件系统进行窗口间通信:
|
||||
- `init-cdesign-form` - 初始化编程题表单数据
|
||||
- `init-excel-form` - 初始化表格题表单数据
|
||||
- `cdesign-form-success` / `excel-form-success` - 表单提交成功
|
||||
- `cdesign-form-cancel` / `excel-form-cancel` - 表单取消
|
||||
|
||||
## 文件修改说明
|
||||
|
||||
### 1. 主要页面修改
|
||||
- `src/views/paper/question/index.vue`: 修改 openForm 函数,为编程题和表格题添加独立窗口支持
|
||||
- `src/views/paper/question/CdesignForm.vue`: 已支持独立窗口模式
|
||||
- `src/views/paper/question/WpsXlsxForm.vue`: 添加独立窗口模式支持
|
||||
|
||||
### 2. 新增独立窗口页面
|
||||
- `src/views/cdesign/independent.vue`: 编程题独立窗口页面
|
||||
- `src/views/excel/independent.vue`: 表格题独立窗口页面
|
||||
|
||||
### 3. 路由配置
|
||||
- `src/router/modules/remaining.ts`: 添加独立窗口路由配置
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 在 paper/question 页面中
|
||||
|
||||
1. **选择题型**: 首先选择相应的专业和题型
|
||||
2. **新增操作**:
|
||||
- 编程题:点击"新增"按钮,自动打开编程题独立窗口
|
||||
- 表格题:点击"新增"按钮,自动打开表格题独立窗口
|
||||
- 其他题型:使用传统对话框模式
|
||||
|
||||
3. **修改操作**:
|
||||
- 在题目列表中点击"修改"按钮
|
||||
- 编程题和表格题会在独立窗口中打开
|
||||
- 其他题型继续使用对话框模式
|
||||
|
||||
### 独立窗口功能
|
||||
|
||||
1. **窗口大小**: 1200x800 像素,可调整大小
|
||||
2. **数据传递**: 通过 Tauri 事件系统自动传递初始化数据
|
||||
3. **表单提交**: 提交成功后自动关闭窗口并刷新主页面列表
|
||||
4. **取消操作**: 取消时自动关闭窗口
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 1. 窗口管理
|
||||
```javascript
|
||||
const { WebviewWindow } = await import('@tauri-apps/api/webviewWindow')
|
||||
const webview = new WebviewWindow(windowLabel, {
|
||||
url: '#/cdesign-independent',
|
||||
title: '新增编程题',
|
||||
width: 1200,
|
||||
height: 800,
|
||||
resizable: true,
|
||||
center: true
|
||||
})
|
||||
```
|
||||
|
||||
### 2. 事件通信
|
||||
```javascript
|
||||
// 发送初始化数据
|
||||
import('@tauri-apps/api/event').then(({ emit }) => {
|
||||
emit('init-cdesign-form', {
|
||||
queryParams,
|
||||
type,
|
||||
id
|
||||
})
|
||||
})
|
||||
|
||||
// 监听成功事件
|
||||
const { listen } = await import('@tauri-apps/api/event')
|
||||
listen('cdesign-form-success', () => {
|
||||
getList() // 刷新列表
|
||||
})
|
||||
```
|
||||
|
||||
### 3. 降级处理
|
||||
如果独立窗口创建失败,会自动降级到传统对话框模式,确保功能的稳定性。
|
||||
|
||||
## 优势特点
|
||||
|
||||
1. **用户体验好**: 独立窗口提供更大的操作空间
|
||||
2. **多任务处理**: 可以同时查看列表和编辑表单
|
||||
3. **兼容性强**: 保持其他题型的传统操作方式不变
|
||||
4. **容错机制**: 具备降级处理机制,保证功能稳定性
|
||||
5. **事件驱动**: 使用 Tauri 事件系统实现优雅的窗口间通信
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保 Tauri 环境正常运行
|
||||
2. 独立窗口需要相应的路由配置
|
||||
3. 表单组件需要支持 `isIndependent` 属性
|
||||
4. 事件名称需要保持一致以确保正常通信
|
||||
|
||||
## 扩展说明
|
||||
|
||||
如需为其他题型添加独立窗口支持:
|
||||
|
||||
1. 在对应的 Form 组件中添加 `isIndependent` 属性支持
|
||||
2. 创建对应的独立窗口页面
|
||||
3. 在路由中添加相应配置
|
||||
4. 在 `openForm` 函数中添加相应的处理逻辑
|
||||
5. 定义相应的事件名称用于通信
|
||||
0
docs/独立窗口系统使用说明.md
Normal file
0
docs/独立窗口系统使用说明.md
Normal file
@@ -38,6 +38,7 @@
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.10",
|
||||
"@zxcvbn-ts/core": "^3.0.4",
|
||||
"ace-builds": "^1.43.2",
|
||||
"animate.css": "^4.1.1",
|
||||
"axios": "^1.6.8",
|
||||
"benz-amr-recorder": "^1.1.5",
|
||||
@@ -79,6 +80,7 @@
|
||||
"vue-i18n": "9.10.2",
|
||||
"vue-router": "4.4.5",
|
||||
"vue-types": "^5.1.1",
|
||||
"vue3-ace-editor": "^2.2.4",
|
||||
"vue3-signature": "^0.2.4",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"web-storage-cache": "^1.1.1",
|
||||
|
||||
26
pnpm-lock.yaml
generated
26
pnpm-lock.yaml
generated
@@ -41,6 +41,9 @@ importers:
|
||||
'@zxcvbn-ts/core':
|
||||
specifier: ^3.0.4
|
||||
version: 3.0.4
|
||||
ace-builds:
|
||||
specifier: ^1.43.2
|
||||
version: 1.43.2
|
||||
animate.css:
|
||||
specifier: ^4.1.1
|
||||
version: 4.1.1
|
||||
@@ -164,6 +167,9 @@ importers:
|
||||
vue-types:
|
||||
specifier: ^5.1.1
|
||||
version: 5.1.3(vue@3.5.12(typescript@5.3.3))
|
||||
vue3-ace-editor:
|
||||
specifier: ^2.2.4
|
||||
version: 2.2.4(ace-builds@1.43.2)(vue@3.5.12(typescript@5.3.3))
|
||||
vue3-signature:
|
||||
specifier: ^0.2.4
|
||||
version: 0.2.4(vue@3.5.12(typescript@5.3.3))
|
||||
@@ -2420,8 +2426,8 @@ packages:
|
||||
resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==, tarball: https://registry.npmmirror.com/JSONStream/-/JSONStream-1.3.5.tgz}
|
||||
hasBin: true
|
||||
|
||||
ace-builds@1.39.1:
|
||||
resolution: {integrity: sha512-HcJbBzx8qY66t9gZo/sQu7pi0wO/CFLdYn1LxQO1WQTfIkMfyc7LRnBpsp/oNCSSU/LL83jXHN1fqyOTuIhUjg==}
|
||||
ace-builds@1.43.2:
|
||||
resolution: {integrity: sha512-3wzJUJX0RpMc03jo0V8Q3bSb/cKPnS7Nqqw8fVHsCCHweKMiTIxT3fP46EhjmVy6MCuxwP801ere+RW245phGw==}
|
||||
|
||||
acorn-jsx@5.3.2:
|
||||
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, tarball: https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz}
|
||||
@@ -5159,6 +5165,12 @@ packages:
|
||||
vue:
|
||||
optional: true
|
||||
|
||||
vue3-ace-editor@2.2.4:
|
||||
resolution: {integrity: sha512-FZkEyfpbH068BwjhMyNROxfEI8135Sc+x8ouxkMdCNkuj/Tuw83VP/gStFQqZHqljyX9/VfMTCdTqtOnJZGN8g==}
|
||||
peerDependencies:
|
||||
ace-builds: '*'
|
||||
vue: ^3
|
||||
|
||||
vue3-signature@0.2.4:
|
||||
resolution: {integrity: sha512-XFwwFVK9OG3F085pKIq2SlNVqx32WdFH+TXbGEWc5FfEKpx8oMmZuAwZZ50K/pH2FgmJSE8IRwU9DDhrLpd6iA==}
|
||||
peerDependencies:
|
||||
@@ -7788,7 +7800,7 @@ snapshots:
|
||||
jsonparse: 1.3.1
|
||||
through: 2.3.8
|
||||
|
||||
ace-builds@1.39.1: {}
|
||||
ace-builds@1.43.2: {}
|
||||
|
||||
acorn-jsx@5.3.2(acorn@8.14.0):
|
||||
dependencies:
|
||||
@@ -9253,7 +9265,7 @@ snapshots:
|
||||
|
||||
jsoneditor@9.10.5:
|
||||
dependencies:
|
||||
ace-builds: 1.39.1
|
||||
ace-builds: 1.43.2
|
||||
ajv: 6.12.6
|
||||
javascript-natural-sort: 0.7.1
|
||||
jmespath: 0.16.0
|
||||
@@ -10675,6 +10687,12 @@ snapshots:
|
||||
optionalDependencies:
|
||||
vue: 3.5.12(typescript@5.3.3)
|
||||
|
||||
vue3-ace-editor@2.2.4(ace-builds@1.43.2)(vue@3.5.12(typescript@5.3.3)):
|
||||
dependencies:
|
||||
ace-builds: 1.43.2
|
||||
resize-observer-polyfill: 1.5.1
|
||||
vue: 3.5.12(typescript@5.3.3)
|
||||
|
||||
vue3-signature@0.2.4(vue@3.5.12(typescript@5.3.3)):
|
||||
dependencies:
|
||||
default-passive-events: 2.0.0
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
"core:window:allow-set-skip-taskbar",
|
||||
"core:webview:allow-set-webview-position",
|
||||
"core:webview:allow-set-webview-size",
|
||||
"core:window:allow-create"
|
||||
"core:window:allow-create",
|
||||
"core:event:allow-emit",
|
||||
"core:event:allow-listen",
|
||||
"core:event:allow-unlisten"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ watch(
|
||||
<div
|
||||
:id="prefixCls"
|
||||
:class="prefixCls"
|
||||
class="relative w-full flex bg-[#fff] dark:bg-[var(--el-bg-color)]"
|
||||
class="relative w-full h-[var(--tags-view-height)] flex bg-[#fff] dark:bg-[var(--el-bg-color)]"
|
||||
>
|
||||
<span
|
||||
:class="tagsViewImmerse ? '' : `${prefixCls}__tool ${prefixCls}__tool--first`"
|
||||
|
||||
@@ -45,9 +45,9 @@ export const useRenderLayout = () => {
|
||||
`${prefixCls}-content-scrollbar`,
|
||||
{
|
||||
'!h-[calc(100%-var(--top-tool-height)-var(--tags-view-height))] mt-[calc(var(--top-tool-height)+var(--tags-view-height))]':
|
||||
fixedHeader.value && tagsView.value,
|
||||
fixedHeader.value && (tagsView.value || !isDashboardPage.value),
|
||||
'!h-[calc(100%-var(--top-tool-height))] mt-[var(--top-tool-height)]':
|
||||
fixedHeader.value && !tagsView.value
|
||||
fixedHeader.value && !tagsView.value && isDashboardPage.value
|
||||
}
|
||||
]}
|
||||
>
|
||||
|
||||
@@ -746,6 +746,27 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
component: () => import('@/views/iot/plugin/detail/index.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
// 独立窗口路由
|
||||
{
|
||||
path: '/cdesign-independent',
|
||||
name: 'CdesignIndependent',
|
||||
component: () => import('@/views/cdesign/independent.vue'),
|
||||
meta: {
|
||||
title: '编程题独立窗口',
|
||||
hidden: true,
|
||||
noTagsView: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/excel-independent',
|
||||
name: 'ExcelIndependent',
|
||||
component: () => import('@/views/excel/independent.vue'),
|
||||
meta: {
|
||||
title: '表格题独立窗口',
|
||||
hidden: true,
|
||||
noTagsView: true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -35,3 +35,20 @@
|
||||
border-left-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
/* Layout border styles */
|
||||
.layout-border__top {
|
||||
border-top: 1px solid var(--el-border-color);
|
||||
}
|
||||
|
||||
.layout-border__bottom {
|
||||
border-bottom: 1px solid var(--el-border-color);
|
||||
}
|
||||
|
||||
.layout-border__left {
|
||||
border-left: 1px solid var(--el-border-color);
|
||||
}
|
||||
|
||||
.layout-border__right {
|
||||
border-right: 1px solid var(--el-border-color);
|
||||
}
|
||||
|
||||
53
src/utils/aceConfig.ts
Normal file
53
src/utils/aceConfig.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import ace from 'ace-builds'
|
||||
|
||||
// ACE 编辑器配置
|
||||
export const configureAce = () => {
|
||||
try {
|
||||
// 设置基础路径
|
||||
ace.config.set('basePath', '/node_modules/ace-builds/src-noconflict/')
|
||||
ace.config.set('workerPath', '/node_modules/ace-builds/src-noconflict/')
|
||||
ace.config.set('modePath', '/node_modules/ace-builds/src-noconflict/')
|
||||
ace.config.set('themePath', '/node_modules/ace-builds/src-noconflict/')
|
||||
|
||||
// 禁用严格的CSP模式来避免worker问题
|
||||
ace.config.set('useStrictCSP', true)
|
||||
|
||||
console.log('ACE Editor configured successfully')
|
||||
} catch (error) {
|
||||
console.warn('ACE Editor configuration warning:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 默认编辑器选项
|
||||
export const defaultAceOptions = {
|
||||
useWorker: false, // 禁用worker避免加载问题
|
||||
enableBasicAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true,
|
||||
fontSize: 14,
|
||||
showPrintMargin: false,
|
||||
highlightActiveLine: true,
|
||||
showGutter: true,
|
||||
wrap: true,
|
||||
autoScrollEditorIntoView: true
|
||||
}
|
||||
|
||||
// 初始化编辑器实例
|
||||
export const initAceEditor = (editor: any) => {
|
||||
try {
|
||||
// 禁用worker
|
||||
editor.session.setUseWorker(false)
|
||||
|
||||
// 设置样式
|
||||
editor.container.style.lineHeight = 1.5
|
||||
editor.renderer.updateFontSize()
|
||||
|
||||
// 设置其他选项
|
||||
editor.setShowPrintMargin(false)
|
||||
editor.setHighlightActiveLine(true)
|
||||
|
||||
console.log('ACE Editor instance initialized successfully')
|
||||
} catch (error) {
|
||||
console.warn('ACE Editor instance initialization warning:', error)
|
||||
}
|
||||
}
|
||||
49
src/views/cdesign/independent.vue
Normal file
49
src/views/cdesign/independent.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="independent-window">
|
||||
<CdesignForm ref="formRef" :isIndependent="true" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||
import CdesignForm from '@/views/paper/question/CdesignForm.vue'
|
||||
|
||||
defineOptions({ name: 'CdesignIndependent' })
|
||||
|
||||
const formRef = ref()
|
||||
|
||||
onMounted(async () => {
|
||||
console.log('CdesignIndependent mounted, sending ready signal...')
|
||||
|
||||
// 发送窗口准备就绪信号
|
||||
try {
|
||||
const { emit } = await import('@tauri-apps/api/event')
|
||||
await emit('cdesign-window-ready', { timestamp: Date.now() })
|
||||
console.log('Sent cdesign-window-ready event')
|
||||
} catch (error) {
|
||||
console.error('Failed to send ready signal:', error)
|
||||
}
|
||||
|
||||
// 监听表单提交成功事件,关闭窗口
|
||||
await listen('cdesign-form-success', async () => {
|
||||
const appWindow = getCurrentWindow()
|
||||
await appWindow.close()
|
||||
})
|
||||
|
||||
// 监听表单取消事件,关闭窗口
|
||||
await listen('cdesign-form-cancel', async () => {
|
||||
const appWindow = getCurrentWindow()
|
||||
await appWindow.close()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.independent-window {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
38
src/views/excel/independent.vue
Normal file
38
src/views/excel/independent.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div class="independent-window">
|
||||
<WpsXlsxForm ref="formRef" :isIndependent="true" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||
import WpsXlsxForm from '@/views/paper/question/WpsXlsxForm.vue'
|
||||
|
||||
defineOptions({ name: 'ExcelIndependent' })
|
||||
|
||||
const formRef = ref()
|
||||
|
||||
onMounted(async () => {
|
||||
// 监听表单提交成功事件,关闭窗口
|
||||
await listen('excel-form-success', async () => {
|
||||
const appWindow = getCurrentWindow()
|
||||
await appWindow.close()
|
||||
})
|
||||
|
||||
// 监听表单取消事件,关闭窗口
|
||||
await listen('excel-form-cancel', async () => {
|
||||
const appWindow = getCurrentWindow()
|
||||
await appWindow.close()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.independent-window {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
|
||||
89
src/views/paper/question/components/CodeEditor.vue
Normal file
89
src/views/paper/question/components/CodeEditor.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<v-ace-editor
|
||||
v-model:value="content"
|
||||
@init="editorInit"
|
||||
lang="c_cpp"
|
||||
theme="tomorrow_night"
|
||||
:style="{ height: editorHeight }"
|
||||
:options="{
|
||||
useWorker: true,
|
||||
enableBasicAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true,
|
||||
fontSize: 14,
|
||||
showPrintMargin: false,
|
||||
highlightActiveLine: true,
|
||||
showGutter: true
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed, defineEmits, defineProps, defineExpose } from 'vue'
|
||||
import { VAceEditor } from 'vue3-ace-editor'
|
||||
// 引入需要的语言模式、主题和扩展
|
||||
import 'ace-builds/src-noconflict/mode-c_cpp'
|
||||
import 'ace-builds/src-noconflict/theme-tomorrow_night'
|
||||
import 'ace-builds/src-noconflict/ext-language_tools'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
rows: {
|
||||
type: Number,
|
||||
default: 8 // 默认行数
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const content = ref(props.modelValue)
|
||||
let editorInstance: any = null
|
||||
|
||||
// 根据行数计算编辑器高度
|
||||
const editorHeight = computed(() => {
|
||||
// 假设每行大约 21px
|
||||
return `${props.rows * 21}px`
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
if (content.value !== newValue) {
|
||||
content.value = newValue
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
watch(content, (newValue) => {
|
||||
emit('update:modelValue', newValue)
|
||||
})
|
||||
|
||||
const editorInit = (editor) => {
|
||||
editorInstance = editor
|
||||
editor.container.style.lineHeight = 1.5
|
||||
editor.renderer.updateFontSize()
|
||||
}
|
||||
|
||||
// 定义获取选中内容的方法
|
||||
const getSelectedText = () => {
|
||||
if (editorInstance) {
|
||||
return editorInstance.getSelectedText()
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// 向父组件暴露方法
|
||||
defineExpose({
|
||||
getSelectedText
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ace_editor {
|
||||
border: 1px solid #444;
|
||||
border-radius: 7px;
|
||||
}
|
||||
</style>
|
||||
@@ -369,8 +369,10 @@ import SpecialtyTree from './SpecialtyTree.vue'
|
||||
import { handleTree } from '@/utils/tree'
|
||||
import * as SpecialtyApi from '@/api/points'
|
||||
import { useSettingStore } from '@/store/modules/settings'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { TaskStatusEnum } from '@/api/bpm/task'
|
||||
const settingStore = useSettingStore()
|
||||
const appStore = useAppStore()
|
||||
defineOptions({ name: 'SystemUser' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
@@ -746,15 +748,165 @@ const setformRef = ref()
|
||||
const emailformRef = ref()
|
||||
const xlsxformRef = ref()
|
||||
const psformRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
const openForm = async (type: string, id?: number) => {
|
||||
console.log(queryParams)
|
||||
if (queryParams.subjectName == '') {
|
||||
return message.confirm('请选择题型!')
|
||||
}
|
||||
if (chooseQuestionType.value.includes('选择题')) {
|
||||
|
||||
// 对编程题和表格题使用独立窗口
|
||||
if (chooseQuestionType.value.includes('编程题')) {
|
||||
try {
|
||||
const { WebviewWindow } = await import('@tauri-apps/api/webviewWindow')
|
||||
const windowLabel = `cdesign-form-${Date.now()}`
|
||||
|
||||
const webview = new WebviewWindow(windowLabel, {
|
||||
url: '#/cdesign-independent',
|
||||
title: type === 'create' ? '新增编程题' : '修改编程题',
|
||||
width: 1200,
|
||||
height: 800,
|
||||
resizable: true,
|
||||
center: true
|
||||
})
|
||||
|
||||
// 监听窗口加载完成,然后传递数据
|
||||
webview.once('tauri://created', () => {
|
||||
console.log('Webview created, setting up data transfer...')
|
||||
// 延迟发送事件,确保组件完全挂载
|
||||
setTimeout(() => {
|
||||
console.log('Sending init-cdesign-form event with data:', { queryParams, type, id })
|
||||
import('@tauri-apps/api/event').then(({ emit }) => {
|
||||
emit('init-cdesign-form', {
|
||||
queryParams,
|
||||
type,
|
||||
id,
|
||||
theme: appStore.getTheme // 传入当前主题
|
||||
})
|
||||
.then(() => {
|
||||
console.log('init-cdesign-form event sent successfully')
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to send init-cdesign-form event:', error)
|
||||
})
|
||||
})
|
||||
}, 500) // 增加延迟到500ms
|
||||
|
||||
// 多次尝试发送事件,确保成功
|
||||
setTimeout(() => {
|
||||
console.log('Second attempt: Sending init-cdesign-form event')
|
||||
import('@tauri-apps/api/event').then(({ emit }) => {
|
||||
emit('init-cdesign-form', {
|
||||
queryParams,
|
||||
type,
|
||||
id,
|
||||
theme: appStore.getTheme
|
||||
})
|
||||
})
|
||||
}, 1000)
|
||||
|
||||
// 第三次尝试
|
||||
setTimeout(() => {
|
||||
console.log('Third attempt: Sending init-cdesign-form event')
|
||||
import('@tauri-apps/api/event').then(({ emit }) => {
|
||||
emit('init-cdesign-form', {
|
||||
queryParams,
|
||||
type,
|
||||
id,
|
||||
theme: appStore.getTheme
|
||||
})
|
||||
})
|
||||
}, 2000)
|
||||
})
|
||||
|
||||
// 监听窗口准备就绪信号
|
||||
const eventModule = await import('@tauri-apps/api/event')
|
||||
const unlistenReady = await eventModule.listen('cdesign-window-ready', () => {
|
||||
console.log('Received cdesign-window-ready, sending init data...')
|
||||
eventModule
|
||||
.emit('init-cdesign-form', {
|
||||
queryParams,
|
||||
type,
|
||||
id,
|
||||
theme: appStore.getTheme
|
||||
})
|
||||
.then(() => {
|
||||
console.log('Data sent in response to window ready signal')
|
||||
})
|
||||
})
|
||||
|
||||
// 监听组件请求初始化数据的事件
|
||||
const unlistenRequest = await eventModule.listen('request-cdesign-init', (event) => {
|
||||
console.log('Received request-cdesign-init from component:', event.payload)
|
||||
eventModule
|
||||
.emit('init-cdesign-form', {
|
||||
queryParams,
|
||||
type,
|
||||
id,
|
||||
theme: appStore.getTheme
|
||||
})
|
||||
.then(() => {
|
||||
console.log('Data sent in response to component request')
|
||||
})
|
||||
})
|
||||
|
||||
// 清理监听器
|
||||
setTimeout(() => {
|
||||
unlistenReady()
|
||||
unlistenRequest()
|
||||
}, 10000) // 10秒后清理监听器
|
||||
|
||||
// 监听表单提交成功事件
|
||||
eventModule.listen('cdesign-form-success', () => {
|
||||
getList() // 刷新列表
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to open independent window:', error)
|
||||
// 降级到对话框模式
|
||||
cformRef.value.open(queryParams, type, id)
|
||||
}
|
||||
} else if (chooseQuestionType.value.includes('表格')) {
|
||||
try {
|
||||
const { WebviewWindow } = await import('@tauri-apps/api/webviewWindow')
|
||||
const windowLabel = `excel-form-${Date.now()}`
|
||||
|
||||
const webview = new WebviewWindow(windowLabel, {
|
||||
url: '#/excel-independent',
|
||||
title: type === 'create' ? '新增表格题' : '修改表格题',
|
||||
width: 1200,
|
||||
height: 800,
|
||||
resizable: true,
|
||||
center: true
|
||||
})
|
||||
|
||||
// 监听窗口加载完成,然后传递数据
|
||||
webview.once('tauri://created', () => {
|
||||
// 延迟发送事件,确保组件完全挂载
|
||||
setTimeout(() => {
|
||||
import('@tauri-apps/api/event').then(({ emit }) => {
|
||||
emit('init-excel-form', {
|
||||
queryParams,
|
||||
type,
|
||||
id,
|
||||
theme: appStore.getTheme // 传入当前主题
|
||||
})
|
||||
})
|
||||
}, 100)
|
||||
})
|
||||
|
||||
// 监听表单提交成功事件
|
||||
const { listen } = await import('@tauri-apps/api/event')
|
||||
listen('excel-form-success', () => {
|
||||
getList() // 刷新列表
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to open independent window:', error)
|
||||
// 降级到对话框模式
|
||||
xlsxformRef.value.open(queryParams, type, id)
|
||||
}
|
||||
}
|
||||
// 其他题型继续使用对话框模式
|
||||
else if (chooseQuestionType.value.includes('选择题')) {
|
||||
formRef.value.open(queryParams, type, id)
|
||||
} else if (chooseQuestionType.value.includes('编程题')) {
|
||||
cformRef.value.open(queryParams, type, id)
|
||||
} else if (chooseQuestionType.value.includes('程序设计')) {
|
||||
mformRef.value.open(queryParams, type, id)
|
||||
} else if (chooseQuestionType.value.includes('网络题')) {
|
||||
@@ -769,8 +921,6 @@ const openForm = (type: string, id?: number) => {
|
||||
setformRef.value.open(queryParams, type, id)
|
||||
} else if (chooseQuestionType.value.includes('邮箱')) {
|
||||
emailformRef.value.open(queryParams, type, id)
|
||||
} else if (chooseQuestionType.value.includes('表格')) {
|
||||
xlsxformRef.value.open(queryParams, type, id)
|
||||
} else if (chooseQuestionType.value.includes('图像处理')) {
|
||||
psformRef.value.open(queryParams, type, id)
|
||||
}
|
||||
|
||||
0
src/views/test/design-test.vue
Normal file
0
src/views/test/design-test.vue
Normal file
@@ -127,7 +127,7 @@ const openIndependentWindow = async (row: any) => {
|
||||
try {
|
||||
// 直接使用 newWindow 方法创建独立窗口
|
||||
await newWindow(`excel-edit-${row.id}`, {
|
||||
url: `/wps/xlsx?id=${row.id}&mode=edit`,
|
||||
url: `/xlsx-independent?id=${row.id}&mode=edit&name=${encodeURIComponent(row.name)}`,
|
||||
title: `编辑 Excel - ${row.name}`,
|
||||
width: 900,
|
||||
height: 650,
|
||||
|
||||
Reference in New Issue
Block a user