This commit is contained in:
Leo
2026-01-15 19:50:23 +08:00
parent 09cb3c2862
commit 04dd5396ac

View File

@@ -1,148 +1,225 @@
<template> <template>
<div class="tendoc-config"> <div class="tendoc-config">
<!-- 授权状态 --> <div class="config-container">
<el-card class="auth-card" style="margin-bottom: 20px;"> <!-- 左侧配置表单 -->
<div slot="header"> <div class="config-left">
<span><i class="el-icon-key"></i> 授权状态</span> <!-- 授权状态 -->
</div> <el-card class="config-section">
<div class="auth-status"> <div slot="header" class="section-header">
<el-tag v-if="config.hasAccessToken" type="success" size="medium"> <i class="el-icon-key"></i>
<i class="el-icon-circle-check"></i> {{ config.accessTokenStatus }} <span>授权状态</span>
</el-tag> </div>
<el-tag v-else type="danger" size="medium"> <div class="auth-status">
<i class="el-icon-circle-close"></i> {{ config.accessTokenStatus }} <el-tag v-if="config.hasAccessToken" type="success" size="medium">
</el-tag> <i class="el-icon-circle-check"></i> {{ config.accessTokenStatus }}
<el-button </el-tag>
v-if="!config.hasAccessToken" <el-tag v-else type="danger" size="medium">
type="primary" <i class="el-icon-circle-close"></i> {{ config.accessTokenStatus }}
size="small" </el-tag>
icon="el-icon-unlock"
@click="handleAuth"
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="config.hint" class="config-hint" style="margin-top: 10px; color: #909399; font-size: 12px;">
<i class="el-icon-info"></i> {{ config.hint }}
</div>
</el-card>
<!-- 自动写入配置 -->
<el-card>
<div slot="header">
<span><i class="el-icon-setting"></i> 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="120px" size="small">
<el-form-item label="文件ID" prop="fileId">
<el-input
v-model="form.fileId"
placeholder="例如DUW50RUprWXh2TGJK"
clearable
style="width: 400px;"
>
<el-button <el-button
slot="append" v-if="!config.hasAccessToken"
icon="el-icon-search" type="primary"
@click="handleFetchSheets" size="small"
:disabled="!form.fileId" icon="el-icon-unlock"
@click="handleAuth"
style="margin-left: 10px;"
> >
获取工作表 立即授权
</el-button> </el-button>
</el-input> <el-button
</el-form-item> v-else
type="info"
<el-form-item label="工作表ID" prop="sheetId"> size="small"
<el-select icon="el-icon-refresh"
v-if="sheetList.length > 0" @click="handleRefreshAuth"
v-model="form.sheetId" style="margin-left: 10px;"
placeholder="请选择工作表"
style="width: 400px;"
clearable
>
<el-option
v-for="sheet in sheetList"
:key="sheet.sheetId"
:label="sheet.title"
:value="sheet.sheetId"
> >
<span style="float: left">{{ sheet.title }}</span> 刷新状态
<span style="float: right; color: #8492a6; font-size: 12px;">{{ sheet.sheetId }}</span> </el-button>
</el-option> </div>
</el-select> <div v-if="config.hint" class="config-hint" style="margin-top: 10px; color: #909399; font-size: 12px;">
<el-input <i class="el-icon-info"></i> {{ config.hint }}
v-else </div>
v-model="form.sheetId" </el-card>
placeholder="例如BB08J2"
clearable
style="width: 400px;"
/>
</el-form-item>
<el-row :gutter="20"> <!-- 文档配置表单 -->
<el-col :span="12"> <el-card class="config-section">
<el-form-item label="表头行号" prop="headerRow"> <div slot="header" class="section-header">
<el-input-number <i class="el-icon-document"></i>
v-model="form.headerRow" <span>H-TF订单自动写入配置</span>
:min="1" <el-tag v-if="config.isConfigured" type="success" size="mini" style="margin-left: 10px;">
:max="100" <i class="el-icon-success"></i> 已配置
style="width: 100%;" </el-tag>
/> <el-tag v-else type="warning" size="mini" style="margin-left: 10px;">
<div style="color: #909399; font-size: 12px; margin-top: 5px;"> <i class="el-icon-warning"></i> 未配置
表头所在的行号从1开始 </el-tag>
</div> </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="例如DUW50RUprWXh2TGJK"
clearable
>
<el-button
slot="append"
icon="el-icon-search"
@click="handleFetchSheets"
:disabled="!form.fileId"
>
获取工作表
</el-button>
</el-input>
</el-form-item> </el-form-item>
</el-col>
<el-col :span="12"> <el-form-item label="工作表ID" prop="sheetId">
<el-form-item label="数据起始行" prop="startRow"> <el-select
<el-input-number v-if="sheetList.length > 0"
v-model="form.startRow" v-model="form.sheetId"
:min="1" placeholder="请选择工作表"
:max="10000"
style="width: 100%;" style="width: 100%;"
clearable
>
<el-option
v-for="sheet in sheetList"
:key="sheet.sheetId"
:label="sheet.title"
:value="sheet.sheetId"
>
<span style="float: left">{{ sheet.title }}</span>
<span style="float: right; color: #8492a6; font-size: 12px;">{{ sheet.sheetId }}</span>
</el-option>
</el-select>
<el-input
v-else
v-model="form.sheetId"
placeholder="例如BB08J2"
clearable
/> />
<div style="color: #909399; font-size: 12px; margin-top: 5px;">
数据开始的行号从1开始
</div>
</el-form-item> </el-form-item>
</el-col>
</el-row>
<el-form-item> <el-row :gutter="10">
<el-button type="primary" @click="handleSave" :loading="saveLoading" icon="el-icon-check"> <el-col :span="12">
保存配置 <el-form-item label="表头行号" prop="headerRow">
</el-button> <el-input-number
<el-button @click="handleTest" :loading="testLoading" icon="el-icon-setting"> v-model="form.headerRow"
测试配置 :min="1"
</el-button> controls-position="right"
<el-button @click="handleClear" :loading="clearLoading" type="danger" plain icon="el-icon-delete"> style="width: 100%;"
清除配置 />
</el-button> </el-form-item>
<el-button @click="showOperationLogs = true" icon="el-icon-document"> </el-col>
操作日志 <el-col :span="12">
</el-button> <el-form-item label="起始行号" prop="startRow">
</el-form-item> <el-input-number
</el-form> v-model="form.startRow"
</el-card> :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-button @click="showOperationLogs = true" icon="el-icon-document">
操作日志
</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 v-if="config.progressHint || config.currentProgress" class="progress-card-wrapper">
<div slot="header" class="card-header">
<i class="el-icon-data-line"></i>
<span>同步进度</span>
</div>
<div class="progress-content">
<div v-if="config.currentProgress" class="progress-detail">
<div class="progress-item">
<span class="label">当前进度</span>
<span class="value"> {{ config.currentProgress }} </span>
</div>
<div class="progress-item">
<span class="label">下次同步</span>
<span class="value">
<template v-if="config.currentProgress <= (form.startRow + 49)">
{{ form.startRow }}
</template>
<template v-else-if="config.currentProgress > (form.startRow + 100)">
{{ config.currentProgress - 100 }}
</template>
<template v-else>
{{ form.startRow }}
</template>
</span>
</div>
<div class="progress-hint">
<i class="el-icon-info"></i>
系统自动回溯检查防止遗漏
</div>
</div>
<div v-else class="no-progress">
{{ config.progressHint || '暂无同步进度' }}
</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从腾讯文档URL中获取</span>
</div>
<div class="help-item">
<i class="el-icon-check"></i>
<span>点击"获取工作表"自动加载</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>
<!-- 操作日志查看对话框 -->
<tencent-doc-operation-logs <tencent-doc-operation-logs
v-model="showOperationLogs" v-model="showOperationLogs"
:file-id="form.fileId" :file-id="form.fileId"
@@ -158,8 +235,7 @@ import {
testAutoWriteConfig, testAutoWriteConfig,
clearAutoWriteConfig, clearAutoWriteConfig,
getDocSheetList, getDocSheetList,
getTencentDocAuthUrl, getTencentDocAuthUrl
getTokenStatus
} from '@/api/jarvis/tendoc' } from '@/api/jarvis/tendoc'
import TencentDocOperationLogs from '@/views/system/jdorder/components/TencentDocOperationLogs' import TencentDocOperationLogs from '@/views/system/jdorder/components/TencentDocOperationLogs'
@@ -176,8 +252,12 @@ export default {
accessTokenStatus: '未授权', accessTokenStatus: '未授权',
fileId: '', fileId: '',
sheetId: '', sheetId: '',
appId: '',
apiBaseUrl: '',
isConfigured: false, isConfigured: false,
hint: '' hint: '',
progressHint: '',
currentProgress: null
}, },
form: { form: {
fileId: '', fileId: '',
@@ -224,8 +304,10 @@ export default {
this.config = res.data this.config = res.data
this.form.fileId = res.data.fileId || '' this.form.fileId = res.data.fileId || ''
this.form.sheetId = res.data.sheetId || '' this.form.sheetId = res.data.sheetId || ''
// 确保 headerRow 和 startRow 是数字类型
this.form.headerRow = parseInt(res.data.headerRow) || 2 this.form.headerRow = parseInt(res.data.headerRow) || 2
this.form.startRow = parseInt(res.data.startRow) || 3 this.form.startRow = parseInt(res.data.startRow) || 3
console.log('配置加载成功 - headerRow:', this.form.headerRow, 'startRow:', this.form.startRow)
} }
} catch (e) { } catch (e) {
this.$message.error('加载配置失败:' + (e.message || '未知错误')) this.$message.error('加载配置失败:' + (e.message || '未知错误'))
@@ -320,12 +402,21 @@ export default {
if (res.code === 200) { if (res.code === 200) {
this.$message.success(`配置保存成功!表头第${this.form.headerRow}行,数据从第${this.form.startRow}行开始`) this.$message.success(`配置保存成功!表头第${this.form.headerRow}行,数据从第${this.form.startRow}行开始`)
this.loadConfig() console.log('配置保存成功 - 保存的值:', {
fileId: this.form.fileId,
sheetId: this.form.sheetId,
headerRow: this.form.headerRow,
startRow: this.form.startRow
})
// 延迟重新加载配置,确保后端已保存
setTimeout(() => {
this.loadConfig()
}, 500)
} else { } else {
this.$message.error('保存配置失败:' + (res.msg || '未知错误')) this.$message.error('保存失败:' + (res.msg || '未知错误'))
} }
} catch (e) { } catch (e) {
this.$message.error('保存配置失败:' + (e.message || '未知错误')) this.$message.error('保存失败:' + (e.message || '未知错误'))
} finally { } finally {
this.saveLoading = false this.saveLoading = false
} }
@@ -337,64 +428,108 @@ export default {
this.testLoading = true this.testLoading = true
try { try {
const res = await testAutoWriteConfig() const res = await testAutoWriteConfig()
if (res.code === 200) { if (res.code === 200) {
this.$message.success('配置测试通过!') this.$alert(
'<pre style="text-align: left; max-height: 400px; overflow: auto;">' +
JSON.stringify(res.data, null, 2) +
'</pre>',
'测试成功',
{
dangerouslyUseHTMLString: true,
confirmButtonText: '确定',
type: 'success'
}
)
} else { } else {
this.$message.error('配置测试失败:' + (res.msg || '未知错误')) this.$message.error('测试失败:' + (res.msg || '未知错误'))
} }
} catch (e) { } catch (e) {
this.$message.error('配置测试失败:' + (e.message || '未知错误')) this.$message.error('测试失败:' + (e.message || '未知错误'))
} finally { } finally {
this.testLoading = false this.testLoading = false
} }
}, },
/** 清除配置 */ /** 清除配置 */
handleClear() { async handleClear() {
this.$confirm('确定要清除配置吗?此操作不可恢复!', '提示', { try {
confirmButtonText: '确定', await this.$confirm('确定要清除配置吗?这不会清除授权令牌。', '提示', {
cancelButtonText: '取消', type: 'warning'
type: 'warning' })
}).then(async () => {
this.clearLoading = true this.clearLoading = true
try { const res = await clearAutoWriteConfig()
const res = await clearAutoWriteConfig()
if (res.code === 200) { if (res.code === 200) {
this.$message.success('配置已清除') this.$message.success('配置已清除')
this.form = { this.form.fileId = ''
fileId: '', this.form.sheetId = ''
sheetId: '', this.form.startRow = 3
headerRow: 2, this.sheetList = []
startRow: 3 this.loadConfig()
} } else {
this.sheetList = [] this.$message.error('清除失败:' + (res.msg || '未知错误'))
this.loadConfig()
} else {
this.$message.error('清除配置失败:' + (res.msg || '未知错误'))
}
} catch (e) {
this.$message.error('清除配置失败:' + (e.message || '未知错误'))
} finally {
this.clearLoading = false
} }
}) } catch (e) {
if (e !== 'cancel') {
this.$message.error('清除失败:' + (e.message || '未知错误'))
}
} finally {
this.clearLoading = false
}
} }
} }
} }
</script> </script>
<style scoped> <style scoped>
.tendoc-config { /* 容器布局 */
padding: 0; .config-container {
display: flex;
gap: 20px;
min-height: 400px;
} }
.auth-card { .config-left {
margin-bottom: 20px; flex: 1;
display: flex;
flex-direction: column;
gap: 15px;
} }
.config-right {
width: 300px;
display: flex;
flex-direction: column;
gap: 15px;
}
/* 配置区块 */
.config-section {
background: #f5f7fa;
border-radius: 6px;
}
.section-header {
display: flex;
align-items: center;
font-size: 14px;
font-weight: 500;
color: #303133;
}
.section-header i {
margin-right: 6px;
font-size: 16px;
color: #409eff;
}
/* 授权状态 */
.auth-status { .auth-status {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px;
} }
.config-hint { .config-hint {
@@ -402,5 +537,197 @@ export default {
color: #909399; color: #909399;
font-size: 12px; font-size: 12px;
} }
</style>
/* 状态卡片 */
.status-card-wrapper {
padding: 0;
border: none;
box-shadow: none;
}
.status-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 8px;
padding: 20px;
color: white;
display: flex;
align-items: center;
gap: 15px;
box-shadow: 0 2px 12px rgba(102, 126, 234, 0.3);
}
.status-card.warning {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.status-icon {
width: 48px;
height: 48px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
flex-shrink: 0;
}
.status-icon.success {
background: rgba(103, 194, 58, 0.2);
}
.status-icon.warning {
background: rgba(230, 162, 60, 0.2);
}
.status-text {
flex: 1;
}
.status-title {
font-size: 16px;
font-weight: 500;
margin-bottom: 5px;
}
.status-desc {
font-size: 12px;
opacity: 0.9;
line-height: 1.5;
}
/* 进度卡片 */
.progress-card-wrapper {
padding: 0;
border: none;
box-shadow: none;
}
.progress-card-wrapper >>> .el-card__body {
padding: 0;
}
.card-header {
background: #f5f7fa;
padding: 12px 15px;
font-size: 14px;
font-weight: 500;
color: #303133;
display: flex;
align-items: center;
border-bottom: 1px solid #e4e7ed;
}
.card-header i {
margin-right: 6px;
color: #409eff;
}
.progress-content {
padding: 15px;
}
.progress-detail {
display: flex;
flex-direction: column;
gap: 10px;
}
.progress-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: #f0f9ff;
border-radius: 4px;
border-left: 3px solid #409eff;
}
.progress-item .label {
font-size: 13px;
color: #606266;
}
.progress-item .value {
font-size: 14px;
font-weight: 500;
color: #303133;
}
.progress-hint {
font-size: 12px;
color: #909399;
padding: 8px 12px;
background: #fef0f0;
border-radius: 4px;
border-left: 3px solid #f56c6c;
display: flex;
align-items: center;
gap: 5px;
}
.no-progress {
font-size: 13px;
color: #909399;
text-align: center;
padding: 10px;
}
/* 帮助卡片 */
.help-card-wrapper {
padding: 0;
border: none;
box-shadow: none;
}
.help-card-wrapper >>> .el-card__body {
padding: 0;
}
.help-content {
padding: 15px;
display: flex;
flex-direction: column;
gap: 10px;
}
.help-item {
display: flex;
align-items: flex-start;
gap: 8px;
font-size: 13px;
color: #606266;
line-height: 1.6;
}
.help-item i {
color: #67c23a;
margin-top: 2px;
flex-shrink: 0;
}
/* Element UI 覆盖样式 */
.config-section >>> .el-form-item {
margin-bottom: 18px;
}
.config-section >>> .el-form-item__label {
font-weight: 500;
color: #606266;
}
.config-section >>> .el-input-number {
width: 100%;
}
/* 响应式调整 */
@media (max-width: 1200px) {
.config-container {
flex-direction: column;
}
.config-right {
width: 100%;
}
}
</style>