完善客户画像,品牌分析
This commit is contained in:
@@ -2,4 +2,4 @@
|
|||||||
ENV = 'development'
|
ENV = 'development'
|
||||||
|
|
||||||
# base api
|
# base api
|
||||||
VUE_APP_BASE_API = '/dev-api'
|
VUE_APP_BASE_API = ''
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
ENV = 'production'
|
ENV = 'production'
|
||||||
|
|
||||||
# base api
|
# base api
|
||||||
VUE_APP_BASE_API = '/prod-api'
|
VUE_APP_BASE_API = ''
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
"description": "A vue admin template with Element UI & axios & iconfont & permission control & lint",
|
"description": "A vue admin template with Element UI & axios & iconfont & permission control & lint",
|
||||||
"author": "Pan <panfree23@gmail.com>",
|
"author": "Pan <panfree23@gmail.com>",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "set CHOKIDAR_USEPOLLING=true && set CHOKIDAR_INTERVAL=1000 && vue-cli-service serve",
|
"dev": "vue-cli-service serve",
|
||||||
"dev:no-watch": "vue-cli-service serve --watch false",
|
"dev-no-open": "vue-cli-service serve --open false",
|
||||||
"build:prod": "vue-cli-service build",
|
"build:prod": "vue-cli-service build",
|
||||||
"build:stage": "vue-cli-service build --mode staging",
|
"build:stage": "vue-cli-service build --mode staging",
|
||||||
"preview": "node build/index.js --preview",
|
"preview": "node build/index.js --preview",
|
||||||
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
|
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
|
||||||
"lint": "eslint --ext .js,.vue src",
|
"lint": "vue-cli-service lint",
|
||||||
"test:unit": "jest --clearCache && vue-cli-service test:unit",
|
"test:unit": "jest --clearCache && vue-cli-service test:unit",
|
||||||
"test:ci": "npm run lint && npm run test:unit"
|
"test:ci": "npm run lint && npm run test:unit"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -120,12 +120,30 @@ export const constantRoutes = [
|
|||||||
{
|
{
|
||||||
path: '/dealership',
|
path: '/dealership',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
|
redirect: '/dealership/index',
|
||||||
|
name: 'Dealership',
|
||||||
|
meta: {
|
||||||
|
title: '门店管理',
|
||||||
|
icon: 'el-icon-s-shop'
|
||||||
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'index',
|
path: 'index',
|
||||||
name: 'Dealership',
|
name: 'DealershipIndex',
|
||||||
component: () => import('@/views/dealership/index'),
|
component: () => import('@/views/dealership/index'),
|
||||||
meta: { title: '门店管理', icon: 'el-icon-s-shop' }
|
meta: { title: '门店管理', icon: 'el-icon-s-shop' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'brand-analysis',
|
||||||
|
name: 'BrandAnalysis',
|
||||||
|
component: () => import('@/views/dealership/brand-analysis'),
|
||||||
|
meta: { title: '品牌分析', icon: 'el-icon-data-analysis' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'customer-profile',
|
||||||
|
name: 'CustomerProfile',
|
||||||
|
component: () => import('@/views/dealership/customer-profile'),
|
||||||
|
meta: { title: '客户画像', icon: 'el-icon-user-solid' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -194,7 +194,7 @@
|
|||||||
<div v-if="scope.row.audioFileUrl">
|
<div v-if="scope.row.audioFileUrl">
|
||||||
<div style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center;">
|
||||||
<el-tag type="success" size="mini" style="margin-right: 8px;">
|
<el-tag type="success" size="mini" style="margin-right: 8px;">
|
||||||
<i class="el-icon-video-play"></i>
|
<i class="el-icon-video-play" />
|
||||||
有音频
|
有音频
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<el-link
|
<el-link
|
||||||
@@ -203,15 +203,15 @@
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
:underline="false"
|
:underline="false"
|
||||||
>
|
>
|
||||||
<i class="el-icon-document"></i>
|
<i class="el-icon-document" />
|
||||||
{{ scope.row.audioFileOriginalName || getAudioFileName(scope.row.audioFileUrl) }}
|
{{ scope.row.audioFileOriginalName || getAudioFileName(scope.row.audioFileUrl) }}
|
||||||
</el-link>
|
</el-link>
|
||||||
<el-button
|
<el-button
|
||||||
type="text"
|
type="text"
|
||||||
size="mini"
|
size="mini"
|
||||||
icon="el-icon-download"
|
icon="el-icon-download"
|
||||||
@click="downloadAudioFile(scope.row)"
|
|
||||||
style="margin-left: 10px;"
|
style="margin-left: 10px;"
|
||||||
|
@click="downloadAudioFile(scope.row)"
|
||||||
>
|
>
|
||||||
下载
|
下载
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -219,12 +219,32 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<el-tag type="info" size="mini">
|
<el-tag type="info" size="mini">
|
||||||
<i class="el-icon-warning"></i>
|
<i class="el-icon-warning" />
|
||||||
暂无音频文件
|
暂无音频文件
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="AI分析链接" prop="aiAnalysisUrl" min-width="150">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<div v-if="scope.row.audioFileUrl">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="mini"
|
||||||
|
icon="el-icon-link"
|
||||||
|
@click="showAiAnalysisUrl(scope.row)"
|
||||||
|
>
|
||||||
|
获取链接
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<el-tag type="info" size="mini">
|
||||||
|
<i class="el-icon-warning" />
|
||||||
|
暂无音频
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="280" fixed="right">
|
<el-table-column label="操作" width="280" fixed="right">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
<el-button
|
||||||
@@ -380,6 +400,7 @@
|
|||||||
width="800px"
|
width="800px"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
@close="handleEditDialogClose"
|
@close="handleEditDialogClose"
|
||||||
|
@open="handleEditDialogOpen"
|
||||||
>
|
>
|
||||||
<el-form ref="audioForm" :model="audioForm" :rules="audioRules" label-width="120px">
|
<el-form ref="audioForm" :model="audioForm" :rules="audioRules" label-width="120px">
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
@@ -495,127 +516,104 @@
|
|||||||
<el-dialog
|
<el-dialog
|
||||||
title="上传音频"
|
title="上传音频"
|
||||||
:visible.sync="uploadDialogVisible"
|
:visible.sync="uploadDialogVisible"
|
||||||
width="800px"
|
width="500px"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
@close="handleUploadDialogClose"
|
@close="handleUploadDialogClose"
|
||||||
|
@open="handleUploadDialogOpen"
|
||||||
>
|
>
|
||||||
<el-form ref="uploadForm" :model="uploadForm" :rules="uploadRules" label-width="120px">
|
<div class="upload-container">
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="录音名称" prop="recordingName">
|
|
||||||
<el-input v-model="uploadForm.recordingName" placeholder="请输入录音名称" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="所属门店" prop="dealershipId">
|
|
||||||
<el-select v-model="uploadForm.dealershipId" placeholder="请选择所属门店" style="width: 100%">
|
|
||||||
<el-option
|
|
||||||
v-for="item in dealershipOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="所属项目" prop="projectId">
|
|
||||||
<el-select v-model="uploadForm.projectId" placeholder="请选择所属项目" style="width: 100%">
|
|
||||||
<el-option
|
|
||||||
v-for="item in projectOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="所属销售" prop="salesId">
|
|
||||||
<el-select v-model="uploadForm.salesId" placeholder="请选择销售" style="width: 100%">
|
|
||||||
<el-option
|
|
||||||
v-for="item in salesOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="客户姓名" prop="customerName">
|
|
||||||
<el-input v-model="uploadForm.customerName" placeholder="请输入客户姓名" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="客户手机号" prop="customerPhone">
|
|
||||||
<el-input v-model="uploadForm.customerPhone" placeholder="请输入客户手机号" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="意向级别" prop="intentionLevel">
|
|
||||||
<el-select v-model="uploadForm.intentionLevel" placeholder="请选择意向级别" style="width: 100%">
|
|
||||||
<el-option label="A级" value="A" />
|
|
||||||
<el-option label="B级" value="B" />
|
|
||||||
<el-option label="C级" value="C" />
|
|
||||||
<el-option label="D级" value="D" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="录音时间" prop="recordingTime">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="uploadForm.recordingTime"
|
|
||||||
type="datetime"
|
|
||||||
placeholder="选择录音时间"
|
|
||||||
format="yyyy-MM-dd HH:mm:ss"
|
|
||||||
value-format="yyyy-MM-dd HH:mm:ss"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-form-item label="音频文件" prop="audioFile">
|
|
||||||
<el-upload
|
<el-upload
|
||||||
ref="audioUpload"
|
ref="audioUpload"
|
||||||
:action="uploadAction"
|
:action="uploadAction"
|
||||||
:headers="uploadHeaders"
|
:headers="uploadHeaders"
|
||||||
:data="uploadData"
|
:data="uploadData"
|
||||||
|
name="file"
|
||||||
:before-upload="beforeAudioUpload"
|
:before-upload="beforeAudioUpload"
|
||||||
:on-success="handleUploadSuccess"
|
:on-success="handleUploadSuccess"
|
||||||
:on-error="handleUploadError"
|
:on-error="handleUploadError"
|
||||||
:on-progress="handleUploadProgress"
|
:on-progress="handleUploadProgress"
|
||||||
|
:on-change="handleFileChange"
|
||||||
|
:on-remove="handleFileRemove"
|
||||||
:file-list="uploadFileList"
|
:file-list="uploadFileList"
|
||||||
:auto-upload="false"
|
:auto-upload="true"
|
||||||
:limit="1"
|
:limit="1"
|
||||||
accept=".mp3,.wav,.m4a,.aac,.flac"
|
accept=".mp3,.wav,.m4a,.aac,.flac"
|
||||||
drag
|
drag
|
||||||
|
:show-file-list="true"
|
||||||
|
class="upload-demo"
|
||||||
>
|
>
|
||||||
<i class="el-icon-upload"></i>
|
<i class="el-icon-upload" />
|
||||||
<div class="el-upload__text">将音频文件拖到此处,或<em>点击上传</em></div>
|
<div class="el-upload__text">将音频文件拖到此处,或<em>点击上传</em></div>
|
||||||
<div class="el-upload__tip" slot="tip">
|
<div slot="tip" class="el-upload__tip">
|
||||||
支持 mp3、wav、m4a、aac、flac 格式,文件大小不超过 100MB
|
支持 mp3、wav、m4a、aac、flac 格式,文件大小不超过 100MB
|
||||||
</div>
|
</div>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</el-form-item>
|
</div>
|
||||||
<el-form-item label="录音描述" prop="description">
|
|
||||||
<el-input
|
|
||||||
v-model="uploadForm.description"
|
|
||||||
type="textarea"
|
|
||||||
:rows="3"
|
|
||||||
placeholder="请输入录音描述"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button @click="uploadDialogVisible = false">取 消</el-button>
|
<el-button @click="uploadDialogVisible = false">关 闭</el-button>
|
||||||
<el-button type="primary" @click="handleUploadSubmit" :loading="uploadLoading">确 定</el-button>
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- AI分析链接对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
title="音频下载链接"
|
||||||
|
:visible.sync="aiAnalysisUrlVisible"
|
||||||
|
width="500px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<div class="ai-analysis-container">
|
||||||
|
<div class="ai-analysis-info">
|
||||||
|
<p>复制以下下载链接给阿里大模型进行语音分析:</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ai-analysis-url">
|
||||||
|
<el-input
|
||||||
|
v-model="aiAnalysisUrl"
|
||||||
|
readonly
|
||||||
|
placeholder="音频下载链接"
|
||||||
|
style="margin-bottom: 15px;"
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
slot="append"
|
||||||
|
type="primary"
|
||||||
|
icon="el-icon-copy-document"
|
||||||
|
@click="copyAiAnalysisUrl"
|
||||||
|
>
|
||||||
|
复制
|
||||||
|
</el-button>
|
||||||
|
</el-input>
|
||||||
|
|
||||||
|
<div class="download-actions">
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="testDownload"
|
||||||
|
style="margin-right: 10px;"
|
||||||
|
>
|
||||||
|
测试下载
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="warning"
|
||||||
|
icon="el-icon-refresh"
|
||||||
|
@click="regenerateUrl"
|
||||||
|
>
|
||||||
|
重新生成链接
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="download-tips">
|
||||||
|
<p><strong>使用说明:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>点击"测试下载"按钮可以直接下载音频文件到本地</li>
|
||||||
|
<li>复制上方链接给阿里大模型进行语音分析</li>
|
||||||
|
<li>如果链接无法访问,点击"重新生成链接"</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="aiAnalysisUrlVisible = false">关 闭</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
@@ -638,7 +636,7 @@
|
|||||||
|
|
||||||
<div class="audio-player">
|
<div class="audio-player">
|
||||||
<div v-if="audioLoading" class="audio-loading">
|
<div v-if="audioLoading" class="audio-loading">
|
||||||
<i class="el-icon-loading"></i>
|
<i class="el-icon-loading" />
|
||||||
<span>音频加载中...</span>
|
<span>音频加载中...</span>
|
||||||
</div>
|
</div>
|
||||||
<audio
|
<audio
|
||||||
@@ -646,13 +644,13 @@
|
|||||||
:src="currentAudio.audioUrl"
|
:src="currentAudio.audioUrl"
|
||||||
controls
|
controls
|
||||||
preload="metadata"
|
preload="metadata"
|
||||||
|
style="width: 100%;"
|
||||||
@loadstart="handleAudioLoadStart"
|
@loadstart="handleAudioLoadStart"
|
||||||
@loadedmetadata="handleAudioLoaded"
|
@loadedmetadata="handleAudioLoaded"
|
||||||
@timeupdate="handleTimeUpdate"
|
@timeupdate="handleTimeUpdate"
|
||||||
@ended="handleAudioEnded"
|
@ended="handleAudioEnded"
|
||||||
@error="handleAudioError"
|
@error="handleAudioError"
|
||||||
@canplay="handleAudioCanPlay"
|
@canplay="handleAudioCanPlay"
|
||||||
style="width: 100%;"
|
|
||||||
>
|
>
|
||||||
您的浏览器不支持音频播放
|
您的浏览器不支持音频播放
|
||||||
</audio>
|
</audio>
|
||||||
@@ -664,23 +662,23 @@
|
|||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
icon="el-icon-video-play"
|
icon="el-icon-video-play"
|
||||||
@click="playAudio"
|
|
||||||
:disabled="!currentAudio.audioUrl"
|
:disabled="!currentAudio.audioUrl"
|
||||||
|
@click="playAudio"
|
||||||
>
|
>
|
||||||
播放
|
播放
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="warning"
|
type="warning"
|
||||||
icon="el-icon-video-pause"
|
icon="el-icon-video-pause"
|
||||||
@click="pauseAudio"
|
|
||||||
:disabled="!currentAudio.audioUrl"
|
:disabled="!currentAudio.audioUrl"
|
||||||
|
@click="pauseAudio"
|
||||||
>
|
>
|
||||||
暂停
|
暂停
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
icon="el-icon-refresh"
|
icon="el-icon-refresh"
|
||||||
@click="restartAudio"
|
|
||||||
:disabled="!currentAudio.audioUrl"
|
:disabled="!currentAudio.audioUrl"
|
||||||
|
@click="restartAudio"
|
||||||
>
|
>
|
||||||
重新播放
|
重新播放
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -701,7 +699,7 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="audio-progress" v-if="audioDuration > 0">
|
<div v-if="audioDuration > 0" class="audio-progress">
|
||||||
<div class="progress-info">
|
<div class="progress-info">
|
||||||
<span>{{ formatTime(currentTime) }} / {{ formatTime(audioDuration) }}</span>
|
<span>{{ formatTime(currentTime) }} / {{ formatTime(audioDuration) }}</span>
|
||||||
<span>{{ Math.round((currentTime / audioDuration) * 100) }}%</span>
|
<span>{{ Math.round((currentTime / audioDuration) * 100) }}%</span>
|
||||||
@@ -713,7 +711,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="audio-notes" v-if="currentAudio.description">
|
<div v-if="currentAudio.description" class="audio-notes">
|
||||||
<h4>录音备注:</h4>
|
<h4>录音备注:</h4>
|
||||||
<p>{{ currentAudio.description }}</p>
|
<p>{{ currentAudio.description }}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -721,7 +719,7 @@
|
|||||||
|
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button @click="playDialogVisible = false">关 闭</el-button>
|
<el-button @click="playDialogVisible = false">关 闭</el-button>
|
||||||
<el-button type="primary" @click="downloadAudio" :disabled="!currentAudio.audioUrl">
|
<el-button type="primary" :disabled="!currentAudio.audioUrl" @click="downloadAudio">
|
||||||
下载音频
|
下载音频
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -762,6 +760,9 @@ export default {
|
|||||||
uploadDialogVisible: false,
|
uploadDialogVisible: false,
|
||||||
uploadLoading: false,
|
uploadLoading: false,
|
||||||
uploadFileList: [],
|
uploadFileList: [],
|
||||||
|
// AI分析链接相关
|
||||||
|
aiAnalysisUrlVisible: false,
|
||||||
|
aiAnalysisUrl: '',
|
||||||
// 音频播放相关
|
// 音频播放相关
|
||||||
playDialogVisible: false,
|
playDialogVisible: false,
|
||||||
currentAudio: {
|
currentAudio: {
|
||||||
@@ -778,25 +779,13 @@ export default {
|
|||||||
audioDuration: 0,
|
audioDuration: 0,
|
||||||
playbackSpeed: '1.0',
|
playbackSpeed: '1.0',
|
||||||
audioLoading: false,
|
audioLoading: false,
|
||||||
uploadAction: process.env.VUE_APP_BASE_API + '/audio/upload',
|
uploadAction: '/api/audio/upload',
|
||||||
uploadHeaders: {
|
uploadHeaders: {
|
||||||
'Authorization': 'Bearer ' + this.$store.getters.token
|
'Authorization': 'Bearer ' + this.$store.getters.token
|
||||||
},
|
},
|
||||||
uploadData: {},
|
uploadData: {
|
||||||
uploadRules: {
|
type: 'audio',
|
||||||
recordingName: [
|
audioId: ''
|
||||||
{ required: true, message: '请输入录音名称', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
customerName: [
|
|
||||||
{ required: true, message: '请输入客户姓名', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
customerPhone: [
|
|
||||||
{ required: true, message: '请输入客户手机号', trigger: 'blur' },
|
|
||||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
audioFile: [
|
|
||||||
{ required: true, message: '请选择音频文件', trigger: 'change' }
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
// 查询参数
|
// 查询参数
|
||||||
queryParams: {
|
queryParams: {
|
||||||
@@ -857,8 +846,8 @@ export default {
|
|||||||
dealershipName: '',
|
dealershipName: '',
|
||||||
projectName: '',
|
projectName: '',
|
||||||
speechModel: '',
|
speechModel: '',
|
||||||
uploadStatus: '',
|
uploadStatus: '已上传',
|
||||||
syncStatus: '',
|
syncStatus: '未同步',
|
||||||
isMerged: false,
|
isMerged: false,
|
||||||
description: '',
|
description: '',
|
||||||
dealershipId: '',
|
dealershipId: '',
|
||||||
@@ -880,19 +869,13 @@ export default {
|
|||||||
duration: [
|
duration: [
|
||||||
{ required: true, message: '请输入录音时长', trigger: 'blur' }
|
{ required: true, message: '请输入录音时长', trigger: 'blur' }
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// 上传音频表单
|
watch: {
|
||||||
uploadForm: {
|
playbackSpeed(newSpeed) {
|
||||||
recordingName: '',
|
if (this.$refs.audioPlayer) {
|
||||||
dealershipId: '',
|
this.$refs.audioPlayer.playbackRate = parseFloat(newSpeed)
|
||||||
projectId: '',
|
|
||||||
salesId: '',
|
|
||||||
customerName: '',
|
|
||||||
customerPhone: '',
|
|
||||||
intentionLevel: '',
|
|
||||||
recordingTime: '',
|
|
||||||
audioFile: null,
|
|
||||||
description: ''
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -902,13 +885,6 @@ export default {
|
|||||||
this.getProjectOptions()
|
this.getProjectOptions()
|
||||||
this.getSalesOptions()
|
this.getSalesOptions()
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
playbackSpeed(newSpeed) {
|
|
||||||
if (this.$refs.audioPlayer) {
|
|
||||||
this.$refs.audioPlayer.playbackRate = parseFloat(newSpeed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
// 获取列表数据
|
// 获取列表数据
|
||||||
getList() {
|
getList() {
|
||||||
@@ -1268,16 +1244,41 @@ export default {
|
|||||||
|
|
||||||
// 获取录音详细信息
|
// 获取录音详细信息
|
||||||
getAudioById(row.id).then(response => {
|
getAudioById(row.id).then(response => {
|
||||||
|
console.log('编辑录音 111111111111111111111', response)
|
||||||
if (response.code === 20000) {
|
if (response.code === 20000) {
|
||||||
const audioData = response.data || row
|
const audioData = response.data.audio || row
|
||||||
this.audioForm = {
|
console.log('获取到的录音数据:', audioData)
|
||||||
...audioData,
|
// 确保所有字段都正确映射
|
||||||
// 确保ID字段正确
|
const formData = {
|
||||||
dealershipId: audioData.dealershipId || audioData.dealership?.id,
|
id: audioData.id,
|
||||||
projectId: audioData.projectId || audioData.project?.id,
|
recordingName: audioData.recordingName || '',
|
||||||
salesId: audioData.salesId || audioData.sales?.id
|
recordingTime: audioData.recordingTime || '',
|
||||||
|
salesName: audioData.salesName || '',
|
||||||
|
duration: audioData.duration || 0,
|
||||||
|
customerName: audioData.customerName || '',
|
||||||
|
customerPhone: audioData.customerPhone || '',
|
||||||
|
uploadTime: audioData.uploadTime || '',
|
||||||
|
intentionLevel: audioData.intentionLevel || '',
|
||||||
|
dealershipName: audioData.dealershipName || '',
|
||||||
|
projectName: audioData.projectName || '',
|
||||||
|
speechModel: audioData.speechModel || '',
|
||||||
|
uploadStatus: audioData.uploadStatus || '',
|
||||||
|
syncStatus: audioData.syncStatus || '',
|
||||||
|
isMerged: audioData.isMerged || false,
|
||||||
|
description: audioData.description || audioData.remarks || '',
|
||||||
|
dealershipId: audioData.dealershipId || audioData.dealership?.id || '',
|
||||||
|
projectId: audioData.projectId || audioData.project?.id || '',
|
||||||
|
salesId: audioData.salesId || audioData.sales?.id || ''
|
||||||
}
|
}
|
||||||
|
console.log('设置后的表单数据 22222222222222222', this.audioForm)
|
||||||
|
// 使用Object.assign确保响应式更新
|
||||||
|
Object.assign(this.audioForm, formData)
|
||||||
|
console.log('设置后的表单数据:', this.audioForm)
|
||||||
|
|
||||||
|
// 强制更新视图
|
||||||
|
this.$nextTick(() => {
|
||||||
this.editDialogVisible = true
|
this.editDialogVisible = true
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
this.$message.error(response.message || '获取录音信息失败')
|
this.$message.error(response.message || '获取录音信息失败')
|
||||||
}
|
}
|
||||||
@@ -1285,17 +1286,64 @@ export default {
|
|||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error('获取录音信息失败:', error)
|
console.error('获取录音信息失败:', error)
|
||||||
// 如果API调用失败,使用行数据
|
// 如果API调用失败,使用行数据
|
||||||
this.audioForm = {
|
const formData = {
|
||||||
...row,
|
id: row.id,
|
||||||
dealershipId: row.dealershipId || row.dealership?.id,
|
recordingName: row.recordingName || '',
|
||||||
projectId: row.projectId || row.project?.id,
|
recordingTime: row.recordingTime || '',
|
||||||
salesId: row.salesId || row.sales?.id
|
salesName: row.salesName || '',
|
||||||
|
duration: row.duration || 0,
|
||||||
|
customerName: row.customerName || '',
|
||||||
|
customerPhone: row.customerPhone || '',
|
||||||
|
uploadTime: row.uploadTime || '',
|
||||||
|
intentionLevel: row.intentionLevel || '',
|
||||||
|
dealershipName: row.dealershipName || '',
|
||||||
|
projectName: row.projectName || '',
|
||||||
|
speechModel: row.speechModel || '',
|
||||||
|
uploadStatus: row.uploadStatus || '',
|
||||||
|
syncStatus: row.syncStatus || '',
|
||||||
|
isMerged: row.isMerged || false,
|
||||||
|
description: row.description || row.remarks || '',
|
||||||
|
dealershipId: row.dealershipId || row.dealership?.id || '',
|
||||||
|
projectId: row.projectId || row.project?.id || '',
|
||||||
|
salesId: row.salesId || row.sales?.id || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 使用Object.assign确保响应式更新
|
||||||
|
Object.assign(this.audioForm, formData)
|
||||||
|
console.log('使用行数据设置的表单数据:', this.audioForm)
|
||||||
|
|
||||||
|
// 强制更新视图
|
||||||
|
this.$nextTick(() => {
|
||||||
this.editDialogVisible = true
|
this.editDialogVisible = true
|
||||||
|
})
|
||||||
this.loading = false
|
this.loading = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 编辑对话框打开
|
||||||
|
handleEditDialogOpen() {
|
||||||
|
console.log('编辑对话框打开,当前表单数据:', this.audioForm)
|
||||||
|
// 强制更新表单
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.audioForm) {
|
||||||
|
this.$refs.audioForm.clearValidate()
|
||||||
|
// 强制重新渲染表单
|
||||||
|
this.$forceUpdate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 上传对话框打开
|
||||||
|
handleUploadDialogOpen() {
|
||||||
|
console.log('上传对话框打开')
|
||||||
|
// 清空上传组件
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.audioUpload) {
|
||||||
|
this.$refs.audioUpload.clearFiles()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
// 编辑对话框关闭
|
// 编辑对话框关闭
|
||||||
handleEditDialogClose() {
|
handleEditDialogClose() {
|
||||||
this.$refs.audioForm?.resetFields()
|
this.$refs.audioForm?.resetFields()
|
||||||
@@ -1312,8 +1360,8 @@ export default {
|
|||||||
dealershipName: '',
|
dealershipName: '',
|
||||||
projectName: '',
|
projectName: '',
|
||||||
speechModel: '',
|
speechModel: '',
|
||||||
uploadStatus: '',
|
uploadStatus: '已上传',
|
||||||
syncStatus: '',
|
syncStatus: '未同步',
|
||||||
isMerged: false,
|
isMerged: false,
|
||||||
description: '',
|
description: '',
|
||||||
dealershipId: '',
|
dealershipId: '',
|
||||||
@@ -1346,6 +1394,7 @@ export default {
|
|||||||
projectId: '',
|
projectId: '',
|
||||||
salesId: ''
|
salesId: ''
|
||||||
}
|
}
|
||||||
|
console.log('添加录音时的表单数据:', this.audioForm)
|
||||||
this.editDialogVisible = true
|
this.editDialogVisible = true
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1397,92 +1446,116 @@ export default {
|
|||||||
|
|
||||||
// 上传音频
|
// 上传音频
|
||||||
handleUpload() {
|
handleUpload() {
|
||||||
this.uploadForm = {
|
// 检查是否有选中的录音
|
||||||
recordingName: '',
|
if (this.selectedIds.length === 0) {
|
||||||
dealershipId: '',
|
this.$message.warning('请先选择要上传音频的录音记录')
|
||||||
projectId: '',
|
return
|
||||||
salesId: '',
|
|
||||||
customerName: '',
|
|
||||||
customerPhone: '',
|
|
||||||
intentionLevel: '',
|
|
||||||
recordingTime: '',
|
|
||||||
audioFile: null,
|
|
||||||
description: ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.selectedIds.length > 1) {
|
||||||
|
this.$message.warning('一次只能为一个录音上传音频文件')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const audioId = this.selectedIds[0]
|
||||||
|
console.log('上传音频 - 选中的录音ID:', audioId)
|
||||||
|
|
||||||
|
// 设置audioId到上传数据中
|
||||||
|
this.uploadData.audioId = audioId
|
||||||
|
|
||||||
this.uploadFileList = []
|
this.uploadFileList = []
|
||||||
|
console.log('上传音频 - 重置后的文件列表:', this.uploadFileList)
|
||||||
|
console.log('上传音频 - 设置的上传数据:', this.uploadData)
|
||||||
this.uploadDialogVisible = true
|
this.uploadDialogVisible = true
|
||||||
},
|
},
|
||||||
|
|
||||||
// 上传音频对话框关闭
|
// 上传音频对话框关闭
|
||||||
handleUploadDialogClose() {
|
handleUploadDialogClose() {
|
||||||
this.$refs.uploadForm?.resetFields()
|
console.log('上传音频对话框关闭 - 重置文件列表')
|
||||||
this.uploadFileList = []
|
this.uploadFileList = []
|
||||||
|
// 重置audioId
|
||||||
|
this.uploadData.audioId = ''
|
||||||
|
console.log('上传音频对话框关闭 - 重置后的文件列表:', this.uploadFileList)
|
||||||
|
console.log('上传音频对话框关闭 - 重置后的上传数据:', this.uploadData)
|
||||||
},
|
},
|
||||||
|
|
||||||
// 上传前检查文件大小和类型
|
// 上传前检查文件大小和类型
|
||||||
beforeAudioUpload(file) {
|
beforeAudioUpload(file) {
|
||||||
|
console.log('上传前检查 - 文件:', file)
|
||||||
|
console.log('上传前检查 - 上传URL:', this.uploadAction)
|
||||||
|
console.log('上传前检查 - 请求头:', this.uploadHeaders)
|
||||||
|
console.log('上传前检查 - 当前audioId:', this.uploadData.audioId)
|
||||||
|
|
||||||
|
// 检查是否有audioId
|
||||||
|
if (!this.uploadData.audioId) {
|
||||||
|
this.$message.error('缺少录音ID,请重新选择录音记录')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const isLt100M = file.size / 1024 / 1024 < 100
|
const isLt100M = file.size / 1024 / 1024 < 100
|
||||||
if (!isLt100M) {
|
if (!isLt100M) {
|
||||||
this.$message.error('上传文件大小不能超过 100MB!')
|
this.$message.error('上传文件大小不能超过 100MB!')
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
return isLt100M
|
console.log('上传前检查 - 文件大小检查结果:', isLt100M)
|
||||||
|
console.log('上传前检查 - 最终上传数据:', this.uploadData)
|
||||||
|
return true
|
||||||
},
|
},
|
||||||
|
|
||||||
// 上传成功
|
// 上传成功
|
||||||
handleUploadSuccess(response, file, fileList) {
|
handleUploadSuccess(response, file, fileList) {
|
||||||
if (response.code === 20000) {
|
console.log('上传成功 - 响应:', response)
|
||||||
|
console.log('上传成功 - 文件:', file)
|
||||||
|
console.log('上传成功 - 文件列表:', fileList)
|
||||||
|
console.log('上传成功 - 使用的audioId:', this.uploadData.audioId)
|
||||||
|
|
||||||
|
// 检查响应格式
|
||||||
|
if (response && (response.code === 20000 || response.code === 200)) {
|
||||||
this.$message.success('音频上传成功')
|
this.$message.success('音频上传成功')
|
||||||
this.uploadForm.audioFile = file
|
|
||||||
this.uploadLoading = false
|
this.uploadLoading = false
|
||||||
|
// 刷新列表
|
||||||
|
this.getList()
|
||||||
|
// 关闭对话框
|
||||||
|
this.uploadDialogVisible = false
|
||||||
} else {
|
} else {
|
||||||
this.$message.error(response.message || '音频上传失败')
|
this.$message.error(response?.message || '音频上传失败')
|
||||||
this.uploadLoading = false
|
this.uploadLoading = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 上传失败
|
// 上传失败
|
||||||
handleUploadError(err, file, fileList) {
|
handleUploadError(err, file, fileList) {
|
||||||
console.error('上传失败:', err)
|
console.error('上传失败 - 错误:', err)
|
||||||
this.$message.error('音频上传失败')
|
console.error('上传失败 - 文件:', file)
|
||||||
|
console.error('上传失败 - 文件列表:', fileList)
|
||||||
|
console.error('上传失败 - 状态:', err.status)
|
||||||
|
console.error('上传失败 - 状态文本:', err.statusText)
|
||||||
|
console.error('上传失败 - 使用的audioId:', this.uploadData.audioId)
|
||||||
|
this.$message.error('音频上传失败: ' + (err.message || err.statusText || '未知错误'))
|
||||||
this.uploadLoading = false
|
this.uploadLoading = false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 文件变化处理
|
||||||
|
handleFileChange(file, fileList) {
|
||||||
|
console.log('文件变化 - 文件:', file)
|
||||||
|
console.log('文件变化 - 文件列表:', fileList)
|
||||||
|
this.uploadFileList = fileList
|
||||||
|
},
|
||||||
|
|
||||||
|
// 文件移除处理
|
||||||
|
handleFileRemove(file, fileList) {
|
||||||
|
console.log('文件移除 - 文件:', file)
|
||||||
|
console.log('文件移除 - 文件列表:', fileList)
|
||||||
|
this.uploadFileList = fileList
|
||||||
|
},
|
||||||
|
|
||||||
// 上传进度
|
// 上传进度
|
||||||
handleUploadProgress(event, file, fileList) {
|
handleUploadProgress(event, file, fileList) {
|
||||||
|
console.log('上传进度 - 事件:', event)
|
||||||
|
console.log('上传进度 - 文件:', file)
|
||||||
this.uploadLoading = true
|
this.uploadLoading = true
|
||||||
},
|
},
|
||||||
|
|
||||||
// 上传提交
|
|
||||||
handleUploadSubmit() {
|
|
||||||
this.$refs.uploadForm.validate(valid => {
|
|
||||||
if (valid) {
|
|
||||||
const formData = new FormData()
|
|
||||||
Object.keys(this.uploadForm).forEach(key => {
|
|
||||||
if (key !== 'audioFile') {
|
|
||||||
formData.append(key, this.uploadForm[key])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (this.uploadForm.audioFile) {
|
|
||||||
formData.append('audioFile', this.uploadForm.audioFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
addAudio(formData).then(response => {
|
|
||||||
if (response.code === 20000) {
|
|
||||||
this.$message.success('音频添加成功')
|
|
||||||
this.uploadDialogVisible = false
|
|
||||||
this.getList()
|
|
||||||
} else {
|
|
||||||
this.$message.error(response.message || '音频添加失败')
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
this.$message.success('音频添加成功')
|
|
||||||
this.uploadDialogVisible = false
|
|
||||||
this.getList()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// 播放音频
|
// 播放音频
|
||||||
handlePlay(row) {
|
handlePlay(row) {
|
||||||
this.currentAudio = {
|
this.currentAudio = {
|
||||||
@@ -1521,7 +1594,9 @@ export default {
|
|||||||
|
|
||||||
// 生成音频URL
|
// 生成音频URL
|
||||||
generateAudioUrl(audioId) {
|
generateAudioUrl(audioId) {
|
||||||
return process.env.VUE_APP_BASE_API + `/audio/stream/${audioId}`
|
const baseUrl = process.env.VUE_APP_API_URL || window.location.origin
|
||||||
|
const apiPath = process.env.VUE_APP_BASE_API || '/api'
|
||||||
|
return baseUrl + apiPath + `/audio/stream/${audioId}`
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取完整音频URL
|
// 获取完整音频URL
|
||||||
@@ -1531,8 +1606,93 @@ export default {
|
|||||||
if (audioFileUrl.startsWith('http')) {
|
if (audioFileUrl.startsWith('http')) {
|
||||||
return audioFileUrl
|
return audioFileUrl
|
||||||
}
|
}
|
||||||
// 如果是相对路径,拼接基础URL
|
// 如果是相对路径,拼接完整的域名
|
||||||
return process.env.VUE_APP_BASE_API + audioFileUrl
|
const baseUrl = process.env.VUE_APP_API_URL || window.location.origin
|
||||||
|
return baseUrl + audioFileUrl
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取下载链接URL
|
||||||
|
getDownloadUrl(audioFileUrl) {
|
||||||
|
if (!audioFileUrl) return ''
|
||||||
|
// 如果已经是完整URL,直接返回
|
||||||
|
if (audioFileUrl.startsWith('http')) {
|
||||||
|
return audioFileUrl
|
||||||
|
}
|
||||||
|
// 如果是相对路径,转换为下载端点
|
||||||
|
const baseUrl = process.env.VUE_APP_API_URL || window.location.origin
|
||||||
|
|
||||||
|
// 从音频文件URL中提取文件名
|
||||||
|
const fileName = audioFileUrl.split('/').pop()
|
||||||
|
if (!fileName) return ''
|
||||||
|
|
||||||
|
// 使用专门的下载端点
|
||||||
|
return `${baseUrl}/api/audio/download/${fileName}`
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取备用下载链接1(带下载参数)
|
||||||
|
getAlternativeUrl1() {
|
||||||
|
if (!this.currentAiAnalysisRow || !this.currentAiAnalysisRow.audioFileUrl) return ''
|
||||||
|
const baseUrl = process.env.VUE_APP_API_URL || window.location.origin
|
||||||
|
return `${baseUrl}${this.currentAiAnalysisRow.audioFileUrl}?download=true&disposition=attachment`
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取备用下载链接2(流式下载)
|
||||||
|
getAlternativeUrl2() {
|
||||||
|
if (!this.currentAiAnalysisRow || !this.currentAiAnalysisRow.audioFileUrl) return ''
|
||||||
|
const baseUrl = process.env.VUE_APP_API_URL || window.location.origin
|
||||||
|
const fileName = this.currentAiAnalysisRow.audioFileUrl.split('/').pop()
|
||||||
|
return `${baseUrl}/api/audio/stream/${fileName}`
|
||||||
|
},
|
||||||
|
|
||||||
|
// 测试下载功能
|
||||||
|
testDownload() {
|
||||||
|
if (!this.currentAiAnalysisRow || !this.currentAiAnalysisRow.audioFileUrl) {
|
||||||
|
this.$message.warning('没有可下载的音频文件')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseUrl = process.env.VUE_APP_API_URL || window.location.origin
|
||||||
|
const audioUrl = `${baseUrl}${this.currentAiAnalysisRow.audioFileUrl}`
|
||||||
|
const fileName = this.currentAiAnalysisRow.audioFileOriginalName ||
|
||||||
|
this.currentAiAnalysisRow.audioFileUrl.split('/').pop()
|
||||||
|
|
||||||
|
// 使用fetch下载文件
|
||||||
|
this.$message.info('正在下载音频文件...')
|
||||||
|
|
||||||
|
fetch(audioUrl)
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`)
|
||||||
|
}
|
||||||
|
return response.blob()
|
||||||
|
})
|
||||||
|
.then(blob => {
|
||||||
|
// 创建下载链接
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = fileName
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
this.$message.success('下载成功!')
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('下载失败:', error)
|
||||||
|
this.$message.error('下载失败,请检查网络连接或文件是否存在')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 重新生成链接
|
||||||
|
regenerateUrl() {
|
||||||
|
if (!this.currentAiAnalysisRow) return
|
||||||
|
|
||||||
|
const baseUrl = process.env.VUE_APP_API_URL || window.location.origin
|
||||||
|
const timestamp = new Date().getTime()
|
||||||
|
const randomParam = Math.random().toString(36).substring(7)
|
||||||
|
this.aiAnalysisUrl = `${baseUrl}${this.currentAiAnalysisRow.audioFileUrl}?t=${timestamp}&r=${randomParam}&download=true`
|
||||||
|
this.$message.success('链接已重新生成')
|
||||||
},
|
},
|
||||||
|
|
||||||
// 重置音频播放器
|
// 重置音频播放器
|
||||||
@@ -1675,6 +1835,57 @@ export default {
|
|||||||
'D': 'danger'
|
'D': 'danger'
|
||||||
}
|
}
|
||||||
return typeMap[level] || 'info'
|
return typeMap[level] || 'info'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 显示AI分析链接
|
||||||
|
showAiAnalysisUrl(row) {
|
||||||
|
this.currentAiAnalysisRow = row
|
||||||
|
// 生成一个带时间戳的下载链接,避免缓存问题
|
||||||
|
const baseUrl = process.env.VUE_APP_API_URL || window.location.origin
|
||||||
|
const timestamp = new Date().getTime()
|
||||||
|
this.aiAnalysisUrl = `${baseUrl}${row.audioFileUrl}?t=${timestamp}&download=true`
|
||||||
|
this.aiAnalysisUrlVisible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
// 复制AI分析链接
|
||||||
|
copyAiAnalysisUrl() {
|
||||||
|
if (this.aiAnalysisUrl) {
|
||||||
|
// 使用现代浏览器的 Clipboard API
|
||||||
|
if (navigator.clipboard && window.isSecureContext) {
|
||||||
|
navigator.clipboard.writeText(this.aiAnalysisUrl).then(() => {
|
||||||
|
this.$message.success('链接已复制到剪贴板')
|
||||||
|
}).catch(() => {
|
||||||
|
this.fallbackCopyTextToClipboard(this.aiAnalysisUrl)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.fallbackCopyTextToClipboard(this.aiAnalysisUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 备用复制方法
|
||||||
|
fallbackCopyTextToClipboard(text) {
|
||||||
|
const textArea = document.createElement('textarea')
|
||||||
|
textArea.value = text
|
||||||
|
textArea.style.position = 'fixed'
|
||||||
|
textArea.style.left = '-999999px'
|
||||||
|
textArea.style.top = '-999999px'
|
||||||
|
document.body.appendChild(textArea)
|
||||||
|
textArea.focus()
|
||||||
|
textArea.select()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const successful = document.execCommand('copy')
|
||||||
|
if (successful) {
|
||||||
|
this.$message.success('链接已复制到剪贴板')
|
||||||
|
} else {
|
||||||
|
this.$message.error('复制失败,请手动复制')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.$message.error('复制失败,请手动复制')
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.removeChild(textArea)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1798,4 +2009,82 @@ export default {
|
|||||||
color: #606266;
|
color: #606266;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 上传音频样式 */
|
||||||
|
.upload-container {
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-demo {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-demo .el-upload {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-demo .el-upload-dragger {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AI分析链接样式 */
|
||||||
|
.ai-analysis-container {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-analysis-info {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #f0f9ff;
|
||||||
|
border-radius: 4px;
|
||||||
|
border-left: 4px solid #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-analysis-info p {
|
||||||
|
margin: 0;
|
||||||
|
color: #606266;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-analysis-url {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-actions {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-tips {
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
border-left: 4px solid #e6a23c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-tips p {
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-tips ul {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-tips li {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -68,13 +68,7 @@
|
|||||||
<el-table-column label="设备信息(最近一次)" min-width="300">
|
<el-table-column label="设备信息(最近一次)" min-width="300">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<div class="device-info">
|
<div class="device-info">
|
||||||
<div>联网时间: {{ scope.row.lastOnlineTime || '-' }}</div>
|
|
||||||
<div>开机时间: {{ scope.row.lastPowerOnTime || '-' }}</div>
|
|
||||||
<div>关机时间: {{ scope.row.lastPowerOffTime || '-' }}</div>
|
|
||||||
<div>WIFI名称: {{ scope.row.wifiName || '-' }}</div>
|
<div>WIFI名称: {{ scope.row.wifiName || '-' }}</div>
|
||||||
<div>未上传数量: {{ scope.row.unuploadedCount || 0 }}</div>
|
|
||||||
<div>空间大小: {{ scope.row.storageUsed || '0GB' }}/{{ scope.row.storageSize || '0GB' }}</div>
|
|
||||||
<div>剩余电量: {{ scope.row.remainingBattery || '-' }}</div>
|
|
||||||
<div>
|
<div>
|
||||||
充电状态:
|
充电状态:
|
||||||
<span
|
<span
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ module.exports = {
|
|||||||
productionSourceMap: false,
|
productionSourceMap: false,
|
||||||
devServer: {
|
devServer: {
|
||||||
port: port,
|
port: port,
|
||||||
open: true,
|
open: false, // 禁用自动打开浏览器
|
||||||
overlay: {
|
overlay: {
|
||||||
warnings: false,
|
warnings: false,
|
||||||
errors: true
|
errors: true
|
||||||
},
|
},
|
||||||
// before: require('./mock/mock-server.js'), // 注释掉 mock 服务器
|
// before: require('./mock/mock-server.js'), // 启用 mock 服务器
|
||||||
watchOptions: {
|
watchOptions: {
|
||||||
ignored: [
|
ignored: [
|
||||||
'**/node_modules/**',
|
'**/node_modules/**',
|
||||||
@@ -56,12 +56,15 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://localhost:9060', // 从环境变量获取后端服务器地址
|
target: 'http://localhost:9060', // 本地后端服务器地址
|
||||||
|
// target: 'https://api.huayang-star.com/', // 生产环境后端地址
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
pathRewrite: {
|
pathRewrite: {
|
||||||
'^/api': '/api' // 保持 /api 前缀
|
'^/api': '/api' // 保持 /api 前缀
|
||||||
},
|
},
|
||||||
logLevel: 'debug' // 开发环境下显示代理日志
|
logLevel: 'debug', // 开发环境下显示代理日志
|
||||||
|
secure: false, // 如果是https,忽略证书验证
|
||||||
|
timeout: 10000 // 设置超时时间
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user