318 lines
16 KiB
HTML
318 lines
16 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>MarketingSystemDataTool</title>
|
||
<link rel="stylesheet" href="./vendor/element-plus.min.css">
|
||
<link rel="stylesheet" href="./styles.css">
|
||
</head>
|
||
<body>
|
||
<div id="app">
|
||
<el-container>
|
||
<el-header height="56px">
|
||
<el-row align="middle" justify="space-between">
|
||
<el-col :span="24">
|
||
<div class="title">导出工具</div>
|
||
</el-col>
|
||
</el-row>
|
||
</el-header>
|
||
<el-main>
|
||
<el-row :gutter="16">
|
||
<el-col :span="24">
|
||
<el-card>
|
||
<template #header>
|
||
<div style="display:flex;align-items:center;gap:8px">
|
||
<span>导出模版</span>
|
||
<el-button type="primary" size="small" @click="createVisible=true">新增模板</el-button>
|
||
</div>
|
||
</template>
|
||
<el-table :data="templates" size="small" stripe>
|
||
<el-table-column prop="id" label="ID" width="80"></el-table-column>
|
||
<el-table-column prop="name" label="名称"></el-table-column>
|
||
<el-table-column label="数据源" width="120">
|
||
<template #default="scope">{{ dsLabel(scope.row.datasource) }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="file_format" label="格式" width="100"></el-table-column>
|
||
<el-table-column prop="field_count" label="字段数" width="100"></el-table-column>
|
||
|
||
<el-table-column label="操作" width="260">
|
||
<template #default="scope">
|
||
<el-button size="small" type="primary" @click="openExport(scope.row)">执行</el-button>
|
||
<el-button size="small" @click="openJobs(scope.row)">任务</el-button>
|
||
<el-button
|
||
v-if="(!hasUserId) || (hasUserId && Number(scope.row.owner_id)===currentUserId)"
|
||
size="small"
|
||
@click="openEdit(scope.row)">编辑</el-button>
|
||
<el-button
|
||
v-if="(!hasUserId) || (Number(scope.row.owner_id)!==0 && Number(scope.row.owner_id)===currentUserId)"
|
||
size="small"
|
||
type="danger"
|
||
@click="removeTemplate(scope.row.id)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</el-card>
|
||
</el-col>
|
||
<el-col :span="24">
|
||
<el-dialog v-model="jobsVisible" :title="'导出任务(模板 '+ (jobsTplId||'') +')'" width="1000px">
|
||
<el-table :data="jobs" size="small" stripe row-key="id">
|
||
<el-table-column prop="id" label="ID"></el-table-column>
|
||
<el-table-column label="校验状态">
|
||
<template #default="scope">{{ scope.row.eval_status || '评估中' }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="进度">
|
||
<template #default="scope">{{ jobPercent(scope.row) }}</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column label="行数">
|
||
<template #default="scope">
|
||
{{ Number(scope.row.row_estimate||0) > 0 ? Number(scope.row.row_estimate||0) : '评估中' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="total_rows" label="已写行数"></el-table-column>
|
||
<el-table-column prop="file_format" label="格式"></el-table-column>
|
||
<el-table-column label="创建时间">
|
||
<template #default="scope">{{ fmtDT(new Date(scope.row.created_at)) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="200">
|
||
<template #default="scope">
|
||
<el-button size="small" @click="openSQL(scope.row.id)">分析</el-button>
|
||
<el-button v-if="scope.row.status==='completed' && Number(scope.row.total_rows)>0" size="small" type="success" @click="download(scope.row.id)">下载</el-button>
|
||
<el-button v-else-if="scope.row.status==='completed'" size="small" disabled>无数据</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<div style="display:flex;justify-content:flex-end;margin-top:8px">
|
||
<el-pagination background layout="prev, pager, next, total" :total="jobsTotal" :page-size="jobsPageSize" v-model:currentPage="jobsPage" @current-change="loadJobs" />
|
||
</div>
|
||
<template #footer>
|
||
<el-button @click="closeJobs">关闭</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
<el-dialog v-model="sqlVisible" title="生成SQL" width="800px">
|
||
<div style="max-height:50vh;overflow:auto"><pre style="white-space:pre-wrap">{{ sqlText }}</pre></div>
|
||
<template #footer>
|
||
<div style="display:flex; align-items:center; justify-content:space-between; gap:12px; width:100%">
|
||
<div style="flex:1; min-width:0; text-align:left; color:#606266">
|
||
<el-text v-if="sqlExplainDesc" type="info" truncated>{{ sqlExplainDesc }}</el-text>
|
||
<el-text v-else type="info">评估中或暂无分析结果</el-text>
|
||
</div>
|
||
<div style="display:flex; gap:8px">
|
||
<el-button @click="sqlVisible=false">关闭</el-button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
</el-col>
|
||
</el-row>
|
||
</el-main>
|
||
</el-container>
|
||
<el-dialog v-model="createVisible" title="新增模板" :width="createWidth">
|
||
<el-form ref="createFormRef" :model="form" :rules="createRules" label-width="110px" status-icon>
|
||
<el-form-item label="模板名称" required show-message prop="name"><el-input v-model="form.name" placeholder="模板名称" /></el-form-item>
|
||
<el-form-item label="数据源" required show-message>
|
||
<el-select v-model="form.datasource" placeholder="选择" :teleported="false">
|
||
<el-option v-for="opt in datasourceOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="导出场景" required show-message>
|
||
<el-select v-model="form.main_table" placeholder="订单数据">
|
||
<el-option v-for="opt in sceneOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="订单类型" required show-message prop="orderType">
|
||
<el-radio-group v-model="form.orderType">
|
||
<el-radio v-for="opt in orderTypeOptionsFor(form.datasource)" :key="opt.value" :label="opt.value">{{ opt.label }}</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<el-form-item label="字段选择" required show-message prop="fieldsSel">
|
||
<div style="border: 1px solid #dcdfe6; border-radius: 4px; padding: 8px; max-height: 300px; overflow-y: auto; background: #fff;">
|
||
<el-tree
|
||
ref="createFieldsTree"
|
||
:data="fieldTreeData"
|
||
:key="form.datasource + '-' + String(form.orderType)"
|
||
show-checkbox
|
||
node-key="value"
|
||
:default-expand-all="false"
|
||
:check-strictly="false"
|
||
:props="{ children: 'children', label: 'label' }"
|
||
@check="onTreeCheck('create', $event)"
|
||
style="background: transparent;"
|
||
/>
|
||
</div>
|
||
<div v-if="form.fieldsSel && form.fieldsSel.length > 0" style="margin-top: 8px; font-size: 12px; color: #909399;">
|
||
已选择 {{ form.fieldsSel.length }} 个字段
|
||
</div>
|
||
</el-form-item>
|
||
|
||
|
||
<el-row :gutter="8">
|
||
<el-col :span="12">
|
||
<el-form-item label="输出格式" required show-message prop="file_format">
|
||
<el-select v-model="form.file_format" :teleported="false" placeholder="请选择" style="width:160px">
|
||
<el-option v-for="opt in formatOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="可见性" required show-message prop="visibility">
|
||
<el-select v-model="form.visibility" clearable :teleported="false" style="width:160px" placeholder="请选择">
|
||
<el-option v-for="opt in visibilityOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="resizeDialog('create', -100)">缩小</el-button>
|
||
<el-button @click="resizeDialog('create', 100)">放大</el-button>
|
||
<el-button @click="createVisible=false">取消</el-button>
|
||
<el-button type="primary" @click="createTemplate">创建</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
<el-dialog v-model="editVisible" title="编辑模板" :width="editWidth">
|
||
<el-form ref="editFormRef" :model="edit" :rules="editRules" label-width="110px">
|
||
<el-form-item label="模板名称" required show-message prop="name"><el-input v-model="edit.name" /></el-form-item>
|
||
<el-form-item label="数据源">
|
||
<el-select v-model="edit.datasource" placeholder="选择" :teleported="false" style="width:160px">
|
||
<el-option v-for="opt in datasourceOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="导出场景">
|
||
<el-select v-model="edit.main_table" placeholder="订单数据" style="width:160px">
|
||
<el-option v-for="opt in editSceneOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="订单类型" required show-message prop="orderType">
|
||
<el-radio-group v-model="edit.orderType">
|
||
<el-radio v-for="opt in orderTypeOptionsFor(edit.datasource)" :key="opt.value" :label="opt.value">{{ opt.label }}</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<el-form-item label="字段选择" required show-message prop="fieldsSel">
|
||
<div style="border: 1px solid #dcdfe6; border-radius: 4px; padding: 8px; max-height: 300px; overflow-y: auto; background: #fff;">
|
||
<el-tree
|
||
ref="editFieldsTree"
|
||
:data="editFieldTreeData"
|
||
:key="edit.datasource + '-' + String(edit.orderType)"
|
||
show-checkbox
|
||
node-key="value"
|
||
:default-expand-all="false"
|
||
:check-strictly="false"
|
||
:props="{ children: 'children', label: 'label' }"
|
||
@check="onTreeCheck('edit', $event)"
|
||
style="background: transparent;"
|
||
/>
|
||
</div>
|
||
<div v-if="edit.fieldsSel && edit.fieldsSel.length > 0" style="margin-top: 8px; font-size: 12px; color: #909399;">
|
||
已选择 {{ edit.fieldsSel.length }} 个字段
|
||
</div>
|
||
</el-form-item>
|
||
<el-row :gutter="8">
|
||
<el-col :span="12">
|
||
<el-form-item label="输出格式">
|
||
<el-select v-model="edit.file_format" :teleported="false" placeholder="请选择" style="width:160px">
|
||
<el-option v-for="opt in formatOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="可见性">
|
||
<el-select v-model="edit.visibility" clearable :disabled="hasUserId" :teleported="false" style="width:160px" placeholder="请选择">
|
||
<el-option v-for="opt in visibilityOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="resizeDialog('edit', -100)">缩小</el-button>
|
||
<el-button @click="resizeDialog('edit', 100)">放大</el-button>
|
||
<el-button @click="editVisible=false">取消</el-button>
|
||
<el-button type="primary" @click="saveEdit">保存</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
<el-dialog v-model="exportVisible" :title="exportTitle" width="1100px">
|
||
<el-form ref="exportFormRef" :model="exportForm" :rules="exportRules" label-width="110px" status-icon>
|
||
<div class="section-title">筛选条件</div>
|
||
<el-row :gutter="8">
|
||
<el-col :span="24">
|
||
<el-form-item label="时间范围" required show-message prop="dateRange">
|
||
<el-date-picker v-model="exportForm.dateRange" type="datetimerange" start-placeholder="开始时间" end-placeholder="结束时间" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="8" v-if="isOrder && exportForm.datasource==='ymt'">
|
||
<el-col :span="8">
|
||
<el-form-item label="创建者" prop="ymtCreatorId">
|
||
<el-select v-model.number="exportForm.ymtCreatorId" :disabled="hasUserId" clearable filterable :teleported="false" placeholder="请选择创建者" style="width:100%">
|
||
<el-option v-for="opt in ymtCreatorOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="客户" prop="ymtMerchantId">
|
||
<el-select v-model.number="exportForm.ymtMerchantId" :disabled="!exportForm.ymtCreatorId" clearable filterable :teleported="false" placeholder="请选择客户" style="width:100%">
|
||
<el-option v-for="opt in ymtMerchantOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="活动" prop="ymtActivityId">
|
||
<el-select v-model.number="exportForm.ymtActivityId" :disabled="!exportForm.ymtMerchantId" clearable filterable :teleported="false" placeholder="请选择活动" style="width:100%">
|
||
<el-option v-for="opt in ymtActivityOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="8" v-if="isOrder && exportForm.datasource==='marketing'">
|
||
<el-col :span="12">
|
||
<el-form-item label="创建者" prop="creator">
|
||
<el-select v-model="exportForm.creatorIds" multiple filterable :disabled="hasUserId" :teleported="false" placeholder="请选择创建者" style="width:100%">
|
||
<el-option v-for="opt in creatorOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="分销商" prop="resellerId">
|
||
<el-select v-model.number="exportForm.resellerId" filterable :teleported="false" placeholder="请选择分销商" style="width:100%" :disabled="!hasCreators">
|
||
<el-option v-for="opt in resellerOptions" :key="opt.value" :label="opt.label||String(opt.value)" :value="opt.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="8" v-if="isOrder && exportForm.datasource==='marketing'">
|
||
<el-col :span="12">
|
||
<el-form-item label="计划" prop="planId">
|
||
<el-select v-model="exportForm.planId" :disabled="!hasReseller" :teleported="false" filterable placeholder="选择计划">
|
||
<el-option v-for="opt in planOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
|
||
<el-row :gutter="8" v-if="isOrder">
|
||
<el-col :span="12" v-if="(exportForm.datasource==='marketing' && exportType===2) || (exportForm.datasource==='ymt' && exportType===3)">
|
||
<el-form-item label="立减金批次号" prop="voucherChannelActivityId"><el-input v-model="exportForm.voucherChannelActivityId" placeholder="请输入立减金批次号" /></el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
|
||
<!-- 输出格式按模板设置,不在此显示 -->
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="exportVisible=false">取消</el-button>
|
||
<el-button type="primary" :loading="exportSubmitting" :disabled="exportSubmitting" @click="submitExport">{{ exportSubmitting ? '估算中...' : '执行并分析' }}</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
</div>
|
||
<script src="./config.js"></script>
|
||
<script src="./vendor/vue.global.prod.js" defer></script>
|
||
<script src="./vendor/element-plus.full.min.js" defer></script>
|
||
<script src="./main.js" defer></script>
|
||
</body>
|
||
</html>
|