吉安感知网项目-前端
罗广辉
2026-07-09 0e99c5c11085bedc83d727c21060252ccbf743e2
feat: 在线编辑功能
8 files modified
2 files added
223 ■■■■ changed files
AGENTS.md 38 ●●●●● patch | view | raw | blame | history
applications/task-work-order/env/.env 4 ●●●● patch | view | raw | blame | history
applications/task-work-order/package.json 1 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/api/documentPreview/index.js 9 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/permission.js 18 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/router/views/index.js 12 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/documentPreview/index.vue 100 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionReport/index.vue 24 ●●●● patch | view | raw | blame | history
pnpm-lock.yaml 16 ●●●●● patch | view | raw | blame | history
pnpm-workspace.yaml 1 ●●●● patch | view | raw | blame | history
AGENTS.md
@@ -12,45 +12,13 @@
  - `uniapps/work-wx/`:使用 UniApp 开发的微信小程序。
- 资源文件通常放在各应用内,具体入口以各应用的 `README.md` 为准。
## 构建、测试与开发命令
在仓库根目录使用 `pnpm`:
- `pnpm install`:安装工作区依赖。
- `pnpm dev`:并行启动所有工作区的开发服务。
- `pnpm dev:drone-command`:启动单个应用。
- `pnpm dev:mobile-web-view`:启动单个应用。
- `pnpm dev:task-work-order`:启动单个应用。
- `pnpm dev:work-app`:启动 UniApp H5 开发服务。
- `pnpm dev:work-wx`:启动 UniApp H5 开发服务。
- `pnpm build:test:<app>` / `pnpm build:prod:<app>`:构建指定应用,例如 `pnpm build:prod:mobile-web-view`。
## 编码风格与命名规范
- 使用 Prettier 统一格式化(`.prettierrc.json`)。
- 使用 Tab 缩进(`tabWidth: 2`, `useTabs: true`),`semi: false`,`singleQuote: true`。
- SCSS 使用 `tabWidth: 4` 且双引号。
- 文件与目录保持 kebabCase,遵循现有应用与包的命名风格。
## 测试指南
- 无需任何测试
## 提交与合并请求规范
- 提交历史使用简短的约定式主题(如 `feat: ...`)。
- 建议使用 `feat:`、`fix:`、`chore:`、`refactor:` 等前缀并保持简洁描述。
- PR 需要包含:目的、影响的应用/包、UI 改动截图。
- 可关联相关 issue 或工单。
## 环境与配置
- Node.js >= 20,pnpm >= 9。
- 优先使用根目录工作区脚本,除非必要不要在子目录直接运行脚本。
# AI Rules / AI行为规范
- 对低风险修改,AI可自主执行,无需批准。
  AI may proceed autonomously for low-risk changes.
- 仅当修改可能影响架构、核心模块、公共接口、数据结构或安全性时,才需要请求批准。
  Ask for approval only for high-impact changes.
- 若问题不影响系统正确性,应自行做出合理决策,而不是提问。
  DO NOT ask questions that do not affect correctness.
- 优先最小化修改范围。
  Prefer minimal changes.
- 未被明确要求时,禁止大规模重构。
  Avoid large refactors unless requested.
- 优先最小化修改范围
- 未被明确要求时,禁止大规模重构
- 每个方法前面加一行简短中文注释
- 修改完不需要走打包编译校验
applications/task-work-order/env/.env
@@ -24,6 +24,10 @@
# 预览地址 previewURL
VITE_APP_PREVIEW_URL=http://220.177.172.27:8100/kkfile
# OnlyOffice 文档服务地址
VITE_APP_ONLYOFFICE_DOCUMENT_SERVER_URL=http://192.168.1.54:8089/
#地形服务地址
VITE_APP_TERRAIN_URL=http://220.177.172.27:8100/ztzf_terrain/
applications/task-work-order/package.json
@@ -13,6 +13,7 @@
        "@amap/amap-jsapi-loader": "catalog:",
        "@dvgis/dc-sdk": "catalog:",
        "@element-plus/icons-vue": "catalog:",
        "@onlyoffice/document-editor-vue": "catalog:",
        "@saber/nf-design-base-elp": "catalog:",
        "@saber/nf-form-design-elp": "catalog:",
        "@saber/nf-form-elp": "catalog:",
applications/task-work-order/src/api/documentPreview/index.js
New file
@@ -0,0 +1,9 @@
import request from '@/axios'
export const getOnlyOfficeConfigApi = params => {
    return request({
        url: '/blade-resource/attach/getOnlyOfficeConfig',
        method: 'get',
        params,
    })
}
applications/task-work-order/src/permission.js
@@ -67,14 +67,16 @@
          //   query: firstMenu.query || {},
          //   meta: firstMenu.meta || {},
          // })
          store.commit('ADD_TAG', {
            name: toMenu.name,
            path: toMenu.path,
            fullPath: toMenu.path,
            params: toMenu.params || {},
            query: toMenu.query || {},
            meta: toMenu.meta || {},
          })
          if (toMenu && to.meta?.isTab !== false) {
            store.commit('ADD_TAG', {
              name: toMenu.name,
              path: toMenu.path,
              fullPath: toMenu.path,
              params: toMenu.params || {},
              query: toMenu.query || {},
              meta: toMenu.meta || {},
            })
          }
          next(to.fullPath)
        }
applications/task-work-order/src/router/views/index.js
@@ -22,6 +22,18 @@
      }
    ],
  },
    {
        path: '/documentPreview/officeEdit',
        name: '巡查报告在线编辑',
        component: () =>
            import(
                /* webpackChunkName: "views" */ '@/views/documentPreview/index.vue'
            ),
        meta: {
            isTab: false,
            menu: false,
        },
    },
  {
    path: '/test',
    component: Layout,
applications/task-work-order/src/views/documentPreview/index.vue
New file
@@ -0,0 +1,100 @@
<template>
    <div class="document-preview-page" v-loading="loading">
        <DocumentEditor
            v-if="config"
            id="inspectionReportOfficeEditor"
            :documentServerUrl="documentServerUrl"
            :config="config"
            height="100%"
            width="100%"
            :events_onAppReady="onAppReady"
            :events_onDocumentReady="onDocumentReady"
            :events_onDocumentStateChange="onDocumentStateChange"
            :events_onWarning="onWarning"
            :events_onError="onError"
            :onLoadComponentError="onLoadComponentError"
        />
        <el-empty v-else-if="!loading" :description="errorMessage || '缺少文档配置'" />
    </div>
</template>
<script setup>
import { computed, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { DocumentEditor } from '@onlyoffice/document-editor-vue'
import { getOnlyOfficeConfigApi } from '@/api/documentPreview'
const route = useRoute()
const documentServerUrl = import.meta.env.VITE_APP_ONLYOFFICE_DOCUMENT_SERVER_URL
const attachId = computed(() => String(route.query.attachId || ''))
const config = ref(null)
const loading = ref(false)
const errorMessage = ref('')
async function getOnlyOfficeConfig() {
    if (!attachId.value) {
        config.value = null
        errorMessage.value = '缺少附件ID'
        return
    }
    loading.value = true
    errorMessage.value = ''
    try {
        const res = await getOnlyOfficeConfigApi({
            attachId: attachId.value,
        })
        const data = res?.data?.data || res?.data
        if (!data?.document || !data?.editorConfig) {
            throw new Error('OnlyOffice配置不完整')
        }
        config.value = data
    } catch (error) {
        console.error('获取OnlyOffice配置失败:', error)
        config.value = null
        errorMessage.value = '获取文档配置失败'
        ElMessage.error(error?.message || '获取文档配置失败')
    } finally {
        loading.value = false
    }
}
watch(attachId, getOnlyOfficeConfig, {
    immediate: true,
})
function onAppReady(event) {
    console.log('OnlyOffice app ready:', event)
}
function onDocumentReady(event) {
    console.log('OnlyOffice document ready:', event)
}
function onDocumentStateChange(event) {
    console.log('OnlyOffice document state change:', event)
}
function onWarning(event) {
    console.warn('OnlyOffice warning:', event)
}
function onError(event) {
    console.error('OnlyOffice error:', event)
}
function onLoadComponentError(errorCode, errorDescription) {
    console.error('OnlyOffice load error:', errorCode, errorDescription)
}
</script>
<style scoped lang="scss">
.document-preview-page {
    height: 100vh;
    min-height: 0;
    width: 100vw;
    overflow: hidden;
}
</style>
applications/task-work-order/src/views/orderView/orderManage/inspectionReport/index.vue
@@ -57,7 +57,7 @@
                    <el-table-column label="操作" class-name="operation-btns">
                        <template v-slot="{ row }">
                            <el-link  type="primary"  @click="seeOnlineWord(row)">查看</el-link>
                            <el-link  type="primary"  @click="openForm(row)">在线编辑</el-link>
                            <el-link  type="primary"  @click="openOfficeEdit(row)">在线编辑</el-link>
                            <el-link  type="primary"  @click="handleDelete(row)">删除</el-link>
                        </template>
                    </el-table-column>
@@ -135,8 +135,8 @@
<script setup>
import { Search, RefreshRight, Download, Upload } from '@element-plus/icons-vue'
import { fjPageApi, fjSubmitApi, fjRemoveApi, fjUploadApi, fjDownloadByByteApi } from './inspectionRequestApi'
import { useStore } from 'vuex'
import { ref, computed, onMounted } from 'vue'
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import PreviewFiles from '@/components/PreviewFiles/PreviewFiles.vue'
import { Base64 } from 'js-base64'
@@ -147,8 +147,7 @@
    taskTypeCascaderProps,
} from '../taskTypeOptions'
const store = useStore()
const userInfo = computed(() => store.getters.userInfo)
const router = useRouter()
@@ -291,6 +290,21 @@
    }
}
function openOfficeEdit(row) {
    if (!row?.id) {
        ElMessage.warning('附件ID不存在')
        return
    }
    const route = router.resolve({
        path: '/documentPreview/officeEdit',
        query: {
            attachId: row.id,
        },
    })
    window.open(route.href, '_blank')
}
// 删除
async function handleDelete(row) {
    const tips = row ? '该条' : '选中的项'
pnpm-lock.yaml
@@ -18,6 +18,9 @@
    '@element-plus/icons-vue':
      specifier: ^2.3.1
      version: 2.3.2
    '@onlyoffice/document-editor-vue':
      specifier: ^1.6.1
      version: 1.6.1
    '@saber/nf-design-base-elp':
      specifier: ^1.3.0
      version: 1.3.0
@@ -640,6 +643,9 @@
      '@element-plus/icons-vue':
        specifier: 'catalog:'
        version: 2.3.2(vue@3.5.27(typescript@5.9.3))
      '@onlyoffice/document-editor-vue':
        specifier: 'catalog:'
        version: 1.6.1(vue@3.5.27(typescript@5.9.3))
      '@saber/nf-design-base-elp':
        specifier: 'catalog:'
        version: 1.3.0
@@ -2796,6 +2802,11 @@
  '@nuxt/kit@3.20.2':
    resolution: {integrity: sha512-laqfmMcWWNV1FsVmm1+RQUoGY8NIJvCRl0z0K8ikqPukoEry0LXMqlQ+xaf8xJRvoH2/78OhZmsEEsUBTXipcw==}
    engines: {node: '>=18.12.0'}
  '@onlyoffice/document-editor-vue@1.6.1':
    resolution: {integrity: sha512-sdU7h684ESSdXvGNDcMf73UmToiZGMVO5QRIazTmGfm+bKOnT5ildomeagYFdnQaHQH0J28EJqc4jqXOcQbicA==}
    peerDependencies:
      vue: ^3.0.0
  '@parcel/watcher-android-arm64@2.5.4':
    resolution: {integrity: sha512-hoh0vx4v+b3BNI7Cjoy2/B0ARqcwVNrzN/n7DLq9ZB4I3lrsvhrkCViJyfTj/Qi5xM9YFiH4AmHGK6pgH1ss7g==}
@@ -12004,6 +12015,11 @@
    transitivePeerDependencies:
      - magicast
  '@onlyoffice/document-editor-vue@1.6.1(vue@3.5.27(typescript@5.9.3))':
    dependencies:
      lodash: 4.17.21
      vue: 3.5.27(typescript@5.9.3)
  '@parcel/watcher-android-arm64@2.5.4':
    optional: true
pnpm-workspace.yaml
@@ -8,6 +8,7 @@
  "@dvgis/dc-sdk": "3.4.0"
  "@dvgis/vite-plugin-dc": "2.2.0"
  "@element-plus/icons-vue": "^2.3.1"
  "@onlyoffice/document-editor-vue": "^1.6.1"
  "@saber/nf-design-base-elp": "^1.3.0"
  "@saber/nf-form-design-elp": "^1.4.1"
  "@saber/nf-form-elp": "^1.4.4"