326 lines
11 KiB
Vue
326 lines
11 KiB
Vue
<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>
|