367 lines
12 KiB
Vue
367 lines
12 KiB
Vue
<template>
|
|
<div class="content-view">
|
|
<div class="table-head-tool">
|
|
<div class="table-title-row">
|
|
<Button type="primary" @click="openOpen">开通订阅</Button>
|
|
</div>
|
|
|
|
<Form ref="formInline" :model="param.seachOption" inline :label-width="80">
|
|
<FormItem label="筛选">
|
|
<Select v-model="param.seachOption.key" style="width: 120px" @on-change="onSearchKeyChange">
|
|
<Option value="user_id">用户</Option>
|
|
<Option value="status">状态</Option>
|
|
</Select>
|
|
</FormItem>
|
|
<FormItem v-if="param.seachOption.key === 'user_id'">
|
|
<Select
|
|
v-model="param.seachOption.value"
|
|
filterable
|
|
clearable
|
|
placeholder="选择用户"
|
|
style="width: 260px"
|
|
class="ml10"
|
|
>
|
|
<Option v-for="u in bizUserOptions" :key="u.id" :value="String(u.id)">{{ bizUserLabel(u) }}</Option>
|
|
</Select>
|
|
</FormItem>
|
|
<FormItem v-else>
|
|
<Input v-model="param.seachOption.value" style="width: 160px" placeholder="pending / active …" class="ml10" />
|
|
</FormItem>
|
|
<FormItem>
|
|
<Button type="primary" @click="load(1)">查询</Button>
|
|
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
|
<Button type="default" @click="doExport" class="ml10">导出 CSV</Button>
|
|
</FormItem>
|
|
</Form>
|
|
</div>
|
|
|
|
<div class="table-body">
|
|
<Table :columns="columns" :data="rows" border stripe />
|
|
<div class="table-page-bar">
|
|
<Page :total="total" :current="param.pageOption.page" :page-size="param.pageOption.pageSize" show-total
|
|
@on-change="onPage" @on-page-size-change="onSize" />
|
|
</div>
|
|
</div>
|
|
|
|
<Modal v-model="openModal" title="开通订阅" width="640" :loading="saving" @on-ok="submitOpen">
|
|
<Form :label-width="110">
|
|
<FormItem label="用户">
|
|
<Select v-model="openForm.user_id" filterable clearable placeholder="请选择" style="width: 100%">
|
|
<Option v-for="u in bizUserOptions" :key="u.id" :value="u.id">{{ bizUserLabel(u) }}</Option>
|
|
</Select>
|
|
</FormItem>
|
|
<FormItem label="套餐">
|
|
<Select v-model="openForm.plan_id" filterable clearable placeholder="请选择" style="width: 100%">
|
|
<Option v-for="p in bizPlanOptions" :key="p.id" :value="p.id">{{ bizPlanLabel(p) }}</Option>
|
|
</Select>
|
|
</FormItem>
|
|
<FormItem label="开始时间"><Input v-model="openForm.start_time" placeholder="2025-01-01 00:00:00" /></FormItem>
|
|
<FormItem label="结束时间"><Input v-model="openForm.end_time" placeholder="2025-12-31 23:59:59" /></FormItem>
|
|
<FormItem label="状态">
|
|
<Select v-model="openForm.status" style="width: 100%">
|
|
<Option value="pending">pending</Option>
|
|
<Option value="active">active</Option>
|
|
</Select>
|
|
</FormItem>
|
|
<FormItem label="续费方式">
|
|
<Select v-model="openForm.renew_mode" style="width: 100%">
|
|
<Option value="manual">manual</Option>
|
|
<Option value="auto">auto</Option>
|
|
</Select>
|
|
</FormItem>
|
|
<FormItem label="支付渠道">
|
|
<Select v-model="openForm.payment_channel" clearable style="width: 100%">
|
|
<Option value="offline">offline</Option>
|
|
<Option value="pay_link">pay_link</Option>
|
|
</Select>
|
|
</FormItem>
|
|
<FormItem label="支付单号"><Input v-model="openForm.payment_ref" /></FormItem>
|
|
</Form>
|
|
</Modal>
|
|
|
|
<Modal v-model="renewModal" title="续费" :loading="saving" @on-ok="submitRenew">
|
|
<Form :label-width="100">
|
|
<FormItem label="新结束时间"><Input v-model="renewForm.end_time" /></FormItem>
|
|
</Form>
|
|
</Modal>
|
|
|
|
<Modal v-model="upgradeModal" title="编辑订阅" :loading="saving" @on-ok="submitUpgrade">
|
|
<Form :label-width="100">
|
|
<FormItem label="套餐">
|
|
<Select v-model="upgradeForm.new_plan_id" filterable clearable placeholder="请选择" style="width: 100%">
|
|
<Option v-for="p in bizPlanOptions" :key="p.id" :value="p.id">{{ bizPlanLabel(p) }}</Option>
|
|
</Select>
|
|
</FormItem>
|
|
<FormItem label="开始"><Input v-model="upgradeForm.start_time" /></FormItem>
|
|
<FormItem label="结束"><Input v-model="upgradeForm.end_time" /></FormItem>
|
|
</Form>
|
|
</Modal>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import subscriptionsServer from '@/api/subscription/subscriptions_server.js'
|
|
import { downloadCsvFromRows } from '@/utils/csvExport.js'
|
|
import subscriptionRelations from '@/mixins/subscriptionRelations.js'
|
|
|
|
export default {
|
|
name: 'SubscriptionRecords',
|
|
mixins: [subscriptionRelations],
|
|
data() {
|
|
return {
|
|
rows: [],
|
|
total: 0,
|
|
param: {
|
|
seachOption: { key: 'user_id', value: '' },
|
|
pageOption: { page: 1, pageSize: 20, total: 0 },
|
|
},
|
|
openModal: false,
|
|
renewModal: false,
|
|
upgradeModal: false,
|
|
saving: false,
|
|
currentRow: null,
|
|
openForm: {},
|
|
renewForm: { end_time: '' },
|
|
upgradeForm: { new_plan_id: undefined, start_time: '', end_time: '' },
|
|
}
|
|
},
|
|
computed: {
|
|
columns() {
|
|
return [
|
|
{ title: 'ID', key: 'id', width: 70 },
|
|
{ title: '用户', key: 'user_id', width: 90 },
|
|
{ title: '套餐', key: 'plan_id', width: 90 },
|
|
{ title: '状态', key: 'status', width: 100 },
|
|
{ title: '开始', key: 'start_time', minWidth: 150 },
|
|
{ title: '结束', key: 'end_time', minWidth: 150 },
|
|
{
|
|
title: '操作',
|
|
key: 'a',
|
|
width: 200,
|
|
render: (h, p) =>
|
|
h('div', [
|
|
h('Button', { props: { size: 'small' }, on: { click: () => this.openRenew(p.row) } }, '续费'),
|
|
h('Button', { props: { size: 'small' }, class: { ml8: true }, on: { click: () => this.openUpgrade(p.row) } }, '编辑'),
|
|
h('Button', { props: { type: 'error', size: 'small' }, class: { ml8: true }, on: { click: () => this.doCancel(p.row) } }, '取消'),
|
|
]),
|
|
},
|
|
]
|
|
},
|
|
},
|
|
async mounted() {
|
|
await this.loadBizRelationOptions()
|
|
this.load(1)
|
|
},
|
|
methods: {
|
|
onSearchKeyChange() {
|
|
this.param.seachOption.value = ''
|
|
},
|
|
async load(page) {
|
|
if (page) this.param.pageOption.page = page
|
|
const res = await subscriptionsServer.page({ param: this.param })
|
|
if (res && res.code === 0) {
|
|
this.rows = res.data.rows || []
|
|
this.total = res.data.count || 0
|
|
} else {
|
|
this.$Message.error((res && res.message) || '加载失败')
|
|
}
|
|
},
|
|
onPage(p) {
|
|
this.param.pageOption.page = p
|
|
this.load()
|
|
},
|
|
onSize(s) {
|
|
this.param.pageOption.pageSize = s
|
|
this.load(1)
|
|
},
|
|
openOpen() {
|
|
const now = new Date()
|
|
const end = new Date(now)
|
|
end.setFullYear(end.getFullYear() + 1)
|
|
const fmt = (d) =>
|
|
`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ${String(
|
|
d.getHours()
|
|
).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:00`
|
|
this.openForm = {
|
|
user_id: undefined,
|
|
plan_id: undefined,
|
|
start_time: fmt(now),
|
|
end_time: fmt(end),
|
|
status: 'pending',
|
|
renew_mode: 'manual',
|
|
payment_channel: '',
|
|
payment_ref: '',
|
|
}
|
|
this.openModal = true
|
|
},
|
|
submitOpen() {
|
|
const uid = this.openForm.user_id
|
|
const pid = this.openForm.plan_id
|
|
if (uid === undefined || uid === null || uid === '') {
|
|
this.$Message.warning('请选择用户')
|
|
return false
|
|
}
|
|
if (pid === undefined || pid === null || pid === '') {
|
|
this.$Message.warning('请选择套餐')
|
|
return false
|
|
}
|
|
this.saving = true
|
|
this._submitOpen()
|
|
return false
|
|
},
|
|
async _submitOpen() {
|
|
const uid = this.openForm.user_id
|
|
const pid = this.openForm.plan_id
|
|
try {
|
|
const body = {
|
|
user_id: Number(uid),
|
|
plan_id: Number(pid),
|
|
start_time: this.openForm.start_time,
|
|
end_time: this.openForm.end_time,
|
|
status: this.openForm.status,
|
|
renew_mode: this.openForm.renew_mode,
|
|
payment_channel: this.openForm.payment_channel || null,
|
|
payment_ref: this.openForm.payment_ref || null,
|
|
}
|
|
const res = await subscriptionsServer.open(body)
|
|
if (res && res.code === 0) {
|
|
this.$Message.success('已创建订阅')
|
|
this.openModal = false
|
|
this.load(1)
|
|
} else {
|
|
this.$Message.error((res && res.message) || '失败')
|
|
}
|
|
} finally {
|
|
this.saving = false
|
|
}
|
|
},
|
|
openRenew(row) {
|
|
this.currentRow = row
|
|
this.renewForm = { end_time: row.end_time || '' }
|
|
this.renewModal = true
|
|
},
|
|
async submitRenew() {
|
|
if (!this.currentRow) return
|
|
this.saving = true
|
|
try {
|
|
const res = await subscriptionsServer.renew({
|
|
subscription_id: this.currentRow.id,
|
|
end_time: this.renewForm.end_time,
|
|
})
|
|
if (res && res.code === 0) {
|
|
this.$Message.success('已续费')
|
|
this.renewModal = false
|
|
this.load(1)
|
|
} else {
|
|
this.$Message.error((res && res.message) || '失败')
|
|
}
|
|
} finally {
|
|
this.saving = false
|
|
}
|
|
},
|
|
openUpgrade(row) {
|
|
this.currentRow = row
|
|
this.upgradeForm = { new_plan_id: row.plan_id != null ? Number(row.plan_id) : undefined, start_time: '', end_time: '' }
|
|
this.upgradeModal = true
|
|
},
|
|
submitUpgrade() {
|
|
if (!this.currentRow) return false
|
|
const np = this.upgradeForm.new_plan_id
|
|
if (np === undefined || np === null || np === '') {
|
|
this.$Message.warning('请选择套餐')
|
|
return false
|
|
}
|
|
this.saving = true
|
|
this._submitUpgrade()
|
|
return false
|
|
},
|
|
async _submitUpgrade() {
|
|
if (!this.currentRow) {
|
|
this.saving = false
|
|
return
|
|
}
|
|
const np = this.upgradeForm.new_plan_id
|
|
try {
|
|
const res = await subscriptionsServer.upgrade({
|
|
subscription_id: this.currentRow.id,
|
|
new_plan_id: Number(np),
|
|
start_time: this.upgradeForm.start_time || undefined,
|
|
end_time: this.upgradeForm.end_time || undefined,
|
|
})
|
|
if (res && res.code === 0) {
|
|
this.$Message.success('已保存')
|
|
this.upgradeModal = false
|
|
this.load(1)
|
|
} else {
|
|
this.$Message.error((res && res.message) || '失败')
|
|
}
|
|
} finally {
|
|
this.saving = false
|
|
}
|
|
},
|
|
doCancel(row) {
|
|
this.$Modal.confirm({
|
|
title: '取消订阅',
|
|
content: '确认取消?',
|
|
onOk: async () => {
|
|
const res = await subscriptionsServer.cancel({ subscription_id: row.id })
|
|
if (res && res.code === 0) {
|
|
this.$Message.success('已取消')
|
|
this.load(1)
|
|
} else {
|
|
this.$Message.error((res && res.message) || '失败')
|
|
}
|
|
},
|
|
})
|
|
},
|
|
async doExport() {
|
|
const res = await subscriptionsServer.exportRows({ param: this.param })
|
|
if (res && res.code === 0 && res.data && res.data.rows) {
|
|
downloadCsvFromRows(res.data.rows, 'subscriptions.csv')
|
|
this.$Message.success('已导出')
|
|
} else {
|
|
this.$Message.error((res && res.message) || '导出失败')
|
|
}
|
|
},
|
|
resetQuery() {
|
|
this.param.seachOption = { key: 'user_id', value: '' }
|
|
this.load(1)
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.content-view {
|
|
width: 100%;
|
|
max-width: 1720px;
|
|
margin: 0 auto;
|
|
padding: 26px 36px 36px;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.table-head-tool {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.table-title-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.ml10 {
|
|
margin-left: 10px;
|
|
}
|
|
|
|
.ml8 {
|
|
margin-left: 8px;
|
|
}
|
|
|
|
.table-page-bar {
|
|
margin-top: 12px;
|
|
text-align: right;
|
|
}
|
|
</style>
|