feat: 优化
This commit is contained in:
@@ -430,10 +430,11 @@ onBeforeUnmount(() => {
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
border-radius: 12px !important;
|
||||
background: #f8fafc !important;
|
||||
border: 1.5px solid #e2e8f0 !important;
|
||||
border: 1.5px solid transparent !important;
|
||||
box-shadow: none !important;
|
||||
transition: all 0.2s ease !important;
|
||||
height: 48px !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
:deep(.ant-input-affix-wrapper:hover) {
|
||||
@@ -445,6 +446,7 @@ onBeforeUnmount(() => {
|
||||
border-color: #3B82F6 !important;
|
||||
background: #ffffff !important;
|
||||
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1) !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.ant-input) {
|
||||
@@ -467,24 +469,29 @@ onBeforeUnmount(() => {
|
||||
font-weight: 500;
|
||||
color: #334155;
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
:deep(.ant-input-affix-wrapper:focus-within) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item-control) {
|
||||
min-height: 48px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 表单验证错误状态 */
|
||||
:deep(.ant-form-item-has-error .ant-input-affix-wrapper) {
|
||||
border-color: #ef4444 !important;
|
||||
background: #ffffff !important;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item-explain-error) {
|
||||
color: #ef4444;
|
||||
font-size: 13px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* ========== 验证码输入 ========== */
|
||||
.code-input {
|
||||
display: flex;
|
||||
|
||||
@@ -25,6 +25,7 @@ const {
|
||||
saveTableDataToSession,
|
||||
loadTableDataFromSession,
|
||||
processApiResponse,
|
||||
appendData,
|
||||
clearData,
|
||||
} = useBenchmarkData()
|
||||
|
||||
@@ -51,6 +52,10 @@ const batchPromptTextCount = ref(0)
|
||||
const savePromptModalVisible = ref(false)
|
||||
const savePromptContent = ref('')
|
||||
|
||||
const maxCursor = ref(0)
|
||||
const hasMore = ref(false)
|
||||
const loadingMore = ref(false)
|
||||
|
||||
async function handleAnalyzeUser() {
|
||||
const sec_user_id = resolveId(form.value.url, {
|
||||
queryKeys: ['user'],
|
||||
@@ -61,6 +66,10 @@ async function handleAnalyzeUser() {
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
maxCursor.value = 0
|
||||
hasMore.value = false
|
||||
await clearData()
|
||||
|
||||
const req = await TikhubService.postTikHup({
|
||||
type: InterfaceType.DOUYIN_WEB_USER_POST_VIDEOS,
|
||||
methodType: MethodType.GET,
|
||||
@@ -75,7 +84,9 @@ async function handleAnalyzeUser() {
|
||||
|
||||
try {
|
||||
const resp = await req
|
||||
processApiResponse(resp, form.value.platform)
|
||||
const result = processApiResponse(resp, form.value.platform)
|
||||
maxCursor.value = result.maxCursor
|
||||
hasMore.value = result.hasMore
|
||||
await saveTableDataToSession()
|
||||
message.success('分析完成')
|
||||
} catch (err) {
|
||||
@@ -164,9 +175,64 @@ async function handleBatchAnalyze() {
|
||||
|
||||
async function handleResetForm() {
|
||||
form.value = { platform: '抖音', url: '', count: 20, sort_type: 0 }
|
||||
maxCursor.value = 0
|
||||
hasMore.value = false
|
||||
await clearData()
|
||||
}
|
||||
|
||||
async function handleLoadMore() {
|
||||
const sec_user_id = resolveId(form.value.url, {
|
||||
queryKeys: ['user'],
|
||||
pathPatterns: ['/user/:id'],
|
||||
})
|
||||
if (!sec_user_id) {
|
||||
return
|
||||
}
|
||||
|
||||
loadingMore.value = true
|
||||
const req = await TikhubService.postTikHup({
|
||||
type: InterfaceType.DOUYIN_WEB_USER_POST_VIDEOS,
|
||||
methodType: MethodType.GET,
|
||||
urlParams: {
|
||||
sec_user_id,
|
||||
max_cursor: maxCursor.value,
|
||||
type: 'tik-app',
|
||||
sort_type: form.value.sort_type,
|
||||
count: form.value.count || 20,
|
||||
},
|
||||
})
|
||||
|
||||
try {
|
||||
const resp = await req
|
||||
|
||||
if (form.value.platform === '抖音') {
|
||||
const { mapFromDouyin } = await import('./utils/benchmarkUtils')
|
||||
const awemeList = resp?.data?.aweme_list || []
|
||||
const newItems = mapFromDouyin(awemeList)
|
||||
appendData(newItems)
|
||||
|
||||
maxCursor.value = resp?.data?.max_cursor || 0
|
||||
hasMore.value = resp?.data?.has_more || !(awemeList.length < (resp?.data?.count || 20))
|
||||
} else {
|
||||
const { mapFromXhs } = await import('./utils/benchmarkUtils')
|
||||
const notes = resp?.data?.notes || resp?.data?.data || []
|
||||
const newItems = mapFromXhs(notes)
|
||||
appendData(newItems)
|
||||
|
||||
maxCursor.value = resp?.data?.max_cursor || 0
|
||||
hasMore.value = resp?.data?.has_more || false
|
||||
}
|
||||
|
||||
await saveTableDataToSession()
|
||||
message.success(`已加载 ${data.value.length} 条数据`)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
message.error('加载失败,请稍后重试')
|
||||
} finally {
|
||||
loadingMore.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleCopyBatchPrompt(prompt) {
|
||||
if (!prompt?.trim()) {
|
||||
message.warning('没有提示词可复制')
|
||||
@@ -224,11 +290,14 @@ defineOptions({ name: 'ContentStyleBenchmark' })
|
||||
:data="data"
|
||||
v-model:selectedRowKeys="selectedRowKeys"
|
||||
:loading="loading"
|
||||
:loading-more="loadingMore"
|
||||
:has-more="hasMore"
|
||||
@export="handleExportToExcel"
|
||||
@batch-analyze="handleBatchAnalyze"
|
||||
@load-more="handleLoadMore"
|
||||
/>
|
||||
|
||||
<section v-if="!data.length && !loading" class="card results-card empty-state">
|
||||
<section v-if="!data.length" class="card results-card empty-state">
|
||||
<a-empty description="暂无数据,请点击开始分析">
|
||||
<template #image>
|
||||
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
@@ -30,29 +30,31 @@ function handleReset() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="card">
|
||||
<section class="form-container">
|
||||
<a-form :model="form" layout="vertical">
|
||||
<a-form-item label="平台">
|
||||
<a-radio-group v-model:value="form.platform" button-style="solid">
|
||||
<a-radio-button value="抖音">抖音</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="主页/视频链接">
|
||||
<a-input
|
||||
class="search-input"
|
||||
v-model:value="form.url"
|
||||
placeholder="粘贴抖音主页或视频链接,或点击下方示例试一试"
|
||||
placeholder="粘贴抖音主页或视频链接"
|
||||
allow-clear
|
||||
size="large"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="最大数量(建议保持默认值20)">
|
||||
|
||||
<a-form-item label="分析数量">
|
||||
<div class="slider-row">
|
||||
<a-slider v-model:value="form.count" :min="1" :max="100" :tooltip-open="true" style="flex:1" />
|
||||
<a-input-number v-model:value="form.count" :min="1" :max="100" style="width:96px; margin-left:12px;" />
|
||||
<a-slider v-model:value="form.count" :min="1" :max="40" :tooltip-open="true" style="flex:1" />
|
||||
<a-input-number v-model:value="form.count" :min="1" :max="40" style="width:96px; margin-left:12px;" />
|
||||
</div>
|
||||
<div class="form-hint">数量越大越全面,但分析时间更长;建议 20–30。</div>
|
||||
<div class="form-hint">建议 20-30 个内容</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-space>
|
||||
<GradientButton
|
||||
text="开始分析"
|
||||
@@ -61,18 +63,17 @@ function handleReset() {
|
||||
size="middle"
|
||||
@click="handleAnalyze"
|
||||
/>
|
||||
<a-button @click="handleReset">清空</a-button>
|
||||
<a-button @click="handleReset">重置</a-button>
|
||||
</a-space>
|
||||
</a-form>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card {
|
||||
.form-container {
|
||||
background: var(--color-surface);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
box-shadow: var(--shadow-inset-card);
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
@@ -87,7 +88,6 @@ function handleReset() {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
|
||||
:deep(.ant-slider) {
|
||||
padding: 10px 0;
|
||||
}
|
||||
@@ -115,30 +115,5 @@ function handleReset() {
|
||||
:deep(.ant-slider-handle:active::after) {
|
||||
box-shadow: 0 0 0 3px var(--color-primary);
|
||||
}
|
||||
.search-input {
|
||||
flex: 1;
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
color: var(--color-text);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 3px rgba(0, 176, 48, 0.1);
|
||||
}
|
||||
|
||||
.search-input:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
color: var(--color-text-secondary);
|
||||
opacity: 0.6;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -16,12 +16,21 @@ import GradientButton from '@/components/GradientButton.vue'
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
loadingMore: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
hasMore: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:selectedRowKeys',
|
||||
'export',
|
||||
'batchAnalyze',
|
||||
'loadMore',
|
||||
])
|
||||
|
||||
const defaultColumns = [
|
||||
@@ -78,6 +87,7 @@ function onSelectChange(selectedKeys) {
|
||||
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
|
||||
:rowKey="(record) => String(record.id)"
|
||||
:loading="loading"
|
||||
:scroll="{ y: 'calc(100vh - 400px)' }"
|
||||
class="benchmark-table">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'cover'">
|
||||
@@ -115,6 +125,21 @@ function onSelectChange(selectedKeys) {
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div v-if="hasMore" class="load-more-footer">
|
||||
<a-button
|
||||
:loading="loadingMore"
|
||||
@click="$emit('loadMore')"
|
||||
size="large"
|
||||
>
|
||||
加载更多内容
|
||||
</a-button>
|
||||
<div class="load-more-hint">已加载 {{ data.length }} 条</div>
|
||||
</div>
|
||||
<div v-else-if="data.length > 0" class="no-more-hint">
|
||||
已加载全部内容
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</section>
|
||||
</template>
|
||||
@@ -187,5 +212,32 @@ function onSelectChange(selectedKeys) {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.load-more-footer {
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.load-more-footer .ant-btn {
|
||||
max-width: 200px;
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.load-more-hint {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.no-more-hint {
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: var(--color-text-secondary);
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -40,12 +40,28 @@ export function useBenchmarkData() {
|
||||
console.log('抖音返回的原始数据:', awemeList[0])
|
||||
data.value = mapFromDouyin(awemeList)
|
||||
console.log('映射后的第一条数据:', data.value[0])
|
||||
|
||||
return {
|
||||
maxCursor: resp?.data?.max_cursor || 0,
|
||||
hasMore: resp?.data?.has_more || !(resp?.data?.aweme_list?.length < (resp?.data?.count || 20)),
|
||||
count: data.value.length,
|
||||
}
|
||||
} else {
|
||||
const notes = resp?.data?.notes || resp?.data?.data || []
|
||||
data.value = mapFromXhs(notes)
|
||||
|
||||
return {
|
||||
maxCursor: resp?.data?.max_cursor || 0,
|
||||
hasMore: resp?.data?.has_more || false,
|
||||
count: data.value.length,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function appendData(newItems) {
|
||||
data.value = [...data.value, ...newItems]
|
||||
}
|
||||
|
||||
async function clearData() {
|
||||
data.value = []
|
||||
selectedRowKeys.value = []
|
||||
@@ -60,6 +76,7 @@ export function useBenchmarkData() {
|
||||
saveTableDataToSession,
|
||||
loadTableDataFromSession,
|
||||
processApiResponse,
|
||||
appendData,
|
||||
clearData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,7 +449,6 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.filter-button {
|
||||
min-width: 80px;
|
||||
|
||||
@media (max-width: 767px) {
|
||||
min-width: auto;
|
||||
|
||||
Reference in New Issue
Block a user