1
This commit is contained in:
@@ -110,9 +110,9 @@ server {
|
|||||||
client_max_body_size 100M;
|
client_max_body_size 100M;
|
||||||
}
|
}
|
||||||
|
|
||||||
# WPS365 OAuth回调接口(必须放在 /jarvis-api/ 之后,location / 之前)
|
# 金山文档 OAuth 回调(须放在 /jarvis-api/ 之后,location / 之前)
|
||||||
location /wps365-callback {
|
location /kdocs-callback {
|
||||||
proxy_pass http://127.0.0.1:30313/wps365-callback;
|
proxy_pass http://127.0.0.1:30313/kdocs-callback;
|
||||||
|
|
||||||
# 请求头设置
|
# 请求头设置
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
|
|||||||
97
src/api/jarvis/kdocs.js
Normal file
97
src/api/jarvis/kdocs.js
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function getKdocsAuthUrl(state) {
|
||||||
|
return request({
|
||||||
|
url: '/jarvis/kdocs/authUrl',
|
||||||
|
method: 'get',
|
||||||
|
params: state ? { state } : {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getKdocsTokenStatus(userId) {
|
||||||
|
return request({
|
||||||
|
url: '/jarvis/kdocs/tokenStatus',
|
||||||
|
method: 'get',
|
||||||
|
params: { userId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function refreshKdocsToken(data) {
|
||||||
|
return request({
|
||||||
|
url: '/jarvis/kdocs/refreshToken',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setKdocsToken(data) {
|
||||||
|
return request({
|
||||||
|
url: '/jarvis/kdocs/setToken',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getKdocsUserInfo(userId) {
|
||||||
|
return request({
|
||||||
|
url: '/jarvis/kdocs/userInfo',
|
||||||
|
method: 'get',
|
||||||
|
params: { userId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getKdocsFileList(params) {
|
||||||
|
return request({
|
||||||
|
url: '/jarvis/kdocs/files',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getKdocsFileInfo(userId, fileToken) {
|
||||||
|
return request({
|
||||||
|
url: '/jarvis/kdocs/fileInfo',
|
||||||
|
method: 'get',
|
||||||
|
params: { userId, fileToken }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getKdocsSheetList(userId, fileToken) {
|
||||||
|
return request({
|
||||||
|
url: '/jarvis/kdocs/sheets',
|
||||||
|
method: 'get',
|
||||||
|
params: { userId, fileToken }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createKdocsSheet(data) {
|
||||||
|
return request({
|
||||||
|
url: '/jarvis/kdocs/createSheet',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function readKdocsCells(params) {
|
||||||
|
return request({
|
||||||
|
url: '/jarvis/kdocs/readCells',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateKdocsCells(data) {
|
||||||
|
return request({
|
||||||
|
url: '/jarvis/kdocs/updateCells',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function batchUpdateKdocsCells(data) {
|
||||||
|
return request({
|
||||||
|
url: '/jarvis/kdocs/batchUpdateCells',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
import request from '@/utils/request'
|
|
||||||
|
|
||||||
// ==================== OAuth授权相关 ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取WPS365授权URL
|
|
||||||
*/
|
|
||||||
export function getWPS365AuthUrl(state) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/authUrl',
|
|
||||||
method: 'get',
|
|
||||||
params: { state }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OAuth回调获取访问令牌
|
|
||||||
*/
|
|
||||||
export function getWPS365AccessToken(code) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/oauth/callback',
|
|
||||||
method: 'get',
|
|
||||||
params: { code }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新访问令牌
|
|
||||||
*/
|
|
||||||
export function refreshWPS365Token(data) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/refreshToken',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取token状态
|
|
||||||
*/
|
|
||||||
export function getWPS365TokenStatus(userId) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/tokenStatus',
|
|
||||||
method: 'get',
|
|
||||||
params: { userId }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置token(用于手动授权)
|
|
||||||
*/
|
|
||||||
export function setWPS365Token(data) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/setToken',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== 用户信息相关 ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取用户信息
|
|
||||||
*/
|
|
||||||
export function getWPS365UserInfo(userId) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/userInfo',
|
|
||||||
method: 'get',
|
|
||||||
params: { userId }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== 文件相关 ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取文件列表
|
|
||||||
*/
|
|
||||||
export function getWPS365FileList(params) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/files',
|
|
||||||
method: 'get',
|
|
||||||
params
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取文件信息
|
|
||||||
*/
|
|
||||||
export function getWPS365FileInfo(userId, fileToken) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/fileInfo',
|
|
||||||
method: 'get',
|
|
||||||
params: { userId, fileToken }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== 工作表相关 ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取工作表列表
|
|
||||||
*/
|
|
||||||
export function getWPS365SheetList(userId, fileToken) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/sheets',
|
|
||||||
method: 'get',
|
|
||||||
params: { userId, fileToken }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建数据表
|
|
||||||
*/
|
|
||||||
export function createWPS365Sheet(data) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/createSheet',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== 单元格操作相关 ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取单元格数据
|
|
||||||
*/
|
|
||||||
export function readWPS365Cells(params) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/readCells',
|
|
||||||
method: 'get',
|
|
||||||
params
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新单元格数据
|
|
||||||
*/
|
|
||||||
export function updateWPS365Cells(data) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/updateCells',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量更新单元格数据
|
|
||||||
*/
|
|
||||||
export function batchUpdateWPS365Cells(data) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/batchUpdateCells',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== AirSheet相关 ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取AirSheet工作表数据
|
|
||||||
* @param {Object} params - {userId, fileId, worksheetId(可选,默认0), range(可选)}
|
|
||||||
*/
|
|
||||||
export function readWPS365AirSheetCells(params) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/readAirSheetCells',
|
|
||||||
method: 'get',
|
|
||||||
params
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新AirSheet工作表数据
|
|
||||||
* @param {Object} data - {userId, fileId, worksheetId(可选,默认0), range, values}
|
|
||||||
*/
|
|
||||||
export function updateWPS365AirSheetCells(data) {
|
|
||||||
return request({
|
|
||||||
url: '/jarvis/wps365/updateAirSheetCells',
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ import { isRelogin } from '@/utils/request'
|
|||||||
|
|
||||||
NProgress.configure({ showSpinner: false })
|
NProgress.configure({ showSpinner: false })
|
||||||
|
|
||||||
const whiteList = ['/login', '/register', '/public/home', '/tools/comment-gen', '/tools/order-search', '/public/order-submit', '/wps365-callback', '/tendoc-callback']
|
const whiteList = ['/login', '/register', '/public/home', '/tools/comment-gen', '/tools/order-search', '/public/order-submit', '/kdocs-callback', '/tendoc-callback']
|
||||||
|
|
||||||
const isWhiteList = (path) => {
|
const isWhiteList = (path) => {
|
||||||
return whiteList.some(pattern => isPathMatch(pattern, path))
|
return whiteList.some(pattern => isPathMatch(pattern, path))
|
||||||
|
|||||||
@@ -253,10 +253,10 @@ export const dynamicRoutes = [
|
|||||||
meta: { title: '文档同步配置', icon: 'document' }
|
meta: { title: '文档同步配置', icon: 'document' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'wps365',
|
path: 'kdocs',
|
||||||
component: () => import('@/views/jarvis/wps365/index'),
|
component: () => import('@/views/jarvis/kdocs/index'),
|
||||||
name: 'WPS365Manage',
|
name: 'KdocsCloudManage',
|
||||||
meta: { title: 'WPS365 在线表格管理', icon: 'document' }
|
meta: { title: '金山文档 在线表格', icon: 'document' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
444
src/views/jarvis/docSync/components/KdocsCloudConfig.vue
Normal file
444
src/views/jarvis/docSync/components/KdocsCloudConfig.vue
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
<template>
|
||||||
|
<div class="kdocs-config">
|
||||||
|
<div class="config-container">
|
||||||
|
<div class="config-left">
|
||||||
|
<el-card class="config-section">
|
||||||
|
<div slot="header" class="section-header">
|
||||||
|
<i class="el-icon-key"></i>
|
||||||
|
<span>授权状态</span>
|
||||||
|
</div>
|
||||||
|
<div class="auth-status">
|
||||||
|
<el-tag v-if="isAuthorized" type="success" size="medium">
|
||||||
|
<i class="el-icon-circle-check"></i> 已授权
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-else type="danger" size="medium">
|
||||||
|
<i class="el-icon-circle-close"></i> 未授权
|
||||||
|
</el-tag>
|
||||||
|
<el-button
|
||||||
|
v-if="!isAuthorized"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-unlock"
|
||||||
|
@click="handleAuthorize"
|
||||||
|
style="margin-left: 10px;"
|
||||||
|
>立即授权</el-button>
|
||||||
|
<el-button
|
||||||
|
v-else
|
||||||
|
type="info"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-refresh"
|
||||||
|
@click="handleRefreshAuth"
|
||||||
|
style="margin-left: 10px;"
|
||||||
|
>刷新状态</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-if="isAuthorized && tokenInfo" class="token-info" style="margin-top: 15px;">
|
||||||
|
<el-descriptions :column="2" border size="small">
|
||||||
|
<el-descriptions-item label="用户ID">{{ tokenInfo.userId || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="Token状态">
|
||||||
|
<el-tag v-if="tokenInfo.isValid" type="success" size="small">有效</el-tag>
|
||||||
|
<el-tag v-else type="warning" size="small">已过期</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if="tokenInfo.expiresIn" label="有效期">
|
||||||
|
{{ Math.floor(tokenInfo.expiresIn / 60) }} 分钟
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="config-section">
|
||||||
|
<div slot="header" class="section-header">
|
||||||
|
<i class="el-icon-document"></i>
|
||||||
|
<span>H-TF订单自动写入配置</span>
|
||||||
|
<el-tag v-if="config.isConfigured" type="success" size="mini" style="margin-left: 10px;">已配置</el-tag>
|
||||||
|
<el-tag v-else type="warning" size="mini" style="margin-left: 10px;">未配置</el-tag>
|
||||||
|
</div>
|
||||||
|
<el-form ref="form" :model="form" :rules="rules" label-width="120px" size="small">
|
||||||
|
<el-form-item label="file_token" prop="fileId">
|
||||||
|
<el-input
|
||||||
|
v-model="form.fileId"
|
||||||
|
placeholder="个人云文档列表接口返回的 file_token(文档 open_id)"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
slot="append"
|
||||||
|
icon="el-icon-search"
|
||||||
|
:disabled="!form.fileId || !isAuthorized"
|
||||||
|
@click="handleTestRead"
|
||||||
|
>测试读取</el-button>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="工作表 sheet_idx">
|
||||||
|
<el-input-number v-model="form.sheetIdx" :min="0" controls-position="right" style="width: 100%;" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="表头行号" prop="headerRow">
|
||||||
|
<el-input-number v-model="form.headerRow" :min="1" controls-position="right" style="width: 100%;" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="起始行号" prop="startRow">
|
||||||
|
<el-input-number v-model="form.startRow" :min="1" controls-position="right" style="width: 100%;" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :loading="saveLoading" icon="el-icon-check" @click="handleSave">保存配置</el-button>
|
||||||
|
<el-button :loading="testLoading" icon="el-icon-setting" @click="handleTest">测试配置</el-button>
|
||||||
|
<el-button type="danger" plain :loading="clearLoading" icon="el-icon-delete" @click="handleClear">清除配置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="config-right">
|
||||||
|
<el-card class="status-card-wrapper">
|
||||||
|
<div class="status-card" :class="config.isConfigured ? 'success' : 'warning'">
|
||||||
|
<div class="status-icon" :class="config.isConfigured ? 'success' : 'warning'">
|
||||||
|
<i :class="config.isConfigured ? 'el-icon-success' : 'el-icon-warning'"></i>
|
||||||
|
</div>
|
||||||
|
<div class="status-text">
|
||||||
|
<div class="status-title">{{ config.isConfigured ? '配置完成' : '配置未完成' }}</div>
|
||||||
|
<div class="status-desc">
|
||||||
|
{{ config.hint || (config.isConfigured ? 'H-TF订单将写入金山文档在线表格(工作表)' : '请完成配置') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
<el-card class="help-card-wrapper">
|
||||||
|
<div slot="header" class="card-header">
|
||||||
|
<i class="el-icon-question"></i>
|
||||||
|
<span>说明</span>
|
||||||
|
</div>
|
||||||
|
<div class="help-content">
|
||||||
|
<div class="help-item"><i class="el-icon-check"></i><span>file_token 来自「在线表格管理」文件列表或开放平台文档列表</span></div>
|
||||||
|
<div class="help-item"><i class="el-icon-check"></i><span>工作表读写使用 KSheet 单元格接口,sheet_idx 与后台 sheet 一致</span></div>
|
||||||
|
<div class="help-item"><i class="el-icon-check"></i><span>数据表(db)需改用数据表记录类 API,本页为工作表(et)场景</span></div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
getKdocsAuthUrl,
|
||||||
|
getKdocsTokenStatus,
|
||||||
|
readKdocsCells,
|
||||||
|
updateKdocsCells
|
||||||
|
} from '@/api/jarvis/kdocs'
|
||||||
|
|
||||||
|
const LS_KEY = 'kdocs_auto_write_config'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'KdocsCloudConfig',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isAuthorized: false,
|
||||||
|
userId: 'default_user',
|
||||||
|
tokenInfo: null,
|
||||||
|
config: { isConfigured: false, hint: '' },
|
||||||
|
form: {
|
||||||
|
fileId: '',
|
||||||
|
sheetIdx: 0,
|
||||||
|
headerRow: 2,
|
||||||
|
startRow: 3
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
fileId: [{ required: true, message: '请输入 file_token', trigger: 'blur' }],
|
||||||
|
headerRow: [
|
||||||
|
{ required: true, message: '请输入表头行号', trigger: 'blur' },
|
||||||
|
{ type: 'number', min: 1, message: '表头行号必须大于0', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
startRow: [
|
||||||
|
{ required: true, message: '请输入数据起始行', trigger: 'blur' },
|
||||||
|
{ type: 'number', min: 1, message: '数据起始行必须大于0', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
saveLoading: false,
|
||||||
|
testLoading: false,
|
||||||
|
clearLoading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.checkAuthStatus()
|
||||||
|
this.loadConfig()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
refresh() {
|
||||||
|
this.checkAuthStatus()
|
||||||
|
this.loadConfig()
|
||||||
|
},
|
||||||
|
async checkAuthStatus() {
|
||||||
|
try {
|
||||||
|
const response = await getKdocsTokenStatus(this.userId)
|
||||||
|
if (response.code === 200) {
|
||||||
|
this.isAuthorized = response.data.hasToken && response.data.isValid
|
||||||
|
this.tokenInfo = response.data
|
||||||
|
if (response.data.userId) {
|
||||||
|
this.userId = response.data.userId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loadConfig() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(LS_KEY)
|
||||||
|
if (raw) {
|
||||||
|
const c = JSON.parse(raw)
|
||||||
|
this.form.fileId = c.fileId || ''
|
||||||
|
this.form.sheetIdx = c.sheetIdx != null ? c.sheetIdx : 0
|
||||||
|
this.form.headerRow = c.headerRow || 2
|
||||||
|
this.form.startRow = c.startRow || 3
|
||||||
|
this.config.isConfigured = !!c.fileId
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extractValues(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 handleAuthorize() {
|
||||||
|
try {
|
||||||
|
const response = await getKdocsAuthUrl()
|
||||||
|
if (response.code === 200) {
|
||||||
|
const w = 600
|
||||||
|
const h = 700
|
||||||
|
window.open(
|
||||||
|
response.data,
|
||||||
|
'KdocsAuth',
|
||||||
|
`width=${w},height=${h},left=${(screen.width - w) / 2},top=${(screen.height - h) / 2},resizable=yes,scrollbars=yes`
|
||||||
|
)
|
||||||
|
this.$message.success('请在弹出窗口完成授权')
|
||||||
|
const handler = (event) => {
|
||||||
|
if (event.data && event.data.type === 'kdocs_oauth_callback') {
|
||||||
|
window.removeEventListener('message', handler)
|
||||||
|
if (event.data.userId) {
|
||||||
|
this.userId = event.data.userId
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
this.checkAuthStatus()
|
||||||
|
this.$message.success('授权完成')
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.addEventListener('message', handler)
|
||||||
|
setTimeout(() => this.checkAuthStatus(), 3000)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.$message.error(e.msg || e.message || '获取授权地址失败')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async handleRefreshAuth() {
|
||||||
|
await this.checkAuthStatus()
|
||||||
|
this.$message.success('已刷新')
|
||||||
|
},
|
||||||
|
async handleTestRead() {
|
||||||
|
if (!this.isAuthorized) {
|
||||||
|
this.$message.warning('请先授权')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!this.form.fileId) {
|
||||||
|
this.$message.warning('请输入 file_token')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await readKdocsCells({
|
||||||
|
userId: this.userId,
|
||||||
|
fileToken: this.form.fileId,
|
||||||
|
sheetIdx: this.form.sheetIdx,
|
||||||
|
range: 'A1:B5'
|
||||||
|
})
|
||||||
|
if (response.code === 200) {
|
||||||
|
this.$message.success('读取成功')
|
||||||
|
console.log('read', response.data)
|
||||||
|
} else {
|
||||||
|
this.$message.warning(response.msg || '读取失败')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.$message.error(e.msg || e.message || '读取失败')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleSave() {
|
||||||
|
this.$refs.form.validate(async (valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
if (!this.isAuthorized) {
|
||||||
|
this.$message.warning('请先授权')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.saveLoading = true
|
||||||
|
try {
|
||||||
|
localStorage.setItem(
|
||||||
|
LS_KEY,
|
||||||
|
JSON.stringify({
|
||||||
|
fileId: this.form.fileId,
|
||||||
|
sheetIdx: this.form.sheetIdx,
|
||||||
|
headerRow: this.form.headerRow,
|
||||||
|
startRow: this.form.startRow
|
||||||
|
})
|
||||||
|
)
|
||||||
|
this.config.isConfigured = true
|
||||||
|
this.config.hint = '将使用金山文档 KSheet 写入工作表'
|
||||||
|
this.$message.success('已保存')
|
||||||
|
} finally {
|
||||||
|
this.saveLoading = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleTest() {
|
||||||
|
if (!this.isAuthorized) {
|
||||||
|
this.$message.warning('请先授权')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$refs.form.validate(async (valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
this.testLoading = true
|
||||||
|
try {
|
||||||
|
const readRes = await readKdocsCells({
|
||||||
|
userId: this.userId,
|
||||||
|
fileToken: this.form.fileId,
|
||||||
|
sheetIdx: this.form.sheetIdx,
|
||||||
|
range: 'A1:B5'
|
||||||
|
})
|
||||||
|
if (readRes.code !== 200) {
|
||||||
|
this.$message.error(readRes.msg || '读失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const testRange = `A${this.form.startRow}:B${this.form.startRow}`
|
||||||
|
const writeRes = await updateKdocsCells({
|
||||||
|
userId: this.userId,
|
||||||
|
fileToken: this.form.fileId,
|
||||||
|
sheetIdx: this.form.sheetIdx,
|
||||||
|
range: testRange,
|
||||||
|
values: [['测试1', '测试2']]
|
||||||
|
})
|
||||||
|
if (writeRes.code === 200) {
|
||||||
|
this.$message.success('读写测试成功')
|
||||||
|
} else {
|
||||||
|
this.$message.warning(writeRes.msg || '写失败')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.$message.error(e.msg || e.message || '测试失败')
|
||||||
|
} finally {
|
||||||
|
this.testLoading = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleClear() {
|
||||||
|
this.$confirm('确定清除本地配置?', '提示', { type: 'warning' })
|
||||||
|
.then(() => {
|
||||||
|
localStorage.removeItem(LS_KEY)
|
||||||
|
this.form.fileId = ''
|
||||||
|
this.form.sheetIdx = 0
|
||||||
|
this.form.headerRow = 2
|
||||||
|
this.form.startRow = 3
|
||||||
|
this.config.isConfigured = false
|
||||||
|
this.config.hint = ''
|
||||||
|
this.$message.success('已清除')
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.kdocs-config {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.config-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
.config-left {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.config-right {
|
||||||
|
width: 300px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.config-section {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.auth-status {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.status-card-wrapper {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.status-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.status-card.success {
|
||||||
|
background-color: #f0f9ff;
|
||||||
|
border: 1px solid #b3d8ff;
|
||||||
|
}
|
||||||
|
.status-card.warning {
|
||||||
|
background-color: #fef0f0;
|
||||||
|
border: 1px solid #fbc4c4;
|
||||||
|
}
|
||||||
|
.status-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 20px;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
.status-icon.success {
|
||||||
|
background-color: #67c23a;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.status-icon.warning {
|
||||||
|
background-color: #e6a23c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.status-text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.status-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.status-desc {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.help-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
.help-item i {
|
||||||
|
color: #67c23a;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,563 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="wps365-config">
|
|
||||||
<div class="config-container">
|
|
||||||
<!-- 左侧:配置表单 -->
|
|
||||||
<div class="config-left">
|
|
||||||
<!-- 授权状态 -->
|
|
||||||
<el-card class="config-section">
|
|
||||||
<div slot="header" class="section-header">
|
|
||||||
<i class="el-icon-key"></i>
|
|
||||||
<span>授权状态</span>
|
|
||||||
</div>
|
|
||||||
<div class="auth-status">
|
|
||||||
<el-tag v-if="isAuthorized" type="success" size="medium">
|
|
||||||
<i class="el-icon-circle-check"></i> 已授权
|
|
||||||
</el-tag>
|
|
||||||
<el-tag v-else type="danger" size="medium">
|
|
||||||
<i class="el-icon-circle-close"></i> 未授权
|
|
||||||
</el-tag>
|
|
||||||
<el-button
|
|
||||||
v-if="!isAuthorized"
|
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
icon="el-icon-unlock"
|
|
||||||
@click="handleAuthorize"
|
|
||||||
style="margin-left: 10px;"
|
|
||||||
>
|
|
||||||
立即授权
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-else
|
|
||||||
type="info"
|
|
||||||
size="small"
|
|
||||||
icon="el-icon-refresh"
|
|
||||||
@click="handleRefreshAuth"
|
|
||||||
style="margin-left: 10px;"
|
|
||||||
>
|
|
||||||
刷新状态
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
<div v-if="isAuthorized && tokenInfo" class="token-info" style="margin-top: 15px;">
|
|
||||||
<el-descriptions :column="2" border size="small">
|
|
||||||
<el-descriptions-item label="用户ID">{{ tokenInfo.userId || '-' }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="Token状态">
|
|
||||||
<el-tag v-if="tokenInfo.isValid" type="success" size="small">有效</el-tag>
|
|
||||||
<el-tag v-else type="warning" size="small">已过期</el-tag>
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="有效期" v-if="tokenInfo.expiresIn">
|
|
||||||
{{ Math.floor(tokenInfo.expiresIn / 60) }} 分钟
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<!-- 文档配置表单 -->
|
|
||||||
<el-card class="config-section">
|
|
||||||
<div slot="header" class="section-header">
|
|
||||||
<i class="el-icon-document"></i>
|
|
||||||
<span>H-TF订单自动写入配置</span>
|
|
||||||
<el-tag v-if="config.isConfigured" type="success" size="mini" style="margin-left: 10px;">
|
|
||||||
<i class="el-icon-success"></i> 已配置
|
|
||||||
</el-tag>
|
|
||||||
<el-tag v-else type="warning" size="mini" style="margin-left: 10px;">
|
|
||||||
<i class="el-icon-warning"></i> 未配置
|
|
||||||
</el-tag>
|
|
||||||
</div>
|
|
||||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px" size="small">
|
|
||||||
<el-form-item label="文件ID" prop="fileId">
|
|
||||||
<el-input
|
|
||||||
v-model="form.fileId"
|
|
||||||
placeholder="例如:VbHZwButmh(从WPS365在线表格URL中获取)"
|
|
||||||
clearable
|
|
||||||
>
|
|
||||||
<el-button
|
|
||||||
slot="append"
|
|
||||||
icon="el-icon-search"
|
|
||||||
@click="handleTestRead"
|
|
||||||
:disabled="!form.fileId || !isAuthorized"
|
|
||||||
>
|
|
||||||
测试读取
|
|
||||||
</el-button>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-row :gutter="10">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="表头行号" prop="headerRow">
|
|
||||||
<el-input-number
|
|
||||||
v-model="form.headerRow"
|
|
||||||
:min="1"
|
|
||||||
controls-position="right"
|
|
||||||
style="width: 100%;"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="起始行号" prop="startRow">
|
|
||||||
<el-input-number
|
|
||||||
v-model="form.startRow"
|
|
||||||
:min="1"
|
|
||||||
controls-position="right"
|
|
||||||
style="width: 100%;"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-button type="primary" @click="handleSave" :loading="saveLoading" icon="el-icon-check">
|
|
||||||
保存配置
|
|
||||||
</el-button>
|
|
||||||
<el-button @click="handleTest" :loading="testLoading" icon="el-icon-setting">
|
|
||||||
测试配置
|
|
||||||
</el-button>
|
|
||||||
<el-button @click="handleClear" :loading="clearLoading" type="danger" plain icon="el-icon-delete">
|
|
||||||
清除配置
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 右侧:状态信息 -->
|
|
||||||
<div class="config-right">
|
|
||||||
<!-- 配置状态提示 -->
|
|
||||||
<el-card class="status-card-wrapper">
|
|
||||||
<div class="status-card" :class="config.isConfigured ? 'success' : 'warning'">
|
|
||||||
<div class="status-icon" :class="config.isConfigured ? 'success' : 'warning'">
|
|
||||||
<i :class="config.isConfigured ? 'el-icon-success' : 'el-icon-warning'"></i>
|
|
||||||
</div>
|
|
||||||
<div class="status-text">
|
|
||||||
<div class="status-title">{{ config.isConfigured ? '配置完成' : '配置未完成' }}</div>
|
|
||||||
<div class="status-desc">{{ config.hint || (config.isConfigured ? 'H-TF订单将自动写入WPS365在线表格' : '请完成配置') }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<!-- 快速帮助 -->
|
|
||||||
<el-card class="help-card-wrapper">
|
|
||||||
<div slot="header" class="card-header">
|
|
||||||
<i class="el-icon-question"></i>
|
|
||||||
<span>配置说明</span>
|
|
||||||
</div>
|
|
||||||
<div class="help-content">
|
|
||||||
<div class="help-item">
|
|
||||||
<i class="el-icon-check"></i>
|
|
||||||
<span>文件ID从WPS365在线表格URL中获取</span>
|
|
||||||
</div>
|
|
||||||
<div class="help-item">
|
|
||||||
<i class="el-icon-check"></i>
|
|
||||||
<span>点击"测试读取"验证文件ID是否正确</span>
|
|
||||||
</div>
|
|
||||||
<div class="help-item">
|
|
||||||
<i class="el-icon-check"></i>
|
|
||||||
<span>表头行号默认为第2行</span>
|
|
||||||
</div>
|
|
||||||
<div class="help-item">
|
|
||||||
<i class="el-icon-check"></i>
|
|
||||||
<span>数据起始行默认为第3行</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import {
|
|
||||||
getWPS365AuthUrl,
|
|
||||||
getWPS365TokenStatus,
|
|
||||||
readWPS365AirSheetCells,
|
|
||||||
updateWPS365AirSheetCells
|
|
||||||
} from '@/api/jarvis/wps365'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'WPS365Config',
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isAuthorized: false,
|
|
||||||
userId: null,
|
|
||||||
tokenInfo: null,
|
|
||||||
config: {
|
|
||||||
isConfigured: false,
|
|
||||||
hint: ''
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
fileId: '',
|
|
||||||
headerRow: 2,
|
|
||||||
startRow: 3
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
fileId: [
|
|
||||||
{ required: true, message: '请输入文件ID', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
headerRow: [
|
|
||||||
{ required: true, message: '请输入表头行号', trigger: 'blur' },
|
|
||||||
{ type: 'number', min: 1, message: '表头行号必须大于0', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
startRow: [
|
|
||||||
{ required: true, message: '请输入数据起始行', trigger: 'blur' },
|
|
||||||
{ type: 'number', min: 1, message: '数据起始行必须大于0', trigger: 'blur' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
saveLoading: false,
|
|
||||||
testLoading: false,
|
|
||||||
clearLoading: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.checkAuthStatus()
|
|
||||||
this.loadConfig()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
/** 刷新配置 */
|
|
||||||
refresh() {
|
|
||||||
this.checkAuthStatus()
|
|
||||||
this.loadConfig()
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 检查授权状态 */
|
|
||||||
async checkAuthStatus() {
|
|
||||||
try {
|
|
||||||
const response = await getWPS365TokenStatus(this.userId)
|
|
||||||
if (response.code === 200) {
|
|
||||||
this.isAuthorized = response.data.hasToken && response.data.isValid
|
|
||||||
this.tokenInfo = response.data
|
|
||||||
if (response.data.userId) {
|
|
||||||
this.userId = response.data.userId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('检查授权状态失败', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 加载配置 */
|
|
||||||
async loadConfig() {
|
|
||||||
try {
|
|
||||||
// TODO: 从后端加载配置
|
|
||||||
// 暂时从localStorage读取
|
|
||||||
const savedConfig = localStorage.getItem('wps365_auto_write_config')
|
|
||||||
if (savedConfig) {
|
|
||||||
const config = JSON.parse(savedConfig)
|
|
||||||
this.form.fileId = config.fileId || ''
|
|
||||||
this.form.headerRow = config.headerRow || 2
|
|
||||||
this.form.startRow = config.startRow || 3
|
|
||||||
this.config.isConfigured = !!(config.fileId)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载配置失败', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 处理授权 */
|
|
||||||
async handleAuthorize() {
|
|
||||||
try {
|
|
||||||
const response = await getWPS365AuthUrl()
|
|
||||||
if (response.code === 200) {
|
|
||||||
const width = 600
|
|
||||||
const height = 700
|
|
||||||
const left = (window.screen.width - width) / 2
|
|
||||||
const top = (window.screen.height - height) / 2
|
|
||||||
|
|
||||||
window.open(
|
|
||||||
response.data,
|
|
||||||
'WPS365授权',
|
|
||||||
`width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`
|
|
||||||
)
|
|
||||||
|
|
||||||
this.$message.success('授权页面已打开,请在新窗口中完成授权')
|
|
||||||
|
|
||||||
const messageHandler = (event) => {
|
|
||||||
if (event.data && event.data.type === 'wps365_oauth_callback') {
|
|
||||||
window.removeEventListener('message', messageHandler)
|
|
||||||
if (event.data.userId) {
|
|
||||||
this.userId = event.data.userId
|
|
||||||
}
|
|
||||||
setTimeout(() => {
|
|
||||||
this.checkAuthStatus()
|
|
||||||
this.$message.success('授权完成')
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
window.addEventListener('message', messageHandler)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.checkAuthStatus()
|
|
||||||
}, 3000)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.$message.error('获取授权URL失败:' + (error.msg || error.message))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 刷新授权状态 */
|
|
||||||
async handleRefreshAuth() {
|
|
||||||
await this.checkAuthStatus()
|
|
||||||
this.$message.success('授权状态已刷新')
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 测试读取 */
|
|
||||||
async handleTestRead() {
|
|
||||||
if (!this.isAuthorized || !this.userId) {
|
|
||||||
this.$message.warning('请先完成授权')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!this.form.fileId) {
|
|
||||||
this.$message.warning('请输入文件ID')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$message.info('正在测试读取...')
|
|
||||||
try {
|
|
||||||
const response = await readWPS365AirSheetCells({
|
|
||||||
userId: this.userId,
|
|
||||||
fileId: this.form.fileId,
|
|
||||||
worksheetId: '0', // AirSheet中worksheetId通常是整数,0表示第一个工作表
|
|
||||||
range: 'A1:B5'
|
|
||||||
})
|
|
||||||
if (response.code === 200) {
|
|
||||||
this.$message.success('读取成功!文件ID正确。')
|
|
||||||
console.log('读取结果:', response.data)
|
|
||||||
} else {
|
|
||||||
this.$message.warning('读取失败: ' + (response.msg || '未知错误'))
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.$message.error('读取失败: ' + (error.msg || error.message))
|
|
||||||
console.error('读取错误:', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 保存配置 */
|
|
||||||
async handleSave() {
|
|
||||||
this.$refs.form.validate(async (valid) => {
|
|
||||||
if (!valid) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (!this.isAuthorized) {
|
|
||||||
this.$message.warning('请先完成授权')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.saveLoading = true
|
|
||||||
try {
|
|
||||||
// TODO: 保存到后端
|
|
||||||
// 暂时保存到localStorage
|
|
||||||
const config = {
|
|
||||||
fileId: this.form.fileId,
|
|
||||||
headerRow: this.form.headerRow,
|
|
||||||
startRow: this.form.startRow
|
|
||||||
}
|
|
||||||
localStorage.setItem('wps365_auto_write_config', JSON.stringify(config))
|
|
||||||
this.config.isConfigured = true
|
|
||||||
this.config.hint = 'H-TF订单将自动写入WPS365在线表格'
|
|
||||||
this.$message.success('配置保存成功')
|
|
||||||
} catch (error) {
|
|
||||||
this.$message.error('保存配置失败: ' + (error.msg || error.message))
|
|
||||||
} finally {
|
|
||||||
this.saveLoading = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 测试配置 */
|
|
||||||
async handleTest() {
|
|
||||||
if (!this.isAuthorized || !this.userId) {
|
|
||||||
this.$message.warning('请先完成授权')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.$refs.form.validate(async (valid) => {
|
|
||||||
if (!valid) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
this.testLoading = true
|
|
||||||
try {
|
|
||||||
// 测试读取
|
|
||||||
const readResponse = await readWPS365AirSheetCells({
|
|
||||||
userId: this.userId,
|
|
||||||
fileId: this.form.fileId,
|
|
||||||
worksheetId: '0', // AirSheet中worksheetId通常是整数,0表示第一个工作表
|
|
||||||
range: 'A1:B5'
|
|
||||||
})
|
|
||||||
if (readResponse.code !== 200) {
|
|
||||||
this.$message.error('读取测试失败: ' + (readResponse.msg || '未知错误'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 测试写入(写入测试数据)
|
|
||||||
const testRange = `A${this.form.startRow}:B${this.form.startRow}`
|
|
||||||
const testValues = [['测试数据1', '测试数据2']]
|
|
||||||
const writeResponse = await updateWPS365AirSheetCells({
|
|
||||||
userId: this.userId,
|
|
||||||
fileId: this.form.fileId,
|
|
||||||
worksheetId: '0', // AirSheet中worksheetId通常是整数,0表示第一个工作表
|
|
||||||
range: testRange,
|
|
||||||
values: testValues
|
|
||||||
})
|
|
||||||
if (writeResponse.code === 200) {
|
|
||||||
this.$message.success('配置测试成功!读写功能正常。')
|
|
||||||
} else {
|
|
||||||
this.$message.warning('写入测试失败: ' + (writeResponse.msg || '未知错误'))
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.$message.error('配置测试失败: ' + (error.msg || error.message))
|
|
||||||
console.error('测试错误:', error)
|
|
||||||
} finally {
|
|
||||||
this.testLoading = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 清除配置 */
|
|
||||||
async handleClear() {
|
|
||||||
this.$confirm('确定要清除配置吗?', '提示', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning'
|
|
||||||
}).then(() => {
|
|
||||||
this.clearLoading = true
|
|
||||||
try {
|
|
||||||
// TODO: 清除后端配置
|
|
||||||
localStorage.removeItem('wps365_auto_write_config')
|
|
||||||
this.form.fileId = ''
|
|
||||||
this.form.headerRow = 2
|
|
||||||
this.form.startRow = 3
|
|
||||||
this.config.isConfigured = false
|
|
||||||
this.config.hint = ''
|
|
||||||
this.$message.success('配置已清除')
|
|
||||||
} catch (error) {
|
|
||||||
this.$message.error('清除配置失败: ' + (error.msg || error.message))
|
|
||||||
} finally {
|
|
||||||
this.clearLoading = false
|
|
||||||
}
|
|
||||||
}).catch(() => {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.wps365-config {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-container {
|
|
||||||
display: flex;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-left {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-right {
|
|
||||||
width: 300px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-section {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-status {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token-info {
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-card-wrapper {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-card {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-card.success {
|
|
||||||
background-color: #f0f9ff;
|
|
||||||
border: 1px solid #b3d8ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-card.warning {
|
|
||||||
background-color: #fef0f0;
|
|
||||||
border: 1px solid #fbc4c4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-icon {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 20px;
|
|
||||||
margin-right: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-icon.success {
|
|
||||||
background-color: #67c23a;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-icon.warning {
|
|
||||||
background-color: #e6a23c;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-text {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-title {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-desc {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #909399;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-card-wrapper {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-content {
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-size: 13px;
|
|
||||||
color: #606266;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-item i {
|
|
||||||
color: #67c23a;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -16,11 +16,11 @@
|
|||||||
<TencentDocConfig ref="tendocConfig" />
|
<TencentDocConfig ref="tendocConfig" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="WPS365" name="wps365">
|
<el-tab-pane label="金山文档" name="kdocs">
|
||||||
<span slot="label">
|
<span slot="label">
|
||||||
<i class="el-icon-document-copy"></i> WPS365
|
<i class="el-icon-document-copy"></i> 金山文档
|
||||||
</span>
|
</span>
|
||||||
<WPS365Config ref="wps365Config" />
|
<KdocsCloudConfig ref="kdocsConfig" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</el-card>
|
</el-card>
|
||||||
@@ -29,13 +29,13 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TencentDocConfig from './components/TencentDocConfig'
|
import TencentDocConfig from './components/TencentDocConfig'
|
||||||
import WPS365Config from './components/WPS365Config'
|
import KdocsCloudConfig from './components/KdocsCloudConfig'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DocSync',
|
name: 'DocSync',
|
||||||
components: {
|
components: {
|
||||||
TencentDocConfig,
|
TencentDocConfig,
|
||||||
WPS365Config
|
KdocsCloudConfig
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -48,8 +48,8 @@ export default {
|
|||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (tab.name === 'tendoc' && this.$refs.tendocConfig) {
|
if (tab.name === 'tendoc' && this.$refs.tendocConfig) {
|
||||||
this.$refs.tendocConfig.refresh()
|
this.$refs.tendocConfig.refresh()
|
||||||
} else if (tab.name === 'wps365' && this.$refs.wps365Config) {
|
} else if (tab.name === 'kdocs' && this.$refs.kdocsConfig) {
|
||||||
this.$refs.wps365Config.refresh()
|
this.$refs.kdocsConfig.refresh()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
325
src/views/jarvis/kdocs/index.vue
Normal file
325
src/views/jarvis/kdocs/index.vue
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
<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 && response.data.isValid
|
||||||
|
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>
|
||||||
@@ -1,453 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<el-card>
|
|
||||||
<div slot="header" class="clearfix">
|
|
||||||
<span>WPS365 在线表格管理</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>请先完成WPS365授权,才能使用文档编辑功能</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>
|
|
||||||
<el-button
|
|
||||||
type="danger"
|
|
||||||
size="small"
|
|
||||||
style="margin-left: 10px"
|
|
||||||
@click="handleRefreshToken">
|
|
||||||
刷新Token
|
|
||||||
</el-button>
|
|
||||||
</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="用户ID">{{ userInfo.user_id || '-' }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="用户名">{{ userInfo.name || '-' }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="邮箱">{{ userInfo.email || '-' }}</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">
|
|
||||||
加载文件
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
<el-table
|
|
||||||
v-loading="fileListLoading"
|
|
||||||
:data="fileList"
|
|
||||||
border
|
|
||||||
style="width: 100%">
|
|
||||||
<el-table-column prop="file_name" label="文件名" width="200" />
|
|
||||||
<el-table-column prop="file_token" label="文件Token" width="250" />
|
|
||||||
<el-table-column prop="file_type" label="类型" width="100" />
|
|
||||||
<el-table-column label="操作" width="200">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
size="mini"
|
|
||||||
@click="handleViewFile(scope.row)">
|
|
||||||
查看
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
type="success"
|
|
||||||
size="mini"
|
|
||||||
@click="handleEditFile(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<pagination
|
|
||||||
v-show="fileTotal > 0"
|
|
||||||
:total="fileTotal"
|
|
||||||
:page.sync="queryParams.page"
|
|
||||||
:limit.sync="queryParams.pageSize"
|
|
||||||
@pagination="handleLoadFiles"
|
|
||||||
/>
|
|
||||||
</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, index) in sheetList"
|
|
||||||
:key="index"
|
|
||||||
:label="sheet.name"
|
|
||||||
:value="index">
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="单元格范围">
|
|
||||||
<el-input
|
|
||||||
v-model="cellRange"
|
|
||||||
placeholder="例如:A1:B10"
|
|
||||||
style="width: 200px">
|
|
||||||
</el-input>
|
|
||||||
</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">
|
|
||||||
</el-input>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import {
|
|
||||||
getWPS365AuthUrl,
|
|
||||||
getWPS365TokenStatus,
|
|
||||||
getWPS365UserInfo,
|
|
||||||
getWPS365FileList,
|
|
||||||
getWPS365SheetList,
|
|
||||||
readWPS365Cells,
|
|
||||||
updateWPS365Cells,
|
|
||||||
refreshWPS365Token
|
|
||||||
} from '@/api/jarvis/wps365'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'WPS365',
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: false,
|
|
||||||
isAuthorized: false,
|
|
||||||
userInfo: null,
|
|
||||||
userId: '', // 这里应该从登录用户获取,暂时使用配置
|
|
||||||
|
|
||||||
// 文件列表
|
|
||||||
fileList: [],
|
|
||||||
fileListLoading: false,
|
|
||||||
fileTotal: 0,
|
|
||||||
queryParams: {
|
|
||||||
page: 1,
|
|
||||||
pageSize: 20
|
|
||||||
},
|
|
||||||
|
|
||||||
// 编辑对话框
|
|
||||||
editDialogVisible: false,
|
|
||||||
currentFile: null,
|
|
||||||
currentSheetIdx: 0,
|
|
||||||
sheetList: [],
|
|
||||||
sheetData: [],
|
|
||||||
sheetDataLoading: false,
|
|
||||||
cellRange: 'A1:Z100',
|
|
||||||
sheetColumns: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
// 初始化时检查授权状态
|
|
||||||
// TODO: 从当前登录用户获取userId
|
|
||||||
this.userId = 'default_user' // 临时值,需要替换为实际用户ID
|
|
||||||
this.checkAuthStatus()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* 检查授权状态
|
|
||||||
*/
|
|
||||||
async checkAuthStatus() {
|
|
||||||
try {
|
|
||||||
const response = await getWPS365TokenStatus(this.userId)
|
|
||||||
if (response.code === 200) {
|
|
||||||
this.isAuthorized = response.data.hasToken && response.data.isValid
|
|
||||||
if (this.isAuthorized) {
|
|
||||||
this.loadUserInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('检查授权状态失败', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载用户信息
|
|
||||||
*/
|
|
||||||
async loadUserInfo() {
|
|
||||||
try {
|
|
||||||
const response = await getWPS365UserInfo(this.userId)
|
|
||||||
if (response.code === 200) {
|
|
||||||
this.userInfo = response.data
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载用户信息失败', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理授权
|
|
||||||
*/
|
|
||||||
async handleAuthorize() {
|
|
||||||
try {
|
|
||||||
const response = await getWPS365AuthUrl()
|
|
||||||
if (response.code === 200) {
|
|
||||||
// 在新窗口打开授权页面
|
|
||||||
window.open(response.data, '_blank')
|
|
||||||
this.$message.success('请在新窗口中完成授权,授权完成后刷新此页面')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.$message.error('获取授权URL失败:' + (error.msg || error.message))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新Token
|
|
||||||
*/
|
|
||||||
async handleRefreshToken() {
|
|
||||||
try {
|
|
||||||
// TODO: 从存储中获取refreshToken
|
|
||||||
const response = await refreshWPS365Token({
|
|
||||||
refreshToken: '' // 需要从存储中获取
|
|
||||||
})
|
|
||||||
if (response.code === 200) {
|
|
||||||
this.$message.success('Token刷新成功')
|
|
||||||
this.checkAuthStatus()
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.$message.error('刷新Token失败:' + (error.msg || error.message))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新
|
|
||||||
*/
|
|
||||||
handleRefresh() {
|
|
||||||
this.checkAuthStatus()
|
|
||||||
if (this.isAuthorized) {
|
|
||||||
this.handleLoadFiles()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载文件列表
|
|
||||||
*/
|
|
||||||
async handleLoadFiles() {
|
|
||||||
if (!this.isAuthorized) {
|
|
||||||
this.$message.warning('请先完成授权')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fileListLoading = true
|
|
||||||
try {
|
|
||||||
const response = await getWPS365FileList({
|
|
||||||
userId: this.userId,
|
|
||||||
page: this.queryParams.page,
|
|
||||||
pageSize: this.queryParams.pageSize
|
|
||||||
})
|
|
||||||
if (response.code === 200) {
|
|
||||||
this.fileList = response.data.files || []
|
|
||||||
this.fileTotal = response.data.total || 0
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.$message.error('加载文件列表失败:' + (error.msg || error.message))
|
|
||||||
} finally {
|
|
||||||
this.fileListLoading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查看文件
|
|
||||||
*/
|
|
||||||
handleViewFile(file) {
|
|
||||||
this.$message.info('查看文件功能开发中')
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 编辑文件
|
|
||||||
*/
|
|
||||||
async handleEditFile(file) {
|
|
||||||
this.currentFile = file
|
|
||||||
this.editDialogVisible = true
|
|
||||||
|
|
||||||
// 加载工作表列表
|
|
||||||
try {
|
|
||||||
const response = await getWPS365SheetList(this.userId, file.file_token)
|
|
||||||
if (response.code === 200) {
|
|
||||||
this.sheetList = response.data.sheets || []
|
|
||||||
if (this.sheetList.length > 0) {
|
|
||||||
this.currentSheetIdx = 0
|
|
||||||
this.handleLoadSheetData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.$message.error('加载工作表列表失败:' + (error.msg || error.message))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载工作表数据
|
|
||||||
*/
|
|
||||||
async handleLoadSheetData() {
|
|
||||||
if (!this.currentFile) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sheetDataLoading = true
|
|
||||||
try {
|
|
||||||
const response = await readWPS365Cells({
|
|
||||||
userId: this.userId,
|
|
||||||
fileToken: this.currentFile.file_token,
|
|
||||||
sheetIdx: this.currentSheetIdx,
|
|
||||||
range: this.cellRange
|
|
||||||
})
|
|
||||||
|
|
||||||
if (response.code === 200) {
|
|
||||||
const values = response.data.values || []
|
|
||||||
this.processSheetData(values)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.$message.error('加载工作表数据失败:' + (error.msg || error.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.length))
|
|
||||||
this.sheetColumns = Array.from({ length: maxCols }, (_, i) => i)
|
|
||||||
|
|
||||||
// 转换为表格数据格式
|
|
||||||
this.sheetData = values.map(row => {
|
|
||||||
const rowData = {}
|
|
||||||
row.forEach((cell, index) => {
|
|
||||||
rowData['col' + index] = cell !== null && cell !== undefined ? String(cell) : ''
|
|
||||||
})
|
|
||||||
// 填充空列
|
|
||||||
for (let i = row.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 value = row['col' + colIndex]
|
|
||||||
return value !== undefined ? value : ''
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await updateWPS365Cells({
|
|
||||||
userId: this.userId,
|
|
||||||
fileToken: this.currentFile.file_token,
|
|
||||||
sheetIdx: this.currentSheetIdx,
|
|
||||||
range: this.cellRange,
|
|
||||||
values: values
|
|
||||||
})
|
|
||||||
|
|
||||||
if (response.code === 200) {
|
|
||||||
this.$message.success('更新成功')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.$message.error('更新失败:' + (error.msg || error.message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.app-container {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
@@ -2276,14 +2276,14 @@ export default {
|
|||||||
// 认领人:buyer
|
// 认领人:buyer
|
||||||
const buyer = row.buyer || ''
|
const buyer = row.buyer || ''
|
||||||
|
|
||||||
// 下单日期:orderTime,格式yyyyMMdd
|
// 下单日期:orderTime,格式 yyyy/MM/dd(如 2020/03/03)
|
||||||
let orderDateStr = ''
|
let orderDateStr = ''
|
||||||
if (row.orderTime) {
|
if (row.orderTime) {
|
||||||
const orderDate = new Date(row.orderTime)
|
const orderDate = new Date(row.orderTime)
|
||||||
const year = orderDate.getFullYear()
|
const year = orderDate.getFullYear()
|
||||||
const month = String(orderDate.getMonth() + 1).padStart(2, '0')
|
const month = String(orderDate.getMonth() + 1).padStart(2, '0')
|
||||||
const day = String(orderDate.getDate()).padStart(2, '0')
|
const day = String(orderDate.getDate()).padStart(2, '0')
|
||||||
orderDateStr = `${year}${month}${day}`
|
orderDateStr = `${year}/${month}/${day}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按顺序拼接:发过运营、需要重发运营、已经重发、需要二次重发运营、二次重发、单号、型号、返现金额(团长)、晒单金额(主图没标)、总共返现、确认收货日期、认领人、下单日期
|
// 按顺序拼接:发过运营、需要重发运营、已经重发、需要二次重发运营、二次重发、单号、型号、返现金额(团长)、晒单金额(主图没标)、总共返现、确认收货日期、认领人、下单日期
|
||||||
|
|||||||
Reference in New Issue
Block a user