Files
ruoyi-vue/src/views/jarvis/kdocs/index.vue
2026-03-23 23:56:45 +08:00

326 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<el-card>
<div slot="header" class="clearfix">
<span>金山文档 在线表格</span>
<el-button style="float: right; padding: 3px 0" type="text" @click="handleRefresh" :loading="loading">刷新</el-button>
</div>
<el-alert v-if="!isAuthorized" title="未授权" type="warning" :closable="false" show-icon>
<template slot="default">
<span>请完成金山文档开放平台授权个人云</span>
<el-button type="primary" size="small" style="margin-left: 10px" @click="handleAuthorize">立即授权</el-button>
</template>
</el-alert>
<el-alert v-else title="已授权" type="success" :closable="false" show-icon>
<template slot="default">
<span>授权状态正常</span>
</template>
</el-alert>
<el-card v-if="isAuthorized && userInfo" style="margin-top: 20px">
<div slot="header">用户信息</div>
<el-descriptions :column="2" border>
<el-descriptions-item label="open_id">{{ userInfo.user_id || userInfo.open_id || '-' }}</el-descriptions-item>
<el-descriptions-item label="昵称">{{ userInfo.nickname || userInfo.name || '-' }}</el-descriptions-item>
</el-descriptions>
</el-card>
<el-card v-if="isAuthorized" style="margin-top: 20px">
<div slot="header">
<span>文件列表个人云扁平列表</span>
<el-button type="primary" size="small" style="float: right" @click="handleLoadFiles(true)">加载 / 刷新</el-button>
</div>
<p v-if="listHint" class="list-hint">{{ listHint }}</p>
<el-table v-loading="fileListLoading" :data="fileList" border style="width: 100%">
<el-table-column prop="file_name" label="文件名" min-width="200" />
<el-table-column prop="file_token" label="file_token" min-width="260" show-overflow-tooltip />
<el-table-column prop="file_type" label="类型" width="100" />
<el-table-column label="操作" width="200">
<template slot-scope="scope">
<el-button type="success" size="mini" @click="handleEditFile(scope.row)">编辑表格</el-button>
</template>
</el-table-column>
</el-table>
<div v-if="hasMoreFiles" style="margin-top: 12px">
<el-button type="default" size="small" :loading="fileListLoading" @click="handleLoadMore">加载更多</el-button>
</div>
</el-card>
<el-dialog title="编辑表格" :visible.sync="editDialogVisible" width="80%" :close-on-click-modal="false">
<div v-if="currentFile">
<el-form :inline="true" class="demo-form-inline">
<el-form-item label="工作表">
<el-select v-model="currentSheetIdx" placeholder="请选择" @change="handleLoadSheetData">
<el-option
v-for="sheet in sheetList"
:key="sheet.sheet_idx + '-' + sheet.name"
:label="sheet.name + ' (idx:' + sheet.sheet_idx + ')'"
:value="sheet.sheet_idx"
/>
</el-select>
</el-form-item>
<el-form-item label="单元格范围">
<el-input v-model="cellRange" placeholder="例如A1:B10" style="width: 200px" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleLoadSheetData">读取数据</el-button>
<el-button type="success" @click="handleUpdateCells">保存数据</el-button>
</el-form-item>
</el-form>
<el-table v-loading="sheetDataLoading" :data="sheetData" border style="width: 100%; margin-top: 20px">
<el-table-column
v-for="(col, index) in sheetColumns"
:key="index"
:prop="'col' + index"
:label="getColumnLabel(index)"
min-width="120"
>
<template slot-scope="scope">
<el-input v-model="scope.row['col' + index]" size="small" />
</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
</el-card>
</div>
</template>
<script>
import {
getKdocsAuthUrl,
getKdocsTokenStatus,
getKdocsUserInfo,
getKdocsFileList,
getKdocsSheetList,
readKdocsCells,
updateKdocsCells
} from '@/api/jarvis/kdocs'
export default {
name: 'KdocsCloud',
data() {
return {
loading: false,
isAuthorized: false,
userInfo: null,
userId: 'default_user',
fileList: [],
fileListLoading: false,
listHint: '',
hasMoreFiles: false,
nextOffset: null,
nextFilter: null,
editDialogVisible: false,
currentFile: null,
currentSheetIdx: 0,
sheetList: [],
sheetData: [],
sheetDataLoading: false,
cellRange: 'A1:Z100',
sheetColumns: []
}
},
created() {
this.checkAuthStatus()
},
methods: {
async checkAuthStatus() {
try {
const response = await getKdocsTokenStatus(this.userId)
if (response.code === 200) {
this.isAuthorized = !!response.data.hasToken
if (this.isAuthorized) {
this.loadUserInfo()
}
}
} catch (e) {
console.error(e)
}
},
async loadUserInfo() {
try {
const response = await getKdocsUserInfo(this.userId)
if (response.code === 200) {
this.userInfo = response.data
}
} catch (e) {
console.error(e)
}
},
async handleAuthorize() {
try {
const response = await getKdocsAuthUrl()
if (response.code === 200) {
window.open(response.data, '_blank')
this.$message.success('请在新窗口完成授权,完成后回到本页刷新')
}
} catch (e) {
this.$message.error(e.msg || e.message || '获取授权地址失败')
}
},
handleRefresh() {
this.checkAuthStatus()
if (this.isAuthorized) {
this.handleLoadFiles(true)
}
},
/** @param {boolean} reset 是否重置游标从第一页拉取 */
async handleLoadFiles(reset) {
if (!this.isAuthorized) {
this.$message.warning('请先授权')
return
}
if (reset) {
this.nextOffset = null
this.nextFilter = null
this.fileList = []
}
this.fileListLoading = true
this.listHint = ''
try {
const params = {
userId: this.userId,
page: 1,
pageSize: 50
}
if (this.nextOffset != null) {
params.next_offset = this.nextOffset
}
if (this.nextFilter) {
params.next_filter = this.nextFilter
}
const response = await getKdocsFileList(params)
if (response.code === 200) {
const chunk = response.data.files || []
this.fileList = reset ? chunk : this.fileList.concat(chunk)
this.nextOffset = response.data.next_offset
this.nextFilter = response.data.next_filter || null
this.hasMoreFiles = !!response.data.has_more
this.listHint =
'file_token 请使用列表中的值(来自文档 open_id。在线表格工作表请选 sheet_type 为 et 的 sheet数据表为 db 时需用数据表 API。'
}
} catch (e) {
this.$message.error(e.msg || e.message || '加载失败')
} finally {
this.fileListLoading = false
}
},
handleLoadMore() {
if (!this.hasMoreFiles) return
this.handleLoadFiles(false)
},
async handleEditFile(file) {
this.currentFile = file
this.editDialogVisible = true
try {
const response = await getKdocsSheetList(this.userId, file.file_token)
if (response.code === 200) {
this.sheetList = response.data.sheets || []
if (this.sheetList.length > 0) {
const first = this.sheetList[0]
this.currentSheetIdx = first.sheet_idx != null ? first.sheet_idx : 0
this.handleLoadSheetData()
} else {
this.$message.warning('未获取到工作表列表')
}
}
} catch (e) {
this.$message.error(e.msg || e.message || '加载工作表失败')
}
},
extractCellValues(payload) {
if (!payload) return []
if (Array.isArray(payload.values)) return payload.values
if (payload.data && Array.isArray(payload.data.values)) return payload.data.values
return []
},
async handleLoadSheetData() {
if (!this.currentFile) return
this.sheetDataLoading = true
try {
const response = await readKdocsCells({
userId: this.userId,
fileToken: this.currentFile.file_token,
sheetIdx: this.currentSheetIdx,
range: this.cellRange
})
if (response.code === 200) {
this.processSheetData(this.extractCellValues(response.data))
}
} catch (e) {
this.$message.error(e.msg || e.message || '读取失败')
} finally {
this.sheetDataLoading = false
}
},
processSheetData(values) {
if (!values || values.length === 0) {
this.sheetData = []
this.sheetColumns = []
return
}
const maxCols = Math.max(...values.map(row => (row ? row.length : 0)))
this.sheetColumns = Array.from({ length: maxCols }, (_, i) => i)
this.sheetData = values.map(row => {
const rowData = {}
const r = row || []
r.forEach((cell, index) => {
rowData['col' + index] = cell !== null && cell !== undefined ? String(cell) : ''
})
for (let i = r.length; i < maxCols; i++) {
rowData['col' + i] = ''
}
return rowData
})
},
getColumnLabel(index) {
let label = ''
let num = index
while (num >= 0) {
label = String.fromCharCode(65 + (num % 26)) + label
num = Math.floor(num / 26) - 1
}
return label
},
async handleUpdateCells() {
if (!this.currentFile) return
const values = this.sheetData.map(row => {
return this.sheetColumns.map(colIndex => {
const v = row['col' + colIndex]
return v !== undefined ? v : ''
})
})
try {
const response = await updateKdocsCells({
userId: this.userId,
fileToken: this.currentFile.file_token,
sheetIdx: this.currentSheetIdx,
range: this.cellRange,
values
})
if (response.code === 200) {
this.$message.success('保存成功')
}
} catch (e) {
this.$message.error(e.msg || e.message || '保存失败')
}
}
}
}
</script>
<style scoped>
.app-container {
padding: 20px;
}
.list-hint {
color: #909399;
font-size: 13px;
margin-bottom: 10px;
}
</style>