305 lines
13 KiB
JavaScript
305 lines
13 KiB
JavaScript
const { createApp, reactive } = Vue;
|
|
const app = createApp({
|
|
setup(){
|
|
const state = reactive({
|
|
templates: [],
|
|
job: {},
|
|
form: {
|
|
name: '',
|
|
datasource: 'marketing',
|
|
main_table: 'order',
|
|
orderType: 1,
|
|
fieldsRaw: 'order_number,creator,out_trade_no,type,status,contract_price,num,total,pay_amount,create_time',
|
|
fieldsSel: [],
|
|
creatorRaw: '',
|
|
permissionMode: 'all',
|
|
timeRange: [],
|
|
file_format: 'xlsx',
|
|
visibility: 'private',
|
|
owner_id: '1'
|
|
},
|
|
createVisible: false,
|
|
editVisible: false,
|
|
exportVisible: false,
|
|
createWidth: (localStorage.getItem('tplDialogWidth') || '900px'),
|
|
editWidth: (localStorage.getItem('tplEditDialogWidth') || '600px'),
|
|
edit: { id: null, name: '', visibility: 'private', file_format: 'csv' },
|
|
export: { tplId: null, orderType: 1, permissionMode: 'all', creatorRaw: '', timeRange: [], file_format: 'xlsx' }
|
|
})
|
|
const FIELDS_MAP = {
|
|
marketing: {
|
|
order: [
|
|
{ value: 'order_number', label: '订单编号' },
|
|
{ value: 'creator', label: '创建者ID' },
|
|
{ value: 'out_trade_no', label: '支付流水号' },
|
|
{ value: 'type', label: '订单类型' },
|
|
{ value: 'status', label: '订单状态' },
|
|
{ value: 'contract_price', label: '合同单价' },
|
|
{ value: 'num', label: '数量' },
|
|
{ value: 'total', label: '总金额' },
|
|
{ value: 'pay_amount', label: '支付金额' },
|
|
{ value: 'create_time', label: '创建时间' },
|
|
{ value: 'update_time', label: '更新时间' }
|
|
]
|
|
,
|
|
order_detail: [
|
|
{ value: 'plan_title', label: '计划标题' },
|
|
{ value: 'reseller_name', label: '分销商名称' },
|
|
{ value: 'product_name', label: '商品名称' },
|
|
{ value: 'show_url', label: '商品图片URL' },
|
|
{ value: 'official_price', label: '官方价' },
|
|
{ value: 'cost_price', label: '成本价' },
|
|
{ value: 'create_time', label: '创建时间' },
|
|
{ value: 'update_time', label: '更新时间' }
|
|
],
|
|
order_cash: [
|
|
{ value: 'channel', label: '渠道' },
|
|
{ value: 'cash_activity_id', label: '红包批次号' },
|
|
{ value: 'receive_status', label: '领取状态' },
|
|
{ value: 'receive_time', label: '拆红包时间' },
|
|
{ value: 'cash_packet_id', label: '红包ID' },
|
|
{ value: 'cash_id', label: '红包规则ID' },
|
|
{ value: 'amount', label: '红包额度' },
|
|
{ value: 'status', label: '状态' },
|
|
{ value: 'expire_time', label: '过期时间' },
|
|
{ value: 'update_time', label: '更新时间' }
|
|
],
|
|
order_voucher: [
|
|
{ value: 'channel', label: '渠道' },
|
|
{ value: 'channel_activity_id', label: '渠道立减金批次' },
|
|
{ value: 'channel_voucher_id', label: '渠道立减金ID' },
|
|
{ value: 'status', label: '状态' },
|
|
{ value: 'grant_time', label: '领取时间' },
|
|
{ value: 'usage_time', label: '核销时间' },
|
|
{ value: 'refund_time', label: '退款时间' },
|
|
{ value: 'status_modify_time', label: '状态更新时间' },
|
|
{ value: 'overdue_time', label: '过期时间' },
|
|
{ value: 'refund_amount', label: '退款金额' },
|
|
{ value: 'official_price', label: '官方价' },
|
|
{ value: 'out_biz_no', label: '外部业务号' },
|
|
{ value: 'account_no', label: '账户号' }
|
|
],
|
|
plan: [
|
|
{ value: 'id', label: '计划ID' },
|
|
{ value: 'title', label: '计划标题' },
|
|
{ value: 'status', label: '状态' },
|
|
{ value: 'begin_time', label: '开始时间' },
|
|
{ value: 'end_time', label: '结束时间' }
|
|
],
|
|
key_batch: [
|
|
{ value: 'id', label: '批次ID' },
|
|
{ value: 'batch_name', label: '批次名称' },
|
|
{ value: 'bind_object', label: '绑定对象' },
|
|
{ value: 'quantity', label: '发放数量' },
|
|
{ value: 'stock', label: '剩余库存' },
|
|
{ value: 'begin_time', label: '开始时间' },
|
|
{ value: 'end_time', label: '结束时间' }
|
|
],
|
|
code_batch: [
|
|
{ value: 'id', label: '兑换批次ID' },
|
|
{ value: 'title', label: '标题' },
|
|
{ value: 'status', label: '状态' },
|
|
{ value: 'begin_time', label: '开始时间' },
|
|
{ value: 'end_time', label: '结束时间' },
|
|
{ value: 'quantity', label: '数量' },
|
|
{ value: 'usage', label: '使用数' },
|
|
{ value: 'stock', label: '库存' }
|
|
],
|
|
voucher: [
|
|
{ value: 'channel', label: '渠道' },
|
|
{ value: 'channel_activity_id', label: '渠道批次号' },
|
|
{ value: 'price', label: '合同单价' },
|
|
{ value: 'balance', label: '剩余额度' },
|
|
{ value: 'used_amount', label: '已用额度' },
|
|
{ value: 'denomination', label: '面额' }
|
|
],
|
|
voucher_batch: [
|
|
{ value: 'channel_activity_id', label: '渠道批次号' },
|
|
{ value: 'temp_no', label: '模板编号' },
|
|
{ value: 'provider', label: '服务商' },
|
|
{ value: 'weight', label: '权重' }
|
|
],
|
|
merchant_key_send: [
|
|
{ value: 'merchant_id', label: '商户ID' },
|
|
{ value: 'out_biz_no', label: '商户业务号' },
|
|
{ value: 'key', label: '券码' },
|
|
{ value: 'status', label: '状态' },
|
|
{ value: 'usage_time', label: '核销时间' },
|
|
{ value: 'create_time', label: '创建时间' }
|
|
]
|
|
}
|
|
}
|
|
const TABLE_LABELS = {
|
|
order: '订单主表',
|
|
order_detail: '订单详情',
|
|
order_cash: '红包订单',
|
|
order_voucher: '立减金订单',
|
|
plan: '活动计划',
|
|
key_batch: 'KEY批次',
|
|
code_batch: '兑换码批次',
|
|
voucher: '立减金',
|
|
voucher_batch: '立减金批次',
|
|
merchant_key_send: '开放平台发放记录'
|
|
}
|
|
const fieldOptions = Vue.computed(()=>{
|
|
const ds = state.form.datasource
|
|
const FM = FIELDS_MAP[ds] || {}
|
|
const node = (table, children=[])=>({ value: table, label: TABLE_LABELS[table]||table, children })
|
|
const fieldsNode = (table)=> (FM[table]||[])
|
|
const orderChildrenBase = []
|
|
orderChildrenBase.push(...fieldsNode('order'))
|
|
orderChildrenBase.push(node('order_detail', fieldsNode('order_detail')))
|
|
const planChildren = []
|
|
planChildren.push(...fieldsNode('plan'))
|
|
planChildren.push(node('key_batch', [
|
|
...fieldsNode('key_batch'),
|
|
node('code_batch', fieldsNode('code_batch'))
|
|
]))
|
|
const voucherChildren = []
|
|
voucherChildren.push(...fieldsNode('order_voucher'))
|
|
voucherChildren.push(node('voucher', [
|
|
...fieldsNode('voucher'),
|
|
node('voucher_batch', fieldsNode('voucher_batch'))
|
|
]))
|
|
const orderChildrenFor = (type)=>{
|
|
const ch = [...orderChildrenBase]
|
|
if(type===1){
|
|
// 直充卡密:排除红包与立减金
|
|
ch.push(node('plan', planChildren))
|
|
ch.push(node('merchant_key_send', fieldsNode('merchant_key_send')))
|
|
} else if(type===2){
|
|
// 立减金:排除红包,保留立减金链
|
|
ch.push(node('order_voucher', voucherChildren))
|
|
ch.push(node('plan', planChildren))
|
|
} else if(type===3){
|
|
// 红包:仅红包链
|
|
ch.push(node('order_cash', fieldsNode('order_cash')))
|
|
ch.push(node('plan', planChildren))
|
|
} else {
|
|
// 未选择类型:全部显示
|
|
ch.push(node('order_cash', fieldsNode('order_cash')))
|
|
ch.push(node('order_voucher', voucherChildren))
|
|
ch.push(node('plan', planChildren))
|
|
ch.push(node('merchant_key_send', fieldsNode('merchant_key_send')))
|
|
}
|
|
return ch
|
|
}
|
|
const type = Number(state.form.orderType || 0)
|
|
const orderNode = node('order', orderChildrenFor(type))
|
|
if(type){
|
|
return [ orderNode ]
|
|
}
|
|
return [
|
|
{ value: 'scene_order', label: '订单数据', children: [ orderNode ] }
|
|
]
|
|
})
|
|
const msg = (t, type='success')=>ElementPlus.ElMessage({message:t,type});
|
|
const visibilityOptions = [
|
|
{ label: '个人', value: 'private' },
|
|
{ label: '公共', value: 'public' }
|
|
]
|
|
const formatOptions = [
|
|
{ label: 'XLSX', value: 'xlsx' },
|
|
{ label: 'CSV', value: 'csv' }
|
|
]
|
|
const fmtDT = (d)=>{
|
|
const pad=(n)=>String(n).padStart(2,'0');
|
|
return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
}
|
|
const loadTemplates = async ()=>{
|
|
const res = await fetch('/api/templates');
|
|
state.templates = await res.json();
|
|
}
|
|
const createTemplate = async ()=>{
|
|
let fields = []
|
|
if(state.form.fieldsSel && state.form.fieldsSel.length){
|
|
fields = state.form.fieldsSel.map(path=>{
|
|
if(Array.isArray(path)){
|
|
if(path.length===4){ return `${path[2]}.${path[3]}` }
|
|
if(path.length===3){ return `${path[1]}.${path[2]}` }
|
|
if(path.length===2){ return `${path[0]}.${path[1]}` }
|
|
}
|
|
return path
|
|
})
|
|
} else {
|
|
fields = state.form.fieldsRaw.split(',').map(s=>s.trim()).filter(Boolean)
|
|
}
|
|
const payload = {
|
|
name: state.form.name,
|
|
datasource: state.form.datasource,
|
|
main_table: state.form.main_table,
|
|
fields,
|
|
file_format: state.form.file_format,
|
|
owner_id: Number(state.form.owner_id),
|
|
visibility: state.form.visibility
|
|
}
|
|
const res = await fetch('/api/templates',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)});
|
|
if(res.ok){ msg('创建成功'); state.createVisible=false; loadTemplates() } else { msg(await res.text(),'error') }
|
|
}
|
|
const openExport = (row)=>{
|
|
state.export.tplId = row.id
|
|
state.export.file_format = row.file_format || 'xlsx'
|
|
state.exportVisible = true
|
|
}
|
|
const submitExport = async ()=>{
|
|
const id = state.export.tplId
|
|
const filters = {}
|
|
if(state.export.permissionMode==='creator' && state.export.creatorRaw){
|
|
filters.creator_in = state.export.creatorRaw.split(',').map(s=>Number(s.trim())).filter(x=>!isNaN(x))
|
|
}
|
|
if(state.export.orderType){ filters.type_eq = Number(state.export.orderType) }
|
|
if(state.export.timeRange && state.export.timeRange.length===2){
|
|
filters.create_time_between = [fmtDT(new Date(state.export.timeRange[0])), fmtDT(new Date(state.export.timeRange[1]))]
|
|
}
|
|
const payload={template_id:Number(id),requested_by:1,permission:{},options:{},filters, file_format: state.export.file_format};
|
|
const r=await fetch('/api/exports',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)});
|
|
const j=await r.json();
|
|
state.exportVisible=false
|
|
loadJob(j.id);
|
|
}
|
|
const clampWidth = (w)=>{
|
|
const n = Math.max(500, Math.min(1400, w))
|
|
return n + 'px'
|
|
}
|
|
const resizeDialog = (kind, delta)=>{
|
|
if(kind==='create'){
|
|
const cur = parseInt(String(state.createWidth).replace('px','')||'900',10)
|
|
const next = clampWidth(cur + delta)
|
|
state.createWidth = next
|
|
localStorage.setItem('tplDialogWidth', next)
|
|
} else if(kind==='edit'){
|
|
const cur = parseInt(String(state.editWidth).replace('px','')||'600',10)
|
|
const next = clampWidth(cur + delta)
|
|
state.editWidth = next
|
|
localStorage.setItem('tplEditDialogWidth', next)
|
|
}
|
|
}
|
|
const openEdit = (row)=>{
|
|
state.edit.id = row.id
|
|
state.edit.name = row.name
|
|
state.edit.visibility = row.visibility
|
|
state.edit.file_format = row.file_format
|
|
state.editVisible = true
|
|
}
|
|
const saveEdit = async ()=>{
|
|
const id = state.edit.id
|
|
const payload = { name: state.edit.name, visibility: state.edit.visibility, file_format: state.edit.file_format }
|
|
const res = await fetch('/api/templates/'+id,{method:'PATCH',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)})
|
|
if(res.ok){ msg('保存成功'); state.editVisible=false; loadTemplates() } else { msg(await res.text(),'error') }
|
|
}
|
|
const removeTemplate = async (id)=>{
|
|
const r = await fetch('/api/templates/'+id,{method:'DELETE'})
|
|
if(r.ok){ msg('删除成功'); loadTemplates() } else { msg(await r.text(),'error') }
|
|
}
|
|
const loadJob = async (id)=>{
|
|
const res=await fetch('/api/exports/'+id);
|
|
state.job = await res.json();
|
|
}
|
|
const download = (id)=>{ window.open('/api/exports/'+id+'/download','_blank') }
|
|
loadTemplates()
|
|
return { ...Vue.toRefs(state), visibilityOptions, formatOptions, fieldOptions, loadTemplates, createTemplate, openExport, submitExport, loadJob, download, openEdit, saveEdit, removeTemplate, resizeDialog }
|
|
}
|
|
})
|
|
app.use(ElementPlus)
|
|
app.mount('#app')
|