Merge remote-tracking branch 'refs/remotes/origin/dev-mall' into release_20260829

# Conflicts:
#	src/main/java/com/budwk/app/sys/controller/v4/SysV4MsgController.java
#	src/main/resources/views/platform/zhghh5/sys/home/msg.js
This commit is contained in:
2026-08-29 09:12:07 +08:00
157 changed files with 13694 additions and 545 deletions
@@ -0,0 +1,509 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="结算单号">
<el-input clearable placeholder="请输入结算单号" v-model="pageForm.settlementId"></el-input>
</search-item>
<search-item label="开票结果">
<el-select clearable placeholder="请选择开票结果" style="width: 100%" v-model="pageForm.bSuccess">
<el-option label="成功" :value="0"></el-option>
<el-option label="失败" :value="1"></el-option>
</el-select>
</search-item>
<search-item label="成功订单">
<el-input clearable placeholder="请输入订单ID" v-model="pageForm.sucOrderIds"></el-input>
</search-item>
<search-item label="失败订单">
<el-input clearable placeholder="请输入订单ID" v-model="pageForm.failOrderIds"></el-input>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="发票管理">
<el-button @click="openApplyDialog" size="mini" type="success">开票申请</el-button>
<el-button @click="openOrderDialog" size="mini" type="primary">可开票订单</el-button>
</table-tool>
<el-table :data="tableData" border class="vi-table" size="small" style="width: 100%" v-loading="tableLoading">
<el-table-column :index="indexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="结算单号" min-width="180" prop="settlementId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="开票结果" min-width="110">
<template slot-scope="{row}">
<el-tag :type="invoiceResultType(row.bSuccess)" size="mini">{{ invoiceResultName(row.bSuccess) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="成功订单" min-width="220" prop="sucOrderIds" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="失败订单" min-width="220" prop="failOrderIds" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="失败原因" min-width="220" prop="failMsg" show-overflow-tooltip></el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="220">
<template slot-scope="{row}">
<el-button @click="openInfoDialog(row)" size="mini" type="primary">发票明细</el-button>
<el-button @click="openOrderDialog(row.sucOrderIds)" size="mini">成功订单</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="pageForm.pageNumber"
:page-size="pageForm.pageSize"
:page-sizes="[10, 20, 30, 50]"
:total="pageForm.totalCount"
@current-change="pageChange"
@size-change="sizeChange"
layout="total, sizes, prev, pager, next"
style="margin-top: 16px; text-align: right"
></el-pagination>
</el-card>
<el-dialog :close-on-click-modal="false" title="开票申请" :visible.sync="applyDialogVisible" top="5vh" width="720px">
<el-form :model="applyForm" label-width="120px" size="small">
<el-form-item label="供应商" required>
<el-select clearable filterable placeholder="请选择供应商" style="width: 100%" v-model="applyForm.supplierId">
<el-option :key="item.supplierId" :label="item.supplierName" :value="item.supplierId" v-for="item in supplierList"></el-option>
</el-select>
</el-form-item>
<el-form-item label="订单完成日期" required>
<el-date-picker
end-placeholder="结束日期"
range-separator="至"
start-placeholder="开始日期"
style="width: 100%"
type="daterange"
value-format="yyyy-MM-dd"
v-model="applyDateRange"
></el-date-picker>
</el-form-item>
<el-form-item label="订单ID" required>
<el-input placeholder="多个订单ID用英文逗号分隔" type="textarea" v-model="applyForm.orderIds"></el-input>
</el-form-item>
<el-form-item label="开票日期" required>
<el-date-picker placeholder="请选择开票日期" style="width: 100%" type="date" value-format="yyyy-MM-dd" v-model="applyForm.invoiceDate"></el-date-picker>
</el-form-item>
<el-form-item label="发票抬头">
<el-input clearable placeholder="请输入发票抬头" v-model="applyForm.invoiceTitle"></el-input>
</el-form-item>
<el-form-item label="纳税人识别号" required>
<el-input clearable placeholder="请输入纳税人识别号" v-model="applyForm.invoiceCode"></el-input>
</el-form-item>
<el-form-item label="发票地址" required>
<el-input clearable placeholder="请输入发票地址" v-model="applyForm.invoiceAddress"></el-input>
</el-form-item>
<el-form-item label="联系电话" required>
<el-input clearable placeholder="请输入联系电话" v-model="applyForm.invoiceContact"></el-input>
</el-form-item>
<el-form-item label="发票类型">
<el-select clearable placeholder="请选择发票类型" style="width: 100%" v-model="applyForm.invoiceType">
<el-option label="全电专票" :value="9"></el-option>
<el-option label="全电普票" :value="10"></el-option>
</el-select>
</el-form-item>
<el-form-item label="开票内容">
<el-input clearable placeholder="请输入开票内容" v-model="applyForm.invoiceContent"></el-input>
</el-form-item>
<el-form-item label="开户银行">
<el-input clearable placeholder="请输入开户银行" v-model="applyForm.bankName"></el-input>
</el-form-item>
<el-form-item label="银行账号">
<el-input clearable placeholder="请输入银行账号" v-model="applyForm.bankAccount"></el-input>
</el-form-item>
<el-form-item label="备注">
<el-input placeholder="请输入备注" type="textarea" v-model="applyForm.remark"></el-input>
</el-form-item>
<el-form-item label="订单积分合计">
<span>{{ orderTotalPoints }}</span>
<el-button @click="loadOrderTotalPoints" size="mini" style="margin-left: 12px">计算</el-button>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="applyDialogVisible = false">取消</el-button>
<el-button @click="submitApply" :loading="applyLoading" type="primary">提交申请</el-button>
</span>
</el-dialog>
<el-dialog :close-on-click-modal="false" title="可开票订单" :visible.sync="orderDialogVisible" top="5vh" width="90%">
<el-form :inline="true" size="small" @submit.native.prevent>
<el-form-item label="供应商">
<el-select clearable filterable placeholder="供应商" style="width: 180px" v-model="orderForm.supplierId">
<el-option :key="item.supplierId" :label="item.supplierName" :value="item.supplierId" v-for="item in supplierList"></el-option>
</el-select>
</el-form-item>
<el-form-item label="订单ID">
<el-input clearable placeholder="多个订单ID用英文逗号分隔" v-model="orderForm.orderId"></el-input>
</el-form-item>
<el-form-item label="完成日期">
<el-date-picker
end-placeholder="结束日期"
range-separator="至"
start-placeholder="开始日期"
style="width: 260px"
type="daterange"
value-format="yyyy-MM-dd"
v-model="orderDateRange"
></el-date-picker>
</el-form-item>
<el-form-item label="开票状态">
<el-select clearable placeholder="开票状态" style="width: 130px" v-model="orderForm.invoiceStatus">
<el-option label="未开票" :value="0"></el-option>
<el-option label="已开票" :value="1"></el-option>
<el-option label="开票中" :value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="orderSearch" type="primary">查询</el-button>
<el-button @click="orderReset">重置</el-button>
<el-button @click="useOrdersForApply" type="success">用于开票</el-button>
</el-form-item>
</el-form>
<el-table :data="orderList" @selection-change="orderSelectionChange" border size="small" style="width: 100%" v-loading="orderLoading">
<el-table-column align="center" type="selection" width="50"></el-table-column>
<el-table-column :index="orderIndexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="订单ID" min-width="170" prop="orderId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商" min-width="150" prop="supplierName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="用户ID" min-width="120" prop="userId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="完成时间" min-width="160" prop="orderCompleteTime"></el-table-column>
<el-table-column align="center" label="订单总金额" min-width="110" prop="totalPrice"></el-table-column>
<el-table-column align="center" label="积分金额" min-width="110" prop="pointsPrice"></el-table-column>
<el-table-column align="center" label="退款积分" min-width="110" prop="refund"></el-table-column>
<el-table-column align="center" label="开票状态" min-width="100">
<template slot-scope="{row}">
<el-tag :type="invoiceStatusType(row.invoiceStatus)" size="mini">{{ invoiceStatusName(row.invoiceStatus) }}</el-tag>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="orderForm.pageNumber"
:page-size="orderForm.pageSize"
:page-sizes="[10, 20, 30, 50]"
:total="orderForm.totalCount"
@current-change="orderPageChange"
@size-change="orderSizeChange"
layout="total, sizes, prev, pager, next"
style="margin-top: 16px; text-align: right"
></el-pagination>
<span slot="footer">
<el-button @click="orderDialogVisible = false">关闭</el-button>
</span>
</el-dialog>
<el-dialog :close-on-click-modal="false" title="发票明细" :visible.sync="infoDialogVisible" top="5vh" width="90%">
<el-form :inline="true" size="small" @submit.native.prevent>
<el-form-item label="发票号码">
<el-input clearable placeholder="发票号码" v-model="infoForm.invoiceId"></el-input>
</el-form-item>
<el-form-item label="发票代码">
<el-input clearable placeholder="发票代码" v-model="infoForm.invoiceCode"></el-input>
</el-form-item>
<el-form-item label="发票日期">
<el-date-picker clearable placeholder="发票日期" type="date" value-format="yyyy-MM-dd" v-model="infoForm.invoiceDate"></el-date-picker>
</el-form-item>
<el-form-item>
<el-button @click="infoSearch" type="primary">查询</el-button>
</el-form-item>
</el-form>
<el-table :data="infoList" border size="small" style="width: 100%" v-loading="infoLoading">
<el-table-column :index="infoIndexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="发票号码" min-width="150" prop="invoiceId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="发票代码" min-width="130" prop="invoiceCode" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="发票日期" min-width="120" prop="invoiceDate"></el-table-column>
<el-table-column align="center" label="发票类型" min-width="110">
<template slot-scope="{row}">{{ invoiceTypeName(row.invoiceType) }}</template>
</el-table-column>
<el-table-column align="center" label="价税合计" min-width="110" prop="invoiceAmount"></el-table-column>
<el-table-column align="center" label="税额" min-width="100" prop="invoiceTaxAmount"></el-table-column>
<el-table-column align="center" label="纳税人识别号" min-width="160" prop="taxIdNumber" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="电子发票地址" min-width="220" prop="url" show-overflow-tooltip></el-table-column>
</el-table>
<el-pagination
:current-page="infoForm.pageNumber"
:page-size="infoForm.pageSize"
:total="infoForm.totalCount"
@current-change="infoPageChange"
layout="total, prev, pager, next"
style="margin-top: 16px; text-align: right"
></el-pagination>
<span slot="footer">
<el-button @click="infoDialogVisible = false">关闭</el-button>
</span>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
supplierList: [],
tableData: [],
tableLoading: false,
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
settlementId: null,
bSuccess: null,
sucOrderIds: null,
failOrderIds: null,
failMsg: null
},
applyDialogVisible: false,
applyLoading: false,
applyDateRange: [],
orderTotalPoints: 0,
applyForm: this.emptyApplyForm(),
orderDialogVisible: false,
orderLoading: false,
orderDateRange: [],
orderList: [],
orderSelections: [],
orderForm: this.emptyOrderForm(),
infoDialogVisible: false,
infoLoading: false,
infoList: [],
infoForm: this.emptyInfoForm()
}
},
methods: {
emptyApplyForm() {
return {
supplierId: null,
orderIds: null,
startDate: null,
endDate: null,
invoiceDate: null,
invoiceTitle: null,
invoiceCode: null,
invoiceAddress: null,
invoiceContact: null,
invoiceType: 10,
invoiceContent: null,
remark: null,
bankAccount: null,
registeredAddress: null,
bankName: null
}
},
emptyOrderForm() {
return {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
supplierId: null,
orderId: null,
startDate: null,
endDate: null,
invoiceStatus: 0
}
},
emptyInfoForm() {
return {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
mainId: null,
invoiceId: null,
invoiceCode: null,
invoiceDate: null,
invoiceType: null
}
},
indexMethod(index) {
return index + 1 + (this.pageForm.pageNumber - 1) * this.pageForm.pageSize
},
orderIndexMethod(index) {
return index + 1 + (this.orderForm.pageNumber - 1) * this.orderForm.pageSize
},
infoIndexMethod(index) {
return index + 1 + (this.infoForm.pageNumber - 1) * this.infoForm.pageSize
},
doSearch() {
this.pageForm.pageNumber = 1
this.pageData()
},
async pageData() {
this.tableLoading = true
const resp = await this.$axios.post(loc() + "/pageData", this.pageForm)
this.tableLoading = false
if (resp.code === 0) {
this.tableData = resp.data.list || []
this.pageForm.totalCount = resp.data.totalCount || 0
} else {
this.notifyWarning(resp.msg)
}
},
pageChange(val) {
this.pageForm.pageNumber = val
this.pageData()
},
sizeChange(val) {
this.pageForm.pageSize = val
this.pageForm.pageNumber = 1
this.pageData()
},
async loadSuppliers() {
const resp = await this.$axios.post(loc() + "/supplierList")
if (resp.code === 0) {
this.supplierList = resp.data || []
}
},
openApplyDialog() {
this.applyForm = this.emptyApplyForm()
this.applyDateRange = []
this.orderTotalPoints = 0
this.applyDialogVisible = true
},
syncApplyDateRange() {
this.applyForm.startDate = this.applyDateRange && this.applyDateRange.length ? this.applyDateRange[0] : null
this.applyForm.endDate = this.applyDateRange && this.applyDateRange.length ? this.applyDateRange[1] : null
},
async loadOrderTotalPoints() {
this.syncApplyDateRange()
if (!this.applyForm.supplierId || !this.applyForm.orderIds) {
this.notifyWarning("请先选择供应商并填写订单ID")
return
}
const resp = await this.$axios.post(loc() + "/orderTotalPoints", {
supplierId: this.applyForm.supplierId,
orderId: this.applyForm.orderIds,
startDate: this.applyForm.startDate,
endDate: this.applyForm.endDate,
invoiceStatus: 0
})
if (resp.code === 0) {
this.orderTotalPoints = resp.data || 0
} else {
this.notifyWarning(resp.msg)
}
},
async submitApply() {
this.syncApplyDateRange()
this.applyLoading = true
const resp = await this.$axios.post(loc() + "/applyInvoice", this.applyForm)
this.applyLoading = false
if (resp.code === 0) {
this.notifySuccess(resp.data || "开票申请成功")
this.applyDialogVisible = false
this.pageData()
} else {
this.notifyWarning(resp.msg)
}
},
openOrderDialog(orderIds) {
this.orderDialogVisible = true
this.orderForm = this.emptyOrderForm()
this.orderDateRange = []
if (typeof orderIds === "string" && orderIds) {
this.orderForm.orderId = orderIds
this.orderForm.invoiceStatus = null
}
this.orderPageData()
},
syncOrderDateRange() {
this.orderForm.startDate = this.orderDateRange && this.orderDateRange.length ? this.orderDateRange[0] : null
this.orderForm.endDate = this.orderDateRange && this.orderDateRange.length ? this.orderDateRange[1] : null
},
orderSearch() {
this.orderForm.pageNumber = 1
this.orderPageData()
},
orderReset() {
this.orderForm = this.emptyOrderForm()
this.orderDateRange = []
this.orderPageData()
},
async orderPageData() {
this.syncOrderDateRange()
this.orderLoading = true
const resp = await this.$axios.post(loc() + "/orderPage", this.orderForm)
this.orderLoading = false
if (resp.code === 0) {
this.orderList = resp.data.list || []
this.orderForm.totalCount = resp.data.totalCount || 0
} else {
this.notifyWarning(resp.msg)
}
},
orderPageChange(val) {
this.orderForm.pageNumber = val
this.orderPageData()
},
orderSizeChange(val) {
this.orderForm.pageSize = val
this.orderForm.pageNumber = 1
this.orderPageData()
},
orderSelectionChange(rows) {
this.orderSelections = rows || []
},
useOrdersForApply() {
if (!this.orderSelections.length) {
this.notifyWarning("请先选择订单")
return
}
const first = this.orderSelections[0]
this.applyForm = this.emptyApplyForm()
this.applyForm.supplierId = first.supplierId
this.applyForm.orderIds = this.orderSelections.map((item) => item.orderId).join(",")
this.applyForm.invoiceDate = new Date().toISOString().slice(0, 10)
this.applyDateRange = [this.orderForm.startDate, this.orderForm.endDate].filter(Boolean)
this.orderDialogVisible = false
this.applyDialogVisible = true
this.loadOrderTotalPoints()
},
openInfoDialog(row) {
this.infoForm = this.emptyInfoForm()
this.infoForm.mainId = row.id
this.infoDialogVisible = true
this.infoPageData()
},
infoSearch() {
this.infoForm.pageNumber = 1
this.infoPageData()
},
async infoPageData() {
this.infoLoading = true
const resp = await this.$axios.post(loc() + "/infoPage", this.infoForm)
this.infoLoading = false
if (resp.code === 0) {
this.infoList = resp.data.list || []
this.infoForm.totalCount = resp.data.totalCount || 0
} else {
this.notifyWarning(resp.msg)
}
},
infoPageChange(val) {
this.infoForm.pageNumber = val
this.infoPageData()
},
invoiceResultName(value) {
return Number(value) === 0 ? "成功" : "失败"
},
invoiceResultType(value) {
return Number(value) === 0 ? "success" : "danger"
},
invoiceStatusName(value) {
const map = { 0: "未开票", 1: "已开票", 2: "开票中" }
return map[value] || "-"
},
invoiceStatusType(value) {
const map = { 0: "info", 1: "success", 2: "warning" }
return map[value] || "info"
},
invoiceTypeName(value) {
const map = { 9: "全电专票", 10: "全电普票" }
return map[value] || "-"
}
},
created() {
this.loadSuppliers()
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,345 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="模板名称">
<el-input clearable placeholder="请输入模板名称" v-model="pageForm.name"></el-input>
</search-item>
<search-item label="模板类型">
<el-select clearable placeholder="请选择模板类型" v-model="pageForm.type">
<el-option :value="1" label="节日消息"></el-option>
<el-option :value="2" label="提醒消息(用户-商城)"></el-option>
<el-option :value="3" label="提醒消息(管理员-商城)"></el-option>
<el-option :value="4" label="生日消息"></el-option>
<el-option :value="5" label="业务消息"></el-option>
</el-select>
</search-item>
<search-item label="状态">
<el-select clearable placeholder="请选择状态" v-model="pageForm.status">
<el-option :value="1" label="启用"></el-option>
<el-option :value="0" label="禁用"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="消息推送">
<el-button @click="openAdd" icon="el-icon-plus" size="small" type="primary">新增</el-button>
<el-button @click="deleteSelected" icon="el-icon-delete" size="small" type="danger">删除</el-button>
</table-tool>
<el-table :data="tableData" @selection-change="handleSelectionChange" border size="small" style="width:100%" v-loading="tableLoading">
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column :index="indexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="模板名称" min-width="150" prop="name" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="模板类型" width="170">
<template slot-scope="{row}">
<el-tag size="small">{{ typeName(row.type) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="模板内容" min-width="250" prop="content" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="状态" width="90">
<template slot-scope="{row}">
<el-switch :active-value="1" :inactive-value="0" @change="changeStatus(row)" v-model="row.status"></el-switch>
</template>
</el-table-column>
<el-table-column align="center" label="创建时间" width="170">
<template slot-scope="{row}">{{ formatTime(row.createdAt) }}</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="190">
<template slot-scope="{row}">
<el-button @click="openEdit(row)" size="mini" type="text">编辑</el-button>
<el-button @click="openRecords(row)" size="mini" type="text">消息记录</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination :current-page="pageForm.pageNumber" :page-size="pageForm.pageSize" :page-sizes="[10,20,30,50]" :total="pageForm.totalCount" @current-change="pageChange" @size-change="sizeChange" layout="total, sizes, prev, pager, next" style="margin-top:16px;text-align:right"></el-pagination>
</el-card>
<el-dialog :close-on-click-modal="false" :title="formData.id ? '修改消息模板' : '新增消息模板'" :visible.sync="editDialogVisible" width="900px">
<el-form :model="formData" :rules="rules" label-width="120px" ref="form">
<el-form-item label="模板名称" prop="name">
<el-input clearable placeholder="请输入模板名称" v-model="formData.name"></el-input>
</el-form-item>
<el-form-item label="模板类型" prop="type">
<el-select placeholder="请选择模板类型" style="width:100%" v-model="formData.type">
<el-option :value="1" label="节日消息"></el-option>
<el-option :value="2" label="提醒消息(用户-商城)"></el-option>
<el-option :value="3" label="提醒消息(管理员-商城)"></el-option>
<el-option :value="4" label="生日消息"></el-option>
<el-option :value="5" label="业务消息"></el-option>
</el-select>
</el-form-item>
<el-form-item v-if="formData.type === 1" label="节日日期">
<el-date-picker placeholder="请选择节日日期" style="width:100%" type="date" value-format="yyyy-MM-dd" v-model="formData.festivalDate"></el-date-picker>
</el-form-item>
<el-form-item label="是否发放积分">
<el-radio-group v-model="formData.isSendPoints">
<el-radio :label="1">发放</el-radio>
<el-radio :label="0">不发放</el-radio>
</el-radio-group>
<el-input-number v-if="formData.isSendPoints === 1" :min="0" :precision="2" controls-position="right" style="margin-left:16px;width:180px" v-model="formData.sendPoints"></el-input-number>
</el-form-item>
<el-form-item label="目标机构列表">
<el-input clearable placeholder="多个机构ID用英文逗号分隔" v-model="formData.deptIdList"></el-input>
</el-form-item>
<el-form-item label="目标工会列表">
<el-input clearable placeholder="多个工会ID用英文逗号分隔" v-model="formData.unionIdList"></el-input>
</el-form-item>
<el-form-item label="内容链接">
<el-input :rows="3" placeholder="请输入内容链接JSON或文本" type="textarea" v-model="formData.contentLink"></el-input>
</el-form-item>
<el-form-item label="模板内容" prop="content">
<el-input :rows="6" placeholder="请输入模板内容" type="textarea" v-model="formData.content"></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="editDialogVisible=false">取消</el-button>
<el-button :loading="submitLoading" @click="submitForm" type="primary">保存</el-button>
</span>
</el-dialog>
<el-dialog :title="recordTitle" :visible.sync="recordDialogVisible" width="1050px">
<el-form inline @submit.native.prevent>
<el-form-item label="用户工号">
<el-input clearable placeholder="请输入用户工号" v-model="recordForm.touser"></el-input>
</el-form-item>
<el-form-item label="状态">
<el-select clearable multiple placeholder="请选择状态" style="width:180px" v-model="recordStatuses">
<el-option :value="0" label="未推送"></el-option>
<el-option :value="2" label="推送中"></el-option>
<el-option :value="1" label="已推送"></el-option>
<el-option :value="3" label="推送失败"></el-option>
<el-option :value="-1" label="不推送"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="recordSearch" type="primary">查询</el-button>
<el-button @click="recordReset">重置</el-button>
<el-button @click="resendSelected" type="primary">确认推送</el-button>
<el-button @click="resendAll" type="primary">一键全部推送</el-button>
</el-form-item>
</el-form>
<el-table :data="recordData" @selection-change="handleRecordSelectionChange" border size="small" v-loading="recordLoading">
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column :index="recordIndexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="用户工号" prop="touser" width="130"></el-table-column>
<el-table-column align="center" label="推送内容" min-width="260" prop="content" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="状态" width="110">
<template slot-scope="{row}">
<el-tag :type="messageStatusTag(row.status)" size="small">{{ messageStatusName(row.status) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="计划推送时间" prop="planPushTime" width="170"></el-table-column>
<el-table-column align="center" label="创建时间" width="170">
<template slot-scope="{row}">{{ formatTime(row.createdAt) }}</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="100">
<template slot-scope="{row}">
<el-button :disabled="row.status !== 0 && row.status !== 3" @click="resendRow(row)" size="mini" type="text">重新推送</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination :current-page="recordForm.pageNumber" :page-size="recordForm.pageSize" :total="recordForm.totalCount" @current-change="recordPageChange" layout="total, prev, pager, next" style="margin-top:16px;text-align:right"></el-pagination>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data() {
return {
tableData: [],
tableLoading: false,
submitLoading: false,
multipleSelection: [],
editDialogVisible: false,
pageForm: {pageNumber: 1, pageSize: 10, totalCount: 0, name: "", type: null, status: null},
formData: {},
rules: {
name: [{required: true, message: "请输入模板名称", trigger: "blur"}],
type: [{required: true, message: "请选择模板类型", trigger: "change"}],
content: [{required: true, message: "请输入模板内容", trigger: "blur"}]
},
recordDialogVisible: false,
recordTitle: "消息记录",
recordData: [],
recordLoading: false,
recordSelection: [],
recordStatuses: [],
recordForm: {pageNumber: 1, pageSize: 10, totalCount: 0, templateId: "", touser: "", statuses: ""}
}
},
methods: {
indexMethod(index) { return index + 1 + (this.pageForm.pageNumber - 1) * this.pageForm.pageSize },
recordIndexMethod(index) { return index + 1 + (this.recordForm.pageNumber - 1) * this.recordForm.pageSize },
formatTime(value) { return value ? new Date(value).toLocaleString() : "" },
typeName(type) {
return {1: "节日消息", 2: "提醒消息(用户-商城)", 3: "提醒消息(管理员-商城)", 4: "生日消息", 5: "业务消息"}[type] || "-"
},
messageStatusName(status) {
return {0: "未推送", 1: "已推送", 2: "推送中", 3: "推送失败", "-1": "不推送"}[status] || "-"
},
messageStatusTag(status) {
if (status === 1) return "success"
if (status === 2) return "warning"
if (status === 3 || status === -1) return "danger"
return "info"
},
doSearch() { this.pageForm.pageNumber = 1; this.pageData() },
async pageData() {
this.tableLoading = true
const res = await this.$axios.post("/platform/zhgh/points-mall/message/pageData", this.pageForm)
this.tableLoading = false
if (res.code === 0) {
this.tableData = res.data.list || []
this.pageForm.totalCount = res.data.totalCount || 0
} else {
this.$message.warning(res.msg)
}
},
pageChange(value) { this.pageForm.pageNumber = value; this.pageData() },
sizeChange(value) { this.pageForm.pageSize = value; this.pageForm.pageNumber = 1; this.pageData() },
handleSelectionChange(value) { this.multipleSelection = value },
openAdd() {
this.formData = {name: "", type: 1, content: "", contentLink: "", status: 1, extParam: "", isSendPoints: 0, sendPoints: 0, deptIdList: "", unionIdList: "", festivalDate: ""}
this.editDialogVisible = true
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
},
openEdit(row) {
this.formData = Object.assign({}, row)
this.editDialogVisible = true
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
},
async submitForm() {
this.$refs.form.validate(async valid => {
if (!valid) return
this.submitLoading = true
const res = await this.$axios.post("/platform/zhgh/points-mall/message/doSave", this.formData)
this.submitLoading = false
if (res.code === 0) {
this.$message.success(res.msg)
this.editDialogVisible = false
this.pageData()
} else {
this.$message.warning(res.msg)
}
})
},
async changeStatus(row) {
const res = await this.$axios.post("/platform/zhgh/points-mall/message/doSave", row)
if (res.code === 0) {
this.$message.success("修改成功")
} else {
row.status = row.status === 1 ? 0 : 1
this.$message.warning(res.msg)
}
},
deleteSelected() {
if (!this.multipleSelection.length) {
this.$message.warning("请至少选择一条数据")
return
}
if (this.multipleSelection.some(item => item.type === 5)) {
this.$message.warning("业务消息不允许删除")
return
}
this.$confirm("确定删除选中模板吗?", "提示", {
type: "warning",
callback: async action => {
if (action === "confirm") {
const res = await this.$axios.post("/platform/zhgh/points-mall/message/doDelete", {ids: this.multipleSelection.map(item => item.id).join(",")})
if (res.code === 0) {
this.$message.success("删除成功")
this.pageData()
} else {
this.$message.warning(res.msg)
}
}
}
})
},
openRecords(row) {
this.recordTitle = "消息记录 - " + row.name
this.recordStatuses = []
this.recordForm = {pageNumber: 1, pageSize: 10, totalCount: 0, templateId: row.id, touser: "", statuses: ""}
this.recordDialogVisible = true
this.recordPageData()
},
handleRecordSelectionChange(value) { this.recordSelection = value },
recordSearch() {
this.recordForm.pageNumber = 1
this.recordForm.statuses = this.recordStatuses.join(",")
this.recordPageData()
},
recordReset() {
this.recordStatuses = []
this.recordForm.touser = ""
this.recordSearch()
},
async recordPageData() {
this.recordLoading = true
const res = await this.$axios.post("/platform/zhgh/points-mall/message/recordPage", this.recordForm)
this.recordLoading = false
if (res.code === 0) {
this.recordData = res.data.list || []
this.recordForm.totalCount = res.data.totalCount || 0
} else {
this.$message.warning(res.msg)
}
},
recordPageChange(value) { this.recordForm.pageNumber = value; this.recordPageData() },
resendRow(row) { this.resend([row]) },
resendSelected() {
if (!this.recordSelection.length) {
this.$message.warning("请至少选择一条消息记录")
return
}
this.resend(this.recordSelection)
},
resend(rows) {
const allowed = rows.filter(row => row.status === 0 || row.status === 3)
if (!allowed.length) {
this.$message.warning("请选择未推送或推送失败的消息")
return
}
this.$confirm("确定重新推送选中消息吗?", "提示", {
type: "warning",
callback: async action => {
if (action === "confirm") {
const res = await this.$axios.post("/platform/zhgh/points-mall/message/resend", {ids: allowed.map(item => item.id).join(",")})
if (res.code === 0) {
this.$message.success("推送已触发")
this.recordPageData()
} else {
this.$message.warning(res.msg)
}
}
}
})
},
resendAll() {
this.$confirm("确定按当前条件推送全部未推送/失败消息吗?", "提示", {
type: "warning",
callback: async action => {
if (action === "confirm") {
const res = await this.$axios.post("/platform/zhgh/points-mall/message/resendAll", this.recordForm)
if (res.code === 0) {
this.$message.success("推送已触发")
this.recordPageData()
} else {
this.$message.warning(res.msg)
}
}
}
})
}
},
created() { this.pageData() }
})
</script>
<!--#
}
#-->
@@ -0,0 +1,395 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="供应商">
<el-select clearable filterable placeholder="请选择供应商" style="width: 100%" v-model="pageForm.supplierId">
<el-option :key="item.supplierId" :label="item.supplierName" :value="item.supplierId" v-for="item in supplierList"></el-option>
</el-select>
</search-item>
<search-item label="订单号">
<el-input clearable placeholder="请输入订单号" v-model="pageForm.orderId"></el-input>
</search-item>
<search-item label="工号">
<el-input clearable placeholder="请输入工号" v-model="pageForm.userId"></el-input>
</search-item>
<search-item label="订单状态">
<el-select clearable placeholder="请选择订单状态" style="width: 100%" v-model="pageForm.orderState">
<el-option :key="item.value" :label="item.label" :value="item.value" v-for="item in orderStateOptions"></el-option>
</el-select>
</search-item>
<search-item label="物流单号">
<el-input clearable placeholder="请输入物流单号" v-model="pageForm.logisticsOrderId"></el-input>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="积分商城订单列表"></table-tool>
<el-table :data="tableData" border class="vi-table" size="small" style="width: 100%" v-loading="tableLoading">
<el-table-column :index="indexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="订单号" min-width="160" prop="orderId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="订单编码" min-width="150" prop="orderNumberId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="工号" min-width="120" prop="userAccount" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商" min-width="140" prop="supplierName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="应用名称" min-width="120" prop="aplName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="创建时间" min-width="160" prop="orderCreateTime" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="订单状态" min-width="110">
<template slot-scope="{row}">
<el-tag :type="orderStatusType(row.orderState)" size="mini">{{ orderStateName(row.orderState) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="订单总金额" min-width="110" prop="totalPrice"></el-table-column>
<el-table-column align="center" label="现金金额" min-width="110">
<template slot-scope="{row}">{{ row.wPayOrAPay || "-" }}</template>
</el-table-column>
<el-table-column align="center" label="消费积分" min-width="110" prop="pointsPrice"></el-table-column>
<el-table-column align="center" label="现金支付" min-width="120">
<template slot-scope="{row}">{{ cashPaidName(row.cashPaid) }}</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="100">
<template slot-scope="{row}">
<el-button @click="openSubOrders(row)" size="mini" type="primary">查看</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="pageForm.pageNumber"
:page-size="pageForm.pageSize"
:page-sizes="[10, 20, 30, 50]"
:total="pageForm.totalCount"
@current-change="pageChange"
@size-change="sizeChange"
layout="total, sizes, prev, pager, next"
style="margin-top: 16px; text-align: right"
></el-pagination>
</el-card>
<order-sub-list-dialog
:order-status-type="orderStatusType"
:order-state-name="orderStateName"
:visible.sync="subDialogVisible"
:main-order-id="currentMainOrderId"
></order-sub-list-dialog>
</div>
<script nonce="${cspNonce!}">
Vue.component("order-detail-dialog", {
props: {
visible: Boolean,
orderId: String
},
data() {
return {
loading: false,
productList: []
}
},
watch: {
visible(val) {
if (val) {
this.loadProducts()
}
},
orderId() {
if (this.visible) {
this.loadProducts()
}
}
},
methods: {
close() {
this.$emit("update:visible", false)
},
async loadProducts() {
if (!this.orderId) {
this.productList = []
return
}
this.loading = true
const resp = await this.$axios.post(loc() + "/productList", { orderId: this.orderId })
this.loading = false
if (resp.code === 0) {
this.productList = (resp.data || []).map((item) => Object.assign({}, item, {
orderId: this.orderId
}))
} else if (this.$message) {
this.$message.warning(resp.msg)
}
}
},
template: `
<el-dialog :close-on-click-modal="false" title="子订单商品明细" :visible="visible" @update:visible="$emit('update:visible', $event)" width="72%">
<el-table :data="productList" border size="small" v-loading="loading">
<el-table-column align="center" label="订单号" min-width="180" prop="orderId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="SKU" min-width="140" prop="sku" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="商品名称" min-width="220" prop="name" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="商品单价" width="110" prop="price"></el-table-column>
<el-table-column align="center" label="商品图片" width="120">
<template slot-scope="{row}">
<el-image
v-if="row.fileId"
:src="row.fileId"
style="width: 60px; height: 60px"
fit="contain"
preview-teleported
></el-image>
</template>
</el-table-column>
<el-table-column align="center" label="商品数量" width="110" prop="number"></el-table-column>
<el-table-column align="center" label="税率" width="110" prop="taxRate"></el-table-column>
</el-table>
<span slot="footer">
<el-button @click="close">关闭</el-button>
</span>
</el-dialog>
`
})
Vue.component("order-sub-list-dialog", {
props: {
visible: Boolean,
mainOrderId: String,
orderStateName: Function,
orderStatusType: Function
},
data() {
return {
loading: false,
subOrderList: [],
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0
},
detailVisible: false,
detailOrderId: null
}
},
watch: {
visible(val) {
if (val) {
this.pageForm.pageNumber = 1
this.pageData()
}
},
mainOrderId() {
if (this.visible) {
this.pageForm.pageNumber = 1
this.pageData()
}
}
},
methods: {
close() {
this.$emit("update:visible", false)
},
async pageData() {
if (!this.mainOrderId) {
this.subOrderList = []
return
}
this.loading = true
const resp = await this.$axios.post(loc() + "/subPageData", {
pageNumber: this.pageForm.pageNumber,
pageSize: this.pageForm.pageSize,
mainOrderId: this.mainOrderId
})
this.loading = false
if (resp.code === 0) {
this.subOrderList = resp.data.list || []
this.pageForm.totalCount = resp.data.totalCount || 0
} else if (this.$message) {
this.$message.warning(resp.msg)
}
},
pageChange(val) {
this.pageForm.pageNumber = val
this.pageData()
},
deliveryStatusName(value) {
const map = {
0: "未发货",
1: "已发货"
}
return map[value] || "-"
},
deliveryStatusType(value) {
const map = {
0: "warning",
1: "success"
}
return map[value] || "info"
},
openDetails(row) {
this.detailOrderId = row.orderId
this.detailVisible = true
}
},
template: `
<div>
<el-dialog :close-on-click-modal="false" :title="'子订单详情 - ' + (mainOrderId || '')" :visible="visible" @update:visible="$emit('update:visible', $event)" top="5vh" width="88%">
<el-table :data="subOrderList" border size="small" style="width: 100%" v-loading="loading">
<el-table-column align="center" label="子订单号" min-width="170" prop="orderId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="订单编码" min-width="150" prop="orderNumberId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商名称" min-width="150" prop="supplierName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="应用名称" min-width="120" prop="aplName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="创建时间" min-width="160" prop="orderCreateTime"></el-table-column>
<el-table-column align="center" label="订单状态" min-width="110">
<template slot-scope="{row}">
<el-tag :type="orderStatusType(row.orderState)" size="mini">{{ orderStateName(row.orderState) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="订单总金额" min-width="120" prop="totalPrice"></el-table-column>
<el-table-column align="center" label="消费积分" min-width="110" prop="pointsPrice"></el-table-column>
<el-table-column align="center" label="供应商ID" min-width="120" prop="supplierId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="工号" min-width="120" prop="userAccount" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="更新时间" min-width="160" prop="orderUpdateTime"></el-table-column>
<el-table-column align="center" label="发货时间" min-width="160" prop="deliveryTime"></el-table-column>
<el-table-column align="center" label="确认收货时间" min-width="160" prop="orderFinishTime"></el-table-column>
<el-table-column align="center" label="完成时间" min-width="160" prop="orderCompleteTime"></el-table-column>
<el-table-column align="center" label="发货状态" min-width="100">
<template slot-scope="{row}">
<el-tag :type="deliveryStatusType(row.deliveryStatus)" size="mini">{{ deliveryStatusName(row.deliveryStatus) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="运费金额" min-width="110" prop="freightCost"></el-table-column>
<el-table-column align="center" label="积分退回" min-width="110" prop="refund"></el-table-column>
<el-table-column align="center" label="物流单号" min-width="140" prop="logisticsOrderId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="物流承运商" min-width="140" prop="crrgBsnNm" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="发票代码" min-width="120" prop="invoiceCode" show-overflow-tooltip></el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="90">
<template slot-scope="{row}">
<el-button @click="openDetails(row)" size="mini">详情</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="pageForm.pageNumber"
:page-size="pageForm.pageSize"
:total="pageForm.totalCount"
@current-change="pageChange"
layout="total, prev, pager, next"
style="margin-top: 16px; text-align: right"
></el-pagination>
<span slot="footer">
<el-button @click="close">关闭</el-button>
</span>
</el-dialog>
<order-detail-dialog :visible.sync="detailVisible" :order-id="detailOrderId"></order-detail-dialog>
</div>
`
})
new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
supplierList: [],
tableData: [],
tableLoading: false,
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
supplierId: null,
orderId: null,
userId: null,
orderState: null,
logisticsOrderId: null
},
subDialogVisible: false,
currentMainOrderId: null,
orderStateOptions: [
{ value: "0", label: "待付款" },
{ value: "1", label: "已支付" },
{ value: "2", label: "进行中" },
{ value: "3", label: "已取消" },
{ value: "4", label: "已妥投" },
{ value: "5", label: "已完成" },
{ value: "6", label: "换货" },
{ value: "7", label: "退货" },
{ value: "8", label: "已废除" }
]
}
},
methods: {
indexMethod(index) {
return index + 1 + (this.pageForm.pageNumber - 1) * this.pageForm.pageSize
},
doSearch() {
this.pageForm.pageNumber = 1
this.pageData()
},
async pageData() {
this.tableLoading = true
const resp = await this.$axios.post(loc() + "/pageData", this.pageForm)
this.tableLoading = false
if (resp.code === 0) {
this.tableData = resp.data.list || []
this.pageForm.totalCount = resp.data.totalCount || 0
} else {
this.notifyWarning(resp.msg)
}
},
pageChange(val) {
this.pageForm.pageNumber = val
this.pageData()
},
sizeChange(val) {
this.pageForm.pageSize = val
this.pageForm.pageNumber = 1
this.pageData()
},
async loadSuppliers() {
const resp = await this.$axios.post(loc() + "/supplierList")
if (resp.code === 0) {
this.supplierList = resp.data || []
}
},
openSubOrders(row) {
this.currentMainOrderId = row.orderId
this.subDialogVisible = true
},
orderStateName(value) {
const item = this.orderStateOptions.find((v) => String(v.value) === String(value))
return item ? item.label : "-"
},
orderStatusType(value) {
const map = {
0: "info",
1: "primary",
2: "warning",
3: "danger",
4: "primary",
5: "success",
6: "warning",
7: "danger",
8: "info"
}
return map[value] || "info"
},
cashPaidName(value) {
const map = {
0: "不涉及",
1: "待现金支付",
2: "已现金支付",
3: "取消现金支付"
}
return map[value] || "-"
}
},
created() {
this.loadSuppliers()
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,279 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="工号">
<el-input clearable placeholder="请输入工号" v-model="pageForm.account"></el-input>
</search-item>
<search-item label="姓名">
<el-input clearable placeholder="请输入姓名" v-model="pageForm.realName"></el-input>
</search-item>
<search-item label="手机号">
<el-input clearable placeholder="请输入手机号" v-model="pageForm.mobile"></el-input>
</search-item>
<search-item label="会员">
<el-select clearable placeholder="请选择" v-model="pageForm.member">
<el-option :value="true" label="是"></el-option>
<el-option :value="false" label="否"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="用户积分管理">
<el-button @click="openBatch('grant')" icon="el-icon-plus" size="small" type="primary">批量增加积分</el-button>
<el-button @click="openBatch('deduction')" icon="el-icon-minus" size="small" type="danger">批量扣减积分</el-button>
<el-button @click="deleteSelected" icon="el-icon-delete" size="small" type="danger">删除积分记录</el-button>
</table-tool>
<el-table :data="tableData" @selection-change="handleSelectionChange" border size="small" style="width:100%" v-loading="tableLoading">
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column :index="indexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="工号" prop="account" width="120"></el-table-column>
<el-table-column align="center" label="姓名" prop="realName" width="120"></el-table-column>
<el-table-column align="center" label="性别" prop="gender" width="70"></el-table-column>
<el-table-column align="center" label="联系方式" prop="mobile" width="130"></el-table-column>
<el-table-column align="center" label="组织名称" min-width="180" prop="unitName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="总分配积分" prop="totalPoints" width="120"></el-table-column>
<el-table-column align="center" label="当前可用积分" prop="availablePoints" width="130"></el-table-column>
<el-table-column align="center" label="在途锁定积分" prop="lockPoints" width="130"></el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="190">
<template slot-scope="{row}">
<el-button @click="openLog(row)" size="mini" type="text">查看</el-button>
<el-button @click="openChange(row, 'grant')" size="mini" type="text">增加</el-button>
<el-button @click="openChange(row, 'deduction')" size="mini" type="text">扣减</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination :current-page="pageForm.pageNumber" :page-size="pageForm.pageSize" :page-sizes="[10,20,30,50]" :total="pageForm.totalCount" @current-change="pageChange" @size-change="sizeChange" layout="total, sizes, prev, pager, next" style="margin-top:16px;text-align:right"></el-pagination>
</el-card>
<el-dialog :close-on-click-modal="false" :title="changeType === 'grant' ? '增加积分' : '扣减积分'" :visible.sync="changeDialogVisible" width="520px">
<el-form :model="changeForm" label-width="110px" ref="changeForm">
<el-form-item label="用户">
<el-input disabled :value="currentRow ? currentRow.realName + '(' + currentRow.account + ')' : ''"></el-input>
</el-form-item>
<el-form-item label="积分">
<el-input-number :min="0.01" :precision="2" :step="1" controls-position="right" style="width:100%" v-model="changeForm.points"></el-input-number>
</el-form-item>
<el-form-item label="备注">
<el-input clearable placeholder="请输入备注" v-model="changeForm.remark"></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="changeDialogVisible=false">取消</el-button>
<el-button :loading="submitLoading" @click="submitChange" type="primary">保存</el-button>
</span>
</el-dialog>
<el-dialog :close-on-click-modal="false" :title="batchType === 'grant' ? '批量增加积分' : '批量扣减积分'" :visible.sync="batchDialogVisible" width="560px">
<el-alert :closable="false" :title="'将对已选择的 ' + multipleSelection.length + ' 个用户执行操作'" show-icon type="warning"></el-alert>
<el-form :model="batchForm" class="mt10" label-width="110px">
<el-form-item label="积分">
<el-input-number :min="0.01" :precision="2" :step="1" controls-position="right" style="width:100%" v-model="batchForm.points"></el-input-number>
</el-form-item>
<el-form-item label="备注">
<el-input clearable placeholder="请输入备注" v-model="batchForm.remark"></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="batchDialogVisible=false">取消</el-button>
<el-button :loading="submitLoading" @click="submitBatch" type="primary">确认</el-button>
</span>
</el-dialog>
<el-dialog :title="logTitle" :visible.sync="logDialogVisible" width="820px">
<el-table :data="logData" border size="small" v-loading="logLoading">
<el-table-column :index="logIndexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="变更积分" prop="changeAmount" width="120"></el-table-column>
<el-table-column align="center" label="变更类型" prop="changeType" width="120">
<template slot-scope="{row}">
<el-tag :type="row.changeType === 'GRANT' ? 'success' : 'danger'" size="small">{{ row.changeType === 'GRANT' ? '增加' : '扣减' }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="备注" min-width="180" prop="remark" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="变更时间" prop="createdAt" width="170">
<template slot-scope="{row}">{{ formatTime(row.createdAt) }}</template>
</el-table-column>
</el-table>
<el-pagination :current-page="logPage.pageNumber" :page-size="logPage.pageSize" :total="logPage.totalCount" @current-change="logPageChange" layout="total, prev, pager, next" style="margin-top:16px;text-align:right"></el-pagination>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data() {
return {
tableData: [],
tableLoading: false,
submitLoading: false,
multipleSelection: [],
currentRow: null,
pageForm: {pageNumber: 1, pageSize: 10, totalCount: 0, account: "", realName: "", mobile: "", member: null},
changeDialogVisible: false,
changeType: "grant",
changeForm: {points: 1, remark: ""},
batchDialogVisible: false,
batchType: "grant",
batchForm: {points: 1, remark: ""},
logDialogVisible: false,
logLoading: false,
logTitle: "积分详情",
logData: [],
logPage: {pageNumber: 1, pageSize: 10, totalCount: 0, userId: ""}
}
},
methods: {
indexMethod(index) {
return index + 1 + (this.pageForm.pageNumber - 1) * this.pageForm.pageSize
},
logIndexMethod(index) {
return index + 1 + (this.logPage.pageNumber - 1) * this.logPage.pageSize
},
formatTime(value) {
if (!value) return ""
return new Date(value).toLocaleString()
},
doSearch() {
this.pageForm.pageNumber = 1
this.pageData()
},
async pageData() {
this.tableLoading = true
const res = await this.$axios.post("/platform/zhgh/points-mall/points/pageData", this.pageForm)
this.tableLoading = false
if (res.code === 0) {
this.tableData = res.data.list || []
this.pageForm.totalCount = res.data.totalCount || 0
} else {
this.$message.warning(res.msg)
}
},
pageChange(value) {
this.pageForm.pageNumber = value
this.pageData()
},
sizeChange(value) {
this.pageForm.pageSize = value
this.pageForm.pageNumber = 1
this.pageData()
},
handleSelectionChange(value) {
this.multipleSelection = value
},
openChange(row, type) {
if (type === "deduction" && Number(row.availablePoints || 0) <= 0) {
this.$message.warning("可用积分为0,请勿继续扣减")
return
}
this.currentRow = row
this.changeType = type
this.changeForm = {points: 1, remark: ""}
this.changeDialogVisible = true
},
async submitChange() {
if (!this.changeForm.points || this.changeForm.points <= 0) {
this.$message.warning("请输入有效的积分数量")
return
}
this.submitLoading = true
const url = this.changeType === "grant" ? "/doGrant" : "/doDeduction"
const res = await this.$axios.post("/platform/zhgh/points-mall/points" + url, {
userId: this.currentRow.userId,
points: this.changeForm.points,
remark: this.changeForm.remark
})
this.submitLoading = false
if (res.code === 0) {
this.$message.success(res.msg)
this.changeDialogVisible = false
this.pageData()
} else {
this.$message.warning(res.msg)
}
},
openBatch(type) {
if (!this.multipleSelection.length) {
this.$message.warning("请至少选择一个用户")
return
}
this.batchType = type
this.batchForm = {points: 1, remark: ""}
this.batchDialogVisible = true
},
async submitBatch() {
if (!this.batchForm.points || this.batchForm.points <= 0) {
this.$message.warning("请输入有效的积分数量")
return
}
this.submitLoading = true
const url = this.batchType === "grant" ? "/batchGrant" : "/batchDeduction"
const res = await this.$axios.post("/platform/zhgh/points-mall/points" + url, {
userIds: this.multipleSelection.map(item => item.userId).join(","),
points: this.batchForm.points,
remark: this.batchForm.remark
})
this.submitLoading = false
if (res.code === 0) {
this.$message.success(res.msg)
this.batchDialogVisible = false
this.pageData()
} else {
this.$message.warning(res.msg)
}
},
deleteSelected() {
const rows = this.multipleSelection.filter(item => item.id)
if (!rows.length) {
this.$message.warning("请选择已有积分记录的数据")
return
}
this.$confirm("确定删除选中的积分记录吗?", "提示", {
type: "warning",
callback: async action => {
if (action === "confirm") {
const res = await this.$axios.post("/platform/zhgh/points-mall/points/doDelete", {ids: rows.map(item => item.id).join(",")})
if (res.code === 0) {
this.$message.success(res.msg)
this.pageData()
} else {
this.$message.warning(res.msg)
}
}
}
})
},
openLog(row) {
this.currentRow = row
this.logTitle = "积分详情 - " + row.realName + "(" + row.account + ")"
this.logPage = {pageNumber: 1, pageSize: 10, totalCount: 0, userId: row.userId}
this.logDialogVisible = true
this.logPageData()
},
async logPageData() {
this.logLoading = true
const res = await this.$axios.post("/platform/zhgh/points-mall/points/logPage", this.logPage)
this.logLoading = false
if (res.code === 0) {
this.logData = res.data.list || []
this.logPage.totalCount = res.data.totalCount || 0
} else {
this.$message.warning(res.msg)
}
},
logPageChange(value) {
this.logPage.pageNumber = value
this.logPageData()
}
},
created() {
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,199 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="计划名称">
<el-input clearable placeholder="请输入计划名称" v-model="pageForm.planName"></el-input>
</search-item>
<search-item label="状态">
<el-select clearable placeholder="请选择状态" v-model="pageForm.status">
<el-option label="待执行" value="待执行"></el-option>
<el-option label="执行中" value="执行中"></el-option>
<el-option label="已完成" value="已完成"></el-option>
<el-option label="已取消" value="已取消"></el-option>
<el-option label="已失败" value="已失败"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="积分发放">
<el-button @click="openAdd" icon="el-icon-plus" size="small" type="primary">新建</el-button>
</table-tool>
<el-table :data="tableData" border size="small" style="width:100%" v-loading="tableLoading">
<el-table-column :index="indexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="计划名称" min-width="180" prop="planName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="计划描述" min-width="180" prop="planDescription" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="发放模式" prop="grantMode" width="110">
<template slot-scope="{row}">{{ row.grantMode === '2' ? '定时发放' : '手动发放' }}</template>
</el-table-column>
<el-table-column align="center" label="发放时间" prop="grantTime" width="170"></el-table-column>
<el-table-column align="center" label="发放积分" prop="grantPoints" width="110"></el-table-column>
<el-table-column align="center" label="当前状态" prop="status" width="110">
<template slot-scope="{row}">
<el-tag :type="statusTag(row.status)" size="small">{{ row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="170">
<template slot-scope="{row}">
<template v-if="row.status === '待执行'">
<el-button @click="startPlan(row)" size="mini" type="text">立即执行</el-button>
<el-button @click="cancelPlan(row)" size="mini" type="text">取消执行</el-button>
</template>
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
<el-pagination :current-page="pageForm.pageNumber" :page-size="pageForm.pageSize" :page-sizes="[10,20,30,50]" :total="pageForm.totalCount" @current-change="pageChange" @size-change="sizeChange" layout="total, sizes, prev, pager, next" style="margin-top:16px;text-align:right"></el-pagination>
</el-card>
<el-dialog :close-on-click-modal="false" title="新建发放计划" :visible.sync="editDialogVisible" width="720px">
<el-form :model="formData" :rules="rules" label-width="120px" ref="form">
<el-form-item label="计划名称" prop="planName">
<el-input clearable placeholder="请输入计划名称" v-model="formData.planName"></el-input>
</el-form-item>
<el-form-item label="发放积分" prop="grantPoints">
<el-input-number :min="0.01" :precision="2" :step="1" controls-position="right" style="width:100%" v-model="formData.grantPoints"></el-input-number>
</el-form-item>
<el-form-item label="发放模式" prop="grantMode">
<el-radio-group v-model="formData.grantMode">
<el-radio border label="1">手动发放</el-radio>
<el-radio border label="2">定时发放</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="formData.grantMode === '2'" label="发放时间" prop="grantTime">
<el-date-picker placeholder="请选择发放时间" style="width:100%" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" v-model="formData.grantTime"></el-date-picker>
</el-form-item>
<el-form-item label="用户ID列表" prop="userIdList">
<el-input :rows="4" placeholder="请输入用户ID,多个用户用英文逗号分隔" type="textarea" v-model="formData.userIdList"></el-input>
</el-form-item>
<el-form-item label="计划描述">
<el-input :rows="3" maxlength="500" placeholder="请输入计划描述" show-word-limit type="textarea" v-model="formData.planDescription"></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="editDialogVisible=false">取消</el-button>
<el-button :loading="submitLoading" @click="submitForm" type="primary">保存</el-button>
</span>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data() {
return {
tableData: [],
tableLoading: false,
submitLoading: false,
editDialogVisible: false,
pageForm: {pageNumber: 1, pageSize: 10, totalCount: 0, planName: "", status: ""},
formData: {},
rules: {
planName: [{required: true, message: "请输入计划名称", trigger: "blur"}],
grantPoints: [{required: true, message: "请输入发放积分", trigger: "blur"}],
grantMode: [{required: true, message: "请选择发放模式", trigger: "change"}],
grantTime: [{required: true, message: "请选择发放时间", trigger: "change"}],
userIdList: [{required: true, message: "请输入用户ID列表", trigger: "blur"}]
}
}
},
methods: {
indexMethod(index) {
return index + 1 + (this.pageForm.pageNumber - 1) * this.pageForm.pageSize
},
statusTag(status) {
if (status === "已完成") return "success"
if (status === "已取消") return "info"
if (status === "已失败") return "danger"
if (status === "执行中") return "warning"
return "primary"
},
doSearch() {
this.pageForm.pageNumber = 1
this.pageData()
},
async pageData() {
this.tableLoading = true
const res = await this.$axios.post("/platform/zhgh/points-mall/pointsgrant/pageData", this.pageForm)
this.tableLoading = false
if (res.code === 0) {
this.tableData = res.data.list || []
this.pageForm.totalCount = res.data.totalCount || 0
} else {
this.$message.warning(res.msg)
}
},
pageChange(value) {
this.pageForm.pageNumber = value
this.pageData()
},
sizeChange(value) {
this.pageForm.pageSize = value
this.pageForm.pageNumber = 1
this.pageData()
},
openAdd() {
this.formData = {planName: "", grantMode: "1", grantTime: "", cronExp: "", userIdList: "", grantPoints: 1, planDescription: ""}
this.editDialogVisible = true
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
},
submitForm() {
this.$refs.form.validate(async valid => {
if (!valid) return
this.submitLoading = true
const res = await this.$axios.post("/platform/zhgh/points-mall/pointsgrant/doAdd", this.formData)
this.submitLoading = false
if (res.code === 0) {
this.$message.success(res.msg)
this.editDialogVisible = false
this.pageData()
} else {
this.$message.warning(res.msg)
}
})
},
startPlan(row) {
this.$confirm("确定立即执行该积分发放计划吗?", "提示", {
type: "warning",
callback: async action => {
if (action === "confirm") {
const res = await this.$axios.post("/platform/zhgh/points-mall/pointsgrant/startPlan", {id: row.id})
if (res.code === 0) {
this.$message.success("执行成功")
this.pageData()
} else {
this.$message.warning(res.msg)
}
}
}
})
},
cancelPlan(row) {
this.$confirm("确定取消该积分发放计划吗?", "提示", {
type: "warning",
callback: async action => {
if (action === "confirm") {
const res = await this.$axios.post("/platform/zhgh/points-mall/pointsgrant/cancelPlan", {id: row.id})
if (res.code === 0) {
this.$message.success("取消成功")
this.pageData()
} else {
this.$message.warning(res.msg)
}
}
}
})
}
},
created() {
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,448 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="供应商">
<el-select clearable filterable placeholder="请选择供应商" style="width: 100%" v-model="pageForm.supplierId">
<el-option :key="item.supplierId" :label="item.supplierName" :value="item.supplierId" v-for="item in supplierList"></el-option>
</el-select>
</search-item>
<search-item label="订单ID">
<el-input clearable placeholder="多个订单ID用英文逗号分隔" v-model="pageForm.orderId"></el-input>
</search-item>
<search-item label="用户ID">
<el-input clearable placeholder="请输入用户ID" v-model="pageForm.userId"></el-input>
</search-item>
<search-item label="起始日期">
<el-date-picker clearable placeholder="开始日期" style="width: 100%" type="date" value-format="yyyy-MM-dd" v-model="pageForm.startDate"></el-date-picker>
</search-item>
<search-item label="结束日期">
<el-date-picker clearable placeholder="结束日期" style="width: 100%" type="date" value-format="yyyy-MM-dd" v-model="pageForm.endDate"></el-date-picker>
</search-item>
<search-item label="对账状态">
<el-select clearable placeholder="请选择对账状态" style="width: 100%" v-model="pageForm.reconciliationStatus">
<el-option :key="item.value" :label="item.label" :value="item.value" v-for="item in reconciliationStatusOptions"></el-option>
</el-select>
</search-item>
<search-item label="发票状态">
<el-select clearable placeholder="请选择发票状态" style="width: 100%" v-model="pageForm.invoiceStatus">
<el-option :key="item.value" :label="item.label" :value="item.value" v-for="item in invoiceStatusOptions"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="对账订单列表">
<el-button @click="openReconcileDialog" size="mini" type="success">对账</el-button>
<el-button @click="openRecordDialog" size="mini" type="primary">对账记录</el-button>
</table-tool>
<el-table :data="tableData" border class="vi-table" size="small" style="width: 100%" v-loading="tableLoading">
<el-table-column :index="indexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="订单ID" min-width="170" prop="orderId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商" min-width="150" prop="supplierName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商编码" min-width="120" prop="supplierId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="用户ID" min-width="120" prop="userId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="下单时间" min-width="160" prop="orderCreateTime" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="完成时间" min-width="160" prop="orderCompleteTime" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="订单状态" min-width="110">
<template slot-scope="{row}">
<el-tag :type="orderStatusType(row.orderState)" size="mini">{{ orderStateName(row.orderState) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="对账状态" min-width="110">
<template slot-scope="{row}">
<el-tag :type="reconciliationStatusType(row.reconciliationStatus)" size="mini">{{ reconciliationStatusName(row.reconciliationStatus) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="发票状态" min-width="110">
<template slot-scope="{row}">
<el-tag :type="invoiceStatusType(row.invoiceStatus)" size="mini">{{ invoiceStatusName(row.invoiceStatus) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="订单总金额" min-width="110" prop="totalPrice"></el-table-column>
<el-table-column align="center" label="现金金额" min-width="110" prop="wPayOrAPay"></el-table-column>
<el-table-column align="center" label="积分金额" min-width="110" prop="pointsPrice"></el-table-column>
<el-table-column align="center" label="退款积分" min-width="110" prop="refund"></el-table-column>
<el-table-column align="center" label="发票代码" min-width="130" prop="invoiceCode" show-overflow-tooltip></el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="90">
<template slot-scope="{row}">
<el-button @click="showOrderDetail(row)" size="mini">详情</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="pageForm.pageNumber"
:page-size="pageForm.pageSize"
:page-sizes="[10, 20, 30, 50]"
:total="pageForm.totalCount"
@current-change="pageChange"
@size-change="sizeChange"
layout="total, sizes, prev, pager, next"
style="margin-top: 16px; text-align: right"
></el-pagination>
</el-card>
<el-dialog :close-on-click-modal="false" title="确认对账" :visible.sync="reconcileDialogVisible" width="480px">
<el-form label-width="100px" size="small">
<el-form-item label="供应商">
<el-select clearable filterable placeholder="请选择供应商" style="width: 100%" v-model="reconcileForm.supplierId">
<el-option :key="item.supplierId" :label="item.supplierName" :value="item.supplierId" v-for="item in supplierList"></el-option>
</el-select>
</el-form-item>
<el-form-item label="对账类型">
<el-select placeholder="请选择对账类型" style="width: 100%" v-model="reconcileForm.reconciliationType">
<el-option label="正常订单对账" :value="1"></el-option>
<el-option label="历史异常订单对账" :value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="起始日期">
<el-date-picker placeholder="开始日期" style="width: 100%" type="date" value-format="yyyy-MM-dd" v-model="reconcileForm.startDate"></el-date-picker>
</el-form-item>
<el-form-item label="结束日期">
<el-date-picker placeholder="结束日期" style="width: 100%" type="date" value-format="yyyy-MM-dd" v-model="reconcileForm.endDate"></el-date-picker>
</el-form-item>
<el-form-item label="订单ID">
<el-input clearable placeholder="多个订单ID用英文逗号分隔" v-model="reconcileForm.orderId"></el-input>
</el-form-item>
<el-form-item label="积分合计">
<span>{{ reconcilePointsTotal }}</span>
</el-form-item>
</el-form>
<el-alert :closable="false" show-icon title="确认后订单将更新为已对账或对账异常,并生成对账记录。" type="warning"></el-alert>
<span slot="footer">
<el-button @click="reconcileDialogVisible = false">取消</el-button>
<el-button @click="loadPointsTotal" :loading="pointsTotalLoading">计算金额</el-button>
<el-button @click="executeReconciliation" :loading="executeLoading" type="primary">确认对账</el-button>
</span>
</el-dialog>
<el-dialog :close-on-click-modal="false" title="对账记录" :visible.sync="recordDialogVisible" top="5vh" width="90%">
<el-form :inline="true" size="small" @submit.native.prevent>
<el-form-item label="供应商">
<el-select clearable filterable placeholder="供应商" style="width: 180px" v-model="recordForm.supplierId">
<el-option :key="item.supplierId" :label="item.supplierName" :value="item.supplierId" v-for="item in supplierList"></el-option>
</el-select>
</el-form-item>
<el-form-item label="订单ID">
<el-input clearable placeholder="订单ID" v-model="recordForm.orderId"></el-input>
</el-form-item>
<el-form-item label="用户ID">
<el-input clearable placeholder="用户ID" v-model="recordForm.userId"></el-input>
</el-form-item>
<el-form-item label="批次号">
<el-input clearable placeholder="批次号" v-model="recordForm.batchNo"></el-input>
</el-form-item>
<el-form-item label="差异类型">
<el-select clearable placeholder="差异类型" style="width: 130px" v-model="recordForm.diffType">
<el-option label="一致" :value="0"></el-option>
<el-option label="金额异常" :value="1"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="recordSearch" type="primary">查询</el-button>
<el-button @click="recordReset">重置</el-button>
</el-form-item>
</el-form>
<el-table :data="recordList" border size="small" style="width: 100%" v-loading="recordLoading">
<el-table-column :index="recordIndexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="批次号" min-width="160" prop="batchNo" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="订单ID" min-width="160" prop="orderId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商" min-width="140" prop="supplierName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="用户ID" min-width="110" prop="userId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="平台总金额" min-width="110" prop="pTotalPrice"></el-table-column>
<el-table-column align="center" label="供应商总金额" min-width="120" prop="sTotalPrice"></el-table-column>
<el-table-column align="center" label="平台积分" min-width="100" prop="pPointsPrice"></el-table-column>
<el-table-column align="center" label="供应商积分" min-width="110" prop="sPointsPrice"></el-table-column>
<el-table-column align="center" label="差异类型" min-width="100">
<template slot-scope="{row}">
<el-tag :type="row.diffType === 0 ? 'success' : 'danger'" size="mini">{{ diffTypeName(row.diffType) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="差异原因" min-width="220" prop="diffReason" show-overflow-tooltip></el-table-column>
</el-table>
<el-pagination
:current-page="recordForm.pageNumber"
:page-size="recordForm.pageSize"
:page-sizes="[10, 20, 30, 50]"
:total="recordForm.totalCount"
@current-change="recordPageChange"
@size-change="recordSizeChange"
layout="total, sizes, prev, pager, next"
style="margin-top: 16px; text-align: right"
></el-pagination>
<span slot="footer">
<el-button @click="recordDialogVisible = false">关闭</el-button>
</span>
</el-dialog>
<el-dialog :close-on-click-modal="false" title="订单详情" :visible.sync="detailDialogVisible" width="760px">
<el-descriptions :column="2" border size="small" v-if="currentOrder">
<el-descriptions-item label="订单ID">{{ currentOrder.orderId || "-" }}</el-descriptions-item>
<el-descriptions-item label="订单编码">{{ currentOrder.orderNumberId || "-" }}</el-descriptions-item>
<el-descriptions-item label="供应商">{{ currentOrder.supplierName || currentOrder.supplierId || "-" }}</el-descriptions-item>
<el-descriptions-item label="用户ID">{{ currentOrder.userId || "-" }}</el-descriptions-item>
<el-descriptions-item label="下单时间">{{ currentOrder.orderCreateTime || "-" }}</el-descriptions-item>
<el-descriptions-item label="完成时间">{{ currentOrder.orderCompleteTime || "-" }}</el-descriptions-item>
<el-descriptions-item label="订单总金额">{{ currentOrder.totalPrice || "0" }}</el-descriptions-item>
<el-descriptions-item label="现金金额">{{ currentOrder.wPayOrAPay || "0" }}</el-descriptions-item>
<el-descriptions-item label="积分金额">{{ currentOrder.pointsPrice || "0" }}</el-descriptions-item>
<el-descriptions-item label="退款积分">{{ currentOrder.refund || "0" }}</el-descriptions-item>
<el-descriptions-item label="物流单号">{{ currentOrder.logisticsOrderId || "-" }}</el-descriptions-item>
<el-descriptions-item label="承运商">{{ currentOrder.crrgBsnNm || "-" }}</el-descriptions-item>
</el-descriptions>
<span slot="footer">
<el-button @click="detailDialogVisible = false">关闭</el-button>
</span>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
supplierList: [],
tableData: [],
tableLoading: false,
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
supplierId: null,
orderId: null,
userId: null,
startDate: null,
endDate: null,
reconciliationStatus: null,
invoiceStatus: null
},
reconcileDialogVisible: false,
reconcilePointsTotal: 0,
pointsTotalLoading: false,
executeLoading: false,
reconcileForm: {
supplierId: null,
reconciliationType: 1,
orderId: null,
startDate: null,
endDate: null
},
recordDialogVisible: false,
recordLoading: false,
recordList: [],
recordForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
supplierId: null,
orderId: null,
userId: null,
batchNo: null,
diffType: null,
noticeStatus: null
},
detailDialogVisible: false,
currentOrder: null,
reconciliationStatusOptions: [
{ value: 0, label: "未对账" },
{ value: 1, label: "已对账" },
{ value: 2, label: "对账异常" }
],
invoiceStatusOptions: [
{ value: 0, label: "未开票" },
{ value: 1, label: "已开票" },
{ value: 2, label: "开票中" }
],
orderStateOptions: [
{ value: 0, label: "待付款" },
{ value: 1, label: "已支付" },
{ value: 2, label: "进行中" },
{ value: 3, label: "已取消" },
{ value: 4, label: "已签收" },
{ value: 5, label: "已完成" },
{ value: 6, label: "换货" },
{ value: 7, label: "退货" },
{ value: 8, label: "已废除" }
]
}
},
methods: {
indexMethod(index) {
return index + 1 + (this.pageForm.pageNumber - 1) * this.pageForm.pageSize
},
recordIndexMethod(index) {
return index + 1 + (this.recordForm.pageNumber - 1) * this.recordForm.pageSize
},
doSearch() {
this.pageForm.pageNumber = 1
this.pageData()
},
async pageData() {
this.tableLoading = true
const resp = await this.$axios.post(loc() + "/pageData", this.pageForm)
this.tableLoading = false
if (resp.code === 0) {
this.tableData = resp.data.list || []
this.pageForm.totalCount = resp.data.totalCount || 0
} else {
this.notifyWarning(resp.msg)
}
},
pageChange(val) {
this.pageForm.pageNumber = val
this.pageData()
},
sizeChange(val) {
this.pageForm.pageSize = val
this.pageForm.pageNumber = 1
this.pageData()
},
async loadSuppliers() {
const resp = await this.$axios.post(loc() + "/supplierList")
if (resp.code === 0) {
this.supplierList = resp.data || []
}
},
openReconcileDialog() {
this.reconcileForm = {
supplierId: this.pageForm.supplierId,
reconciliationType: this.pageForm.reconciliationStatus === 2 ? 2 : 1,
orderId: this.pageForm.orderId,
startDate: this.pageForm.startDate,
endDate: this.pageForm.endDate
}
this.reconcilePointsTotal = 0
this.reconcileDialogVisible = true
this.loadPointsTotal()
},
async loadPointsTotal() {
if (!this.reconcileForm.supplierId || !this.reconcileForm.startDate || !this.reconcileForm.endDate || !this.reconcileForm.reconciliationType) {
this.notifyWarning("请先选择供应商、对账类型和对账日期")
return
}
this.pointsTotalLoading = true
const resp = await this.$axios.post(loc() + "/pointsTotal", this.reconcileForm)
this.pointsTotalLoading = false
if (resp.code === 0) {
this.reconcilePointsTotal = resp.data || 0
} else {
this.notifyWarning(resp.msg)
}
},
async executeReconciliation() {
if (!this.reconcileForm.supplierId || !this.reconcileForm.startDate || !this.reconcileForm.endDate || !this.reconcileForm.reconciliationType) {
this.notifyWarning("请先选择供应商、对账类型和对账日期")
return
}
this.executeLoading = true
const resp = await this.$axios.post(loc() + "/execute", this.reconcileForm)
this.executeLoading = false
if (resp.code === 0) {
this.notifySuccess(resp.data || "对账完成")
this.reconcileDialogVisible = false
this.pageData()
} else {
this.notifyWarning(resp.msg)
}
},
openRecordDialog() {
this.recordDialogVisible = true
this.recordForm.supplierId = this.pageForm.supplierId
this.recordForm.orderId = this.pageForm.orderId
this.recordPageData()
},
recordSearch() {
this.recordForm.pageNumber = 1
this.recordPageData()
},
recordReset() {
this.recordForm = {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
supplierId: null,
orderId: null,
userId: null,
batchNo: null,
diffType: null,
noticeStatus: null
}
this.recordPageData()
},
async recordPageData() {
this.recordLoading = true
const resp = await this.$axios.post(loc() + "/recordPage", this.recordForm)
this.recordLoading = false
if (resp.code === 0) {
this.recordList = resp.data.list || []
this.recordForm.totalCount = resp.data.totalCount || 0
} else {
this.notifyWarning(resp.msg)
}
},
recordPageChange(val) {
this.recordForm.pageNumber = val
this.recordPageData()
},
recordSizeChange(val) {
this.recordForm.pageSize = val
this.recordForm.pageNumber = 1
this.recordPageData()
},
showOrderDetail(row) {
this.currentOrder = row
this.detailDialogVisible = true
},
reconciliationStatusName(value) {
const item = this.reconciliationStatusOptions.find((v) => String(v.value) === String(value))
return item ? item.label : "-"
},
reconciliationStatusType(value) {
const map = { 0: "info", 1: "success", 2: "danger" }
return map[value] || "info"
},
invoiceStatusName(value) {
const item = this.invoiceStatusOptions.find((v) => String(v.value) === String(value))
return item ? item.label : "-"
},
invoiceStatusType(value) {
const map = { 0: "info", 1: "success", 2: "warning" }
return map[value] || "info"
},
orderStateName(value) {
const item = this.orderStateOptions.find((v) => String(v.value) === String(value))
return item ? item.label : "-"
},
orderStatusType(value) {
const map = {
0: "info",
1: "primary",
2: "warning",
3: "danger",
4: "primary",
5: "success",
6: "warning",
7: "danger",
8: "info"
}
return map[value] || "info"
},
diffTypeName(value) {
return Number(value) === 0 ? "一致" : "异常"
}
},
created() {
this.loadSuppliers()
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,196 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="供应商">
<el-select clearable filterable placeholder="请选择供应商" style="width: 100%" v-model="pageForm.supplierId">
<el-option :key="item.supplierId" :label="item.supplierName" :value="item.supplierId" v-for="item in supplierList"></el-option>
</el-select>
</search-item>
<search-item label="订单ID">
<el-input clearable placeholder="请输入订单ID" v-model="pageForm.orderId"></el-input>
</search-item>
<search-item label="用户ID">
<el-input clearable placeholder="请输入用户ID" v-model="pageForm.userId"></el-input>
</search-item>
<search-item label="批次号">
<el-input clearable placeholder="请输入对账批次号" v-model="pageForm.batchNo"></el-input>
</search-item>
<search-item label="差异类型">
<el-select clearable placeholder="请选择差异类型" style="width: 100%" v-model="pageForm.diffType">
<el-option label="一致" :value="0"></el-option>
<el-option label="异常" :value="1"></el-option>
</el-select>
</search-item>
<search-item label="通知状态">
<el-select clearable placeholder="请选择通知状态" style="width: 100%" v-model="pageForm.noticeStatus">
<el-option label="未推送" :value="0"></el-option>
<el-option label="推送成功" :value="1"></el-option>
<el-option label="推送失败" :value="2"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="对账记录"></table-tool>
<el-table :data="tableData" border class="vi-table" size="small" style="width: 100%" v-loading="tableLoading">
<el-table-column :index="indexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="批次号" min-width="170" prop="batchNo" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="订单ID" min-width="170" prop="orderId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商" min-width="150" prop="supplierName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商编码" min-width="130" prop="supplierId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="用户ID" min-width="120" prop="userId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="订单总金额(平台)" min-width="140" prop="pTotalPrice"></el-table-column>
<el-table-column align="center" label="现金金额(平台)" min-width="130" prop="pCashAmount"></el-table-column>
<el-table-column align="center" label="积分金额(平台)" min-width="130" prop="pPointsPrice"></el-table-column>
<el-table-column align="center" label="退款金额(平台)" min-width="130" prop="pRefund"></el-table-column>
<el-table-column align="center" label="订单总金额(对账)" min-width="140" prop="sTotalPrice"></el-table-column>
<el-table-column align="center" label="现金金额(对账)" min-width="130" prop="sCashAmount"></el-table-column>
<el-table-column align="center" label="积分金额(对账)" min-width="130" prop="sPointsPrice"></el-table-column>
<el-table-column align="center" label="退款金额(对账)" min-width="130" prop="sRefund"></el-table-column>
<el-table-column align="center" label="差异类型" min-width="110">
<template slot-scope="{row}">
<el-tag :type="diffTypeTag(row.diffType)" size="mini">{{ diffTypeName(row.diffType) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="通知状态" min-width="110">
<template slot-scope="{row}">
<el-tag :type="noticeStatusTag(row.noticeStatus)" size="mini">{{ noticeStatusName(row.noticeStatus) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="推送次数" min-width="90" prop="pushTime"></el-table-column>
<el-table-column align="center" label="差异原因" min-width="240" prop="diffReason" show-overflow-tooltip></el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="90">
<template slot-scope="{row}">
<el-button @click="openDetail(row)" size="mini" type="primary">查看</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="pageForm.pageNumber"
:page-size="pageForm.pageSize"
:page-sizes="[10, 20, 30, 50]"
:total="pageForm.totalCount"
@current-change="pageChange"
@size-change="sizeChange"
layout="total, sizes, prev, pager, next"
style="margin-top: 16px; text-align: right"
></el-pagination>
</el-card>
<el-dialog :close-on-click-modal="false" title="对账记录详情" :visible.sync="detailDialogVisible" width="820px">
<el-descriptions :column="2" border size="small" v-if="currentRecord">
<el-descriptions-item label="批次号">{{ currentRecord.batchNo || "-" }}</el-descriptions-item>
<el-descriptions-item label="订单ID">{{ currentRecord.orderId || "-" }}</el-descriptions-item>
<el-descriptions-item label="供应商">{{ currentRecord.supplierName || currentRecord.supplierId || "-" }}</el-descriptions-item>
<el-descriptions-item label="用户ID">{{ currentRecord.userId || "-" }}</el-descriptions-item>
<el-descriptions-item label="平台总金额">{{ currentRecord.pTotalPrice || "0" }}</el-descriptions-item>
<el-descriptions-item label="对账总金额">{{ currentRecord.sTotalPrice || "0" }}</el-descriptions-item>
<el-descriptions-item label="平台现金金额">{{ currentRecord.pCashAmount || "0" }}</el-descriptions-item>
<el-descriptions-item label="对账现金金额">{{ currentRecord.sCashAmount || "0" }}</el-descriptions-item>
<el-descriptions-item label="平台积分金额">{{ currentRecord.pPointsPrice || "0" }}</el-descriptions-item>
<el-descriptions-item label="对账积分金额">{{ currentRecord.sPointsPrice || "0" }}</el-descriptions-item>
<el-descriptions-item label="平台退款金额">{{ currentRecord.pRefund || "0" }}</el-descriptions-item>
<el-descriptions-item label="对账退款金额">{{ currentRecord.sRefund || "0" }}</el-descriptions-item>
<el-descriptions-item label="差异类型">{{ diffTypeName(currentRecord.diffType) }}</el-descriptions-item>
<el-descriptions-item label="通知状态">{{ noticeStatusName(currentRecord.noticeStatus) }}</el-descriptions-item>
<el-descriptions-item label="推送次数">{{ currentRecord.pushTime || 0 }}</el-descriptions-item>
<el-descriptions-item label="差异原因">{{ currentRecord.diffReason || "-" }}</el-descriptions-item>
</el-descriptions>
<span slot="footer">
<el-button @click="detailDialogVisible = false">关闭</el-button>
</span>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
supplierList: [],
tableData: [],
tableLoading: false,
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
supplierId: null,
orderId: null,
userId: null,
diffType: null,
batchNo: null,
noticeStatus: null
},
detailDialogVisible: false,
currentRecord: null
}
},
methods: {
indexMethod(index) {
return index + 1 + (this.pageForm.pageNumber - 1) * this.pageForm.pageSize
},
doSearch() {
this.pageForm.pageNumber = 1
this.pageData()
},
async pageData() {
this.tableLoading = true
const resp = await this.$axios.post(loc() + "/pageData", this.pageForm)
this.tableLoading = false
if (resp.code === 0) {
this.tableData = resp.data.list || []
this.pageForm.totalCount = resp.data.totalCount || 0
} else {
this.notifyWarning(resp.msg)
}
},
pageChange(val) {
this.pageForm.pageNumber = val
this.pageData()
},
sizeChange(val) {
this.pageForm.pageSize = val
this.pageForm.pageNumber = 1
this.pageData()
},
async loadSuppliers() {
const resp = await this.$axios.post(loc() + "/supplierList")
if (resp.code === 0) {
this.supplierList = resp.data || []
}
},
openDetail(row) {
this.currentRecord = row
this.detailDialogVisible = true
},
diffTypeName(value) {
return Number(value) === 0 ? "一致" : "异常"
},
diffTypeTag(value) {
return Number(value) === 0 ? "success" : "danger"
},
noticeStatusName(value) {
const map = { 0: "未推送", 1: "推送成功", 2: "推送失败" }
return map[value] || "-"
},
noticeStatusTag(value) {
const map = { 0: "info", 1: "success", 2: "danger" }
return map[value] || "info"
}
},
created() {
this.loadSuppliers()
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,327 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="供应商名称">
<el-input clearable placeholder="请输入供应商名称" v-model="pageForm.supplierName"></el-input>
</search-item>
<search-item label="商城类型">
<el-select clearable placeholder="请选择商城类型" style="width: 100%" v-model="pageForm.mallType">
<el-option :value="1" label="积分商城"></el-option>
<el-option :value="2" label="观影商城"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="供应商列表">
<el-button @click="openAdd" icon="el-icon-plus" size="small" type="primary">新建</el-button>
<el-button @click="deleteSelected" icon="el-icon-delete" size="small" type="danger">删除</el-button>
</table-tool>
<el-table
:data="tableData"
@selection-change="handleSelectionChange"
border
class="vi-table"
ref="multipleTable"
size="small"
style="width: 100%"
v-loading="tableLoading"
>
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column :index="indexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="供应商ID" min-width="110" prop="supplierId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商名称" min-width="140" prop="supplierName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="商城类型" min-width="100">
<template slot-scope="{row}">
<el-tag size="mini" :type="row.mallType === 1 ? 'success' : 'warning'">{{ mallTypeName(row.mallType) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="首页链接" min-width="180" prop="supplierUrl" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="订单链接" min-width="180" prop="supplierOrderUrl" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="售后订单链接" min-width="180" prop="supplierReturnOrderUrl" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="API链接" min-width="180" prop="supplierApiUrl" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="场景号" min-width="110" prop="sroNo" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="合作到期" min-width="160" prop="cooperationExpTime" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="排序" width="80" prop="sortCode"></el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="150">
<template slot-scope="{row}">
<el-button @click="openEdit(row)" size="mini" type="primary">修改</el-button>
<el-button @click="deleteRows([row])" size="mini" type="danger">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="pageForm.pageNumber"
:page-size="pageForm.pageSize"
:page-sizes="[10, 20, 30, 50]"
:total="pageForm.totalCount"
@current-change="pageChange"
@size-change="sizeChange"
layout="total, sizes, prev, pager, next"
style="margin-top: 16px; text-align: right"
></el-pagination>
</el-card>
<el-dialog :close-on-click-modal="false" :title="formData.id ? '修改供应商' : '新增供应商'" :visible.sync="editDialogVisible" width="760px">
<el-form :model="formData" :rules="rules" label-width="130px" ref="form">
<el-row :gutter="18">
<el-col :span="12">
<el-form-item label="供应商ID" prop="supplierId">
<el-input maxlength="64" placeholder="请输入供应商ID" v-model="formData.supplierId"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="供应商名称" prop="supplierName">
<el-input maxlength="100" placeholder="请输入供应商名称" v-model="formData.supplierName"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商城类型" prop="mallType">
<el-select placeholder="请选择商城类型" style="width: 100%" v-model="formData.mallType">
<el-option :value="1" label="积分商城"></el-option>
<el-option :value="2" label="观影商城"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="排序" prop="sortCode">
<el-input-number :min="0" controls-position="right" style="width: 100%" v-model="formData.sortCode"></el-input-number>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="首页链接" prop="supplierUrl">
<el-input maxlength="1000" placeholder="请输入首页链接" v-model="formData.supplierUrl"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="订单链接" prop="supplierOrderUrl">
<el-input maxlength="1000" placeholder="请输入订单详情链接" v-model="formData.supplierOrderUrl"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="售后订单链接" prop="supplierReturnOrderUrl">
<el-input maxlength="1000" placeholder="请输入售后订单链接" v-model="formData.supplierReturnOrderUrl"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="API链接" prop="supplierApiUrl">
<el-input maxlength="1000" placeholder="请输入供应商API地址" v-model="formData.supplierApiUrl"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="场景号" prop="sroNo">
<el-input maxlength="100" placeholder="请输入场景号" v-model="formData.sroNo"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="合作到期时间" prop="cooperationExpTime">
<el-date-picker
placeholder="请选择合作到期时间"
style="width: 100%"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
v-model="formData.cooperationExpTime"
></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客户端ID" prop="clientId">
<el-input maxlength="128" placeholder="请输入客户端ID" v-model="formData.clientId"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客户端密钥" prop="clientSecret">
<el-input maxlength="256" placeholder="请输入客户端密钥" show-password v-model="formData.clientSecret"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="图标/图片" prop="supplierPic">
<file-upload
:value.sync="formData.supplierPic"
:upload_number="1"
accept=".jpg,.jpeg,.png,.gif,.webp"
upload_mode="image"
upload_result_category="interval"
upload_result_type="url"
></file-upload>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="联系信息" prop="contactInfo">
<el-input maxlength="500" placeholder="请输入联系信息" v-model="formData.contactInfo"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<span slot="footer">
<el-button @click="editDialogVisible = false">取消</el-button>
<el-button :loading="submitLoading" @click="submitForm" type="primary">保存</el-button>
</span>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
tableData: [],
tableLoading: false,
submitLoading: false,
multipleSelection: [],
editDialogVisible: false,
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
supplierName: null,
mallType: null
},
formData: this.defaultForm(),
rules: {
supplierId: [{ required: true, message: "请输入供应商ID", trigger: ["change", "blur"] }],
supplierName: [{ required: true, message: "请输入供应商名称", trigger: ["change", "blur"] }],
mallType: [{ required: true, message: "请选择商城类型", trigger: ["change", "blur"] }],
sortCode: [{ required: true, message: "请输入排序", trigger: ["change", "blur"] }]
}
}
},
methods: {
defaultForm() {
return {
id: null,
supplierId: null,
supplierName: null,
supplierUrl: null,
supplierOrderUrl: null,
supplierReturnOrderUrl: null,
supplierApiUrl: null,
supplierPic: null,
contactInfo: null,
sroNo: null,
cooperationExpTime: null,
clientId: null,
clientSecret: null,
sortCode: 99,
mallType: 1
}
},
indexMethod(index) {
return index + 1 + (this.pageForm.pageNumber - 1) * this.pageForm.pageSize
},
doSearch() {
this.pageForm.pageNumber = 1
this.pageData()
},
async pageData() {
this.tableLoading = true
const resp = await this.$axios.post(loc() + "/pageData", this.pageForm)
this.tableLoading = false
if (resp.code === 0) {
this.tableData = resp.data.list || []
this.pageForm.totalCount = resp.data.totalCount || 0
} else {
this.notifyWarning(resp.msg)
}
},
pageChange(val) {
this.pageForm.pageNumber = val
this.pageData()
},
sizeChange(val) {
this.pageForm.pageSize = val
this.pageForm.pageNumber = 1
this.pageData()
},
handleSelectionChange(val) {
this.multipleSelection = val
},
openAdd() {
this.formData = this.defaultForm()
this.editDialogVisible = true
this.$nextTick(() => {
this.$refs.form && this.$refs.form.clearValidate()
})
},
openEdit(row) {
this.formData = Object.assign(this.defaultForm(), row)
delete this.formData.supplierPicPath
this.editDialogVisible = true
this.$nextTick(() => {
this.$refs.form && this.$refs.form.clearValidate()
})
},
submitForm() {
this.$refs.form.validate(async (valid) => {
if (!valid) {
return
}
this.submitLoading = true
const url = this.formData.id ? "/doEdit" : "/doAdd"
const formData = Object.assign({}, this.formData)
delete formData.supplierPicPath
const resp = await this.$axios.post(loc() + url, formData)
this.submitLoading = false
if (resp.code === 0) {
this.$message.success(resp.msg)
this.editDialogVisible = false
this.pageData()
} else {
this.notifyWarning(resp.msg)
}
})
},
deleteSelected() {
if (!this.multipleSelection.length) {
this.$message.warning("请至少选择一条数据")
return
}
this.deleteRows(this.multipleSelection)
},
deleteRows(rows) {
const names = rows.map((row) => row.supplierName).join("、")
this.$confirm("确定删除供应商“" + names + "”吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
callback: async (action) => {
if (action !== "confirm") {
return
}
const resp = await this.$axios.post(loc() + "/doDelete", {
ids: rows.map((row) => row.id).join(",")
})
if (resp.code === 0) {
this.$message.success(resp.msg)
this.pageData()
} else {
this.notifyWarning(resp.msg)
}
}
})
},
mallTypeName(type) {
if (type === 1) {
return "积分商城"
}
if (type === 2) {
return "观影商城"
}
return "-"
}
},
created() {
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,517 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar fixed placeholder title="积分商城" left-arrow left-text="返回" :border="false"
@click-left="back"></van-nav-bar>
<van-search v-model="keyword" class="fixed-search" shape="round" placeholder="搜索供应商名称"></van-search>
<main class="mall-page">
<van-swipe class="banner" :autoplay="3200" :height="bannerHeight" :show-indicators="false">
<van-swipe-item v-for="banner in banners" :key="banner.id">
<van-image class="banner-image" :src="banner.image" :alt="banner.alt" fit="fill"></van-image>
</van-swipe-item>
</van-swipe>
<section class="account-panel">
<div class="account-card points-card">
<strong>我的积分</strong>
<p>购物时 1 积分可抵扣 1 元</p>
<div class="points-value">
<span>¥</span>
<b>{{ userPoints }}</b>
</div>
</div>
<div class="account-card order-card">
<strong>我的订单</strong>
<p>积分兑换,好物随心换</p>
<van-button class="order-button" round color="#35bfe6" @click="go('/platform/h5/points-mall/order')">
去查看
</van-button>
</div>
</section>
<section class="supplier-section">
<div class="section-head">
<strong>供应商专区</strong>
<span>{{ filteredSuppliers.length }} 家</span>
</div>
<van-skeleton title :row="6" :loading="loading" animated>
<van-empty v-if="!filteredSuppliers.length" description="暂无供应商"></van-empty>
<article v-for="item in filteredSuppliers" :key="item.id || item.supplierId" class="supplier-card">
<div class="supplier-icon">
<img :src="item.supplierPic || defaultLogo" :alt="item.supplierName" @error="onImgError">
</div>
<div class="supplier-info">
<div class="supplier-title">
<h2>{{ item.supplierName }}</h2>
<van-tag class="official-tag" plain type="warning">官方</van-tag>
</div>
<p>{{ item.contactInfo || '精选服务,品质兑换' }}</p>
<div class="tags">
<span>正品保障</span>
<span>积分兑换</span>
<span>售后无忧</span>
</div>
<div class="stats">
<span>{{ mallTypeName(item.mallType) }}</span>
<i></i>
<span>{{ item.sroNo || '服务专区' }}</span>
</div>
</div>
<van-button class="enter-supplier" round color="#ff651d" @click="openSupplier(item)">进入店铺
</van-button>
</article>
</van-skeleton>
</section>
</main>
<footer class="services">
<div class="service">
<van-icon name="shield-o" color="#3d78ed" size="22"></van-icon>
<span><b>正品保障</b><small>品牌服务</small></span>
</div>
<div class="service">
<van-icon name="logistics" color="#15b8a3" size="22"></van-icon>
<span><b>便捷配送</b><small>订单直达</small></span>
</div>
<div class="service">
<van-icon name="service-o" color="#725cf2" size="22"></van-icon>
<span><b>售后无忧</b><small>服务跟进</small></span>
</div>
</footer>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
store,
data() {
return {
keyword: "",
loading: false,
bannerHeight: 86,
banners: [
{
id: "welfare",
image: "/assets/mobile/img/mall/supplier-banner/supplier-banner-welfare.png",
alt: "优质供应商精选"
},
{
id: "quality",
image: "/assets/mobile/img/mall/supplier-banner/supplier-banner-quality.png",
alt: "品质好物精选直供"
},
{
id: "daily",
image: "/assets/mobile/img/mall/supplier-banner/supplier-banner-daily.png",
alt: "精选商品每日推荐"
}
],
supplierList: [],
userPoints: 0,
defaultLogo: "/assets/platform/images/logo.png"
}
},
computed: {
filteredSuppliers() {
const keyword = (this.keyword || "").trim()
if (!keyword) {
return this.supplierList
}
return this.supplierList.filter((item) => {
return [item.supplierName, item.contactInfo, item.sroNo].join("").indexOf(keyword) > -1
})
}
},
methods: {
back() {
this.$pjaxReplace("/platform/h5/home")
},
go(url) {
this.$pjaxReplace(url)
},
async loadSuppliers() {
this.loading = true
const resp = await this.$axios.post("/platform/h5/points-mall/supplierList", {mallType: 1})
this.loading = false
if (resp.code === 0) {
this.supplierList = resp.data || []
} else {
this.$toast(resp.msg || "供应商加载失败")
}
},
async loadPoints() {
const resp = await this.$axios.post("/platform/h5/points-mall/me/points")
if (resp.code === 0) {
this.userPoints = resp.data || 0
}
},
async openSupplier(item) {
if (!item.supplierUrl) {
this.$toast("暂无跳转地址")
return
}
const userId = this.$store && this.$store.state.user ? this.$store.state.user.id : ""
if (!userId) {
this.$toast("获取用户信息失败,请重新登录后再试")
return
}
window.location.href = this.withSupplierQuery(item.supplierUrl, {
userId: userId
})
},
withSupplierQuery(url, params) {
const query = Object.keys(params)
.filter((key) => params[key] !== undefined && params[key] !== null && params[key] !== "")
.map((key) => encodeURIComponent(key) + "=" + encodeURIComponent(params[key]))
.join("&")
if (!query) {
return url
}
const hashIndex = url.indexOf("#")
if (hashIndex > -1) {
const base = url.slice(0, hashIndex)
const hash = url.slice(hashIndex)
const joiner = hash.indexOf("?") > -1 ? "&" : "?"
return base + hash + joiner + query
}
return url + (url.indexOf("?") > -1 ? "&" : "?") + query
},
onImgError(e) {
e.target.src = this.defaultLogo
},
mallTypeName(type) {
if (type === 1) {
return "积分商城"
}
if (type === 2) {
return "观影商城"
}
return "商城服务"
},
updateBannerHeight() {
const pagePadding = 34
const width = Math.max(document.documentElement.clientWidth - pagePadding, 0)
this.bannerHeight = Math.max(Math.round(width * 315 / 1492 * 1.18), 86)
}
},
created() {
this.loadSuppliers()
this.loadPoints()
},
mounted() {
this.updateBannerHeight()
window.addEventListener("resize", this.updateBannerHeight)
},
beforeDestroy() {
window.removeEventListener("resize", this.updateBannerHeight)
}
})
</script>
<style>
body {
background: #fafafa;
}
.fixed-search {
position: fixed;
z-index: 10;
top: 46px;
right: 0;
left: 0;
}
.mall-page {
display: flow-root;
box-sizing: border-box;
background: #fafafa;
}
.banner {
overflow: hidden;
margin: 64px 17px 0;
border-radius: 14px;
background: #fff;
}
.banner .van-swipe__track,
.banner .van-swipe-item {
height: 100%;
}
.banner-image {
display: block;
width: 100%;
height: 100%;
}
.banner-image .van-image__img {
display: block;
width: 100%;
height: 100%;
object-fit: fill;
}
.account-panel {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 14px;
margin: 12px 17px 0;
}
.account-card {
min-height: 110px;
padding: 13px 12px 12px;
box-sizing: border-box;
border-radius: 10px;
background: #fff;
box-shadow: 0 10px 22px rgba(25, 35, 60, .06);
}
.account-card strong {
display: block;
color: #8a4d12;
font-size: 15px;
line-height: 20px;
}
.account-card p {
overflow: hidden;
margin: 4px 0 0;
color: #d6a887;
font-size: 11px;
line-height: 16px;
white-space: nowrap;
text-overflow: ellipsis;
}
.points-value {
display: flex;
align-items: center;
gap: 15px;
margin-top: 13px;
}
.points-value span {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
border-radius: 50%;
color: #fff;
background: #ffb400;
font-size: 14px;
font-weight: 700;
box-shadow: 0 5px 12px rgba(255, 180, 0, .28);
}
.points-value b {
min-width: 0;
color: #ff8a00;
font-size: 20px;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.order-card {
position: relative;
}
.order-button {
position: absolute;
right: 18px;
bottom: 15px;
min-width: 64px;
height: 28px;
padding: 0 12px;
font-size: 13px;
}
.supplier-section {
margin: 14px 17px 76px;
}
.section-head {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 2px 10px;
}
.section-head strong {
color: #1c1c1c;
font-size: 17px;
}
.section-head span {
color: #999;
font-size: 11px;
}
.supplier-card {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 13px;
padding: 14px;
border-radius: 17px;
background: #fff;
}
.supplier-icon {
width: 74px;
height: 74px;
flex: none;
overflow: hidden;
border-radius: 16px;
background: #f4f7ff;
}
.supplier-icon img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.supplier-info {
min-width: 0;
flex: 1;
}
.supplier-title, .tags, .stats, .service {
display: flex;
align-items: center;
}
.supplier-title {
gap: 7px;
}
.supplier-title h2 {
min-width: 0;
overflow: hidden;
margin: 0;
color: #1c1c1c;
font-size: 16px;
line-height: 20px;
text-overflow: ellipsis;
white-space: nowrap;
}
.official-tag {
flex: none;
padding: 0 4px;
font-size: 10px;
line-height: 16px;
}
.supplier-info p {
overflow: hidden;
margin: 4px 0;
color: #777;
font-size: 10px;
text-overflow: ellipsis;
white-space: nowrap;
}
.tags {
gap: 5px;
flex-wrap: nowrap;
overflow-x: auto;
overflow-y: hidden;
padding-bottom: 1px;
font-size: 9px;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.tags::-webkit-scrollbar {
display: none;
}
.tags span {
flex: 0 0 auto;
padding: 2px 6px;
border-radius: 5px;
color: #ff651d;
background: #fff2e8;
white-space: nowrap;
}
.stats {
gap: 5px;
min-width: 0;
margin-top: 3px;
overflow: hidden;
color: #868686;
font-size: 10px;
white-space: nowrap;
}
.stats span {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.stats i {
width: 1px;
height: 13px;
flex: none;
background: #ddd;
}
.enter-supplier {
width: 72px;
height: 24px;
flex: none;
font-size: 10px;
}
.services {
position: fixed;
right: 0;
bottom: 0;
left: 0;
z-index: 9;
display: flex;
height: 58px;
background: #fff;
border-top: 1px solid #f2f2f2;
}
.service {
flex: 1;
justify-content: center;
gap: 5px;
min-width: 0;
font-size: 10px;
}
.service span {
display: grid;
min-width: 0;
font-size: 9px;
}
.service b {
color: #333;
font-weight: 600;
}
.service small {
overflow: hidden;
margin: 0;
color: #c0c4cc;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
<!--#
}
#-->
@@ -0,0 +1,338 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar title="我的订单" left-arrow left-text="返回" @click-left="back" fixed placeholder></van-nav-bar>
<div class="order-page order-list-page">
<section class="order-header">
<div class="points-content">
<div class="points-label">剩余积分</div>
<div class="points-value">
<span>¥</span>
<strong>{{ userPoints }}</strong>
</div>
</div>
</section>
<van-sticky :offset-top="0">
<div class="filter-bar">
<div class="grid-icon">
<van-icon name="apps-o" size="20" color="#1989fa"></van-icon>
</div>
<div class="custom-tab-wrap">
<div class="tab-group tab-group--normal">
<div
v-for="tab in normalTabList"
:key="tab.code"
class="tab-item"
:class="{ 'tab-item--active': Number(pageForm.orderState) === Number(tab.code) }"
@click="onTabClick(tab.code)"
>
{{ tab.name }}
</div>
</div>
<div class="tab-divider">|</div>
<div class="tab-group tab-group--special">
<div
v-for="tab in specialTabList"
:key="tab.code"
class="tab-item"
:class="{ 'tab-item--active': Number(pageForm.orderState) === Number(tab.code) }"
@click="onTabClick(tab.code)"
>
{{ tab.name }}
</div>
</div>
</div>
</div>
</van-sticky>
<van-pull-refresh v-model="refreshing" class="list-container" @refresh="onRefresh">
<van-list v-if="tableData.length" v-model="tableLoading" :finished="tableFinished" finished-text="没有更多了" @load="pageData">
<article class="order-card" v-for="item in tableData" :key="item.id || item.orderId">
<header class="card-header">
<div class="shop-info">
<span class="shop-name">{{ item.supplierName || "积分商城" }}</span>
<van-icon name="arrow" color="#ccc" size="12"></van-icon>
<span class="order-type-name">{{ getOrderTypeName(item.orderType) }}</span>
</div>
<div class="status-box">
<span class="status-text" :class="'status-bg-' + getStatusClass(item.orderState)">{{ orderStateName(item.orderState) }}</span>
</div>
</header>
<div class="card-body" v-if="item.productImgList && item.productImgList.length">
<div class="multi-prod-view" v-if="item.productImgList.length > 1">
<div class="imgs-scroll">
<div class="img-box" v-for="(img, index) in item.productImgList.slice(0, 3)" :key="index">
<van-image :src="img" fit="cover" radius="4"></van-image>
</div>
</div>
</div>
<div class="single-prod-view" v-else>
<van-image :src="item.productImgList[0]" fit="cover" radius="4" class="p-img"></van-image>
</div>
</div>
<div class="order-info">
<div class="info-row" v-if="item.cashPaid === 1">
<span class="payment-warning">积分已支付,待“支付宝/微信/其他”支付</span>
</div>
<div class="info-row" v-if="(Number(item.orderState) === 6 || Number(item.orderState) === 7) && item.orderMainId">
<span class="label">关联发货单号:</span>
<span class="value">{{ item.orderMainId }}</span>
</div>
<div class="info-row" v-if="Number(item.orderState) === 6 && item.relPointsPrice">
<span class="label">原发货单消耗积分:</span>
<span class="value points">{{ item.relPointsPrice }}</span>
</div>
<div class="info-row" v-if="item.wPayOrAPay">
<span class="label">支付宝/微信/其他:</span>
<span class="value points">{{ item.wPayOrAPay }}</span>
</div>
<div class="info-row" v-if="item.refund">
<span class="label">退回积分:</span>
<span class="value points">{{ item.refund }}</span>
</div>
<div class="info-row" v-if="item.orderNumberId">
<span class="label">订单编号:</span>
<span class="value">{{ item.orderNumberId }}</span>
</div>
<div class="info-row">
<span class="label">订单号:</span>
<span class="value">{{ item.orderId }}</span>
</div>
<div class="info-row">
<span class="label">消费积分:</span>
<span class="value points">{{ item.pointsPrice || 0 }}</span>
</div>
<div class="info-row" v-if="item.freightCost">
<span class="label">运费:</span>
<span class="value">¥ {{ item.freightCost }}</span>
</div>
<div class="info-row">
<span class="label">创建时间:</span>
<span class="value">{{ item.orderCreateTime || "-" }}</span>
</div>
</div>
<footer class="card-footer">
<div class="order-type"></div>
<div class="action-bar">
<van-button v-if="Number(item.orderState) === 4" round size="small" color="#4CAF50" @click.stop="userOrderReceive(item)">确认收货</van-button>
<van-button round size="small" color="#409EFF" @click.stop="openSupplierOrder(item)">查看订单</van-button>
</div>
</footer>
</article>
</van-list>
<van-empty
v-else-if="!tableLoading"
class="empty-tip"
image="/assets/platform/plugins/vant-green/images/nodata/nodata.png"
description="暂无订单数据"
></van-empty>
</van-pull-refresh>
</div>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data() {
return {
userPoints: 0,
tableLoading: false,
refreshing: false,
tableFinished: false,
tableData: [],
normalTabList: [
{ code: -1, name: "全部", class: "gray" },
{ code: 0, name: "待付款", class: "orange" },
{ code: 2, name: "进行中", class: "blue" },
{ code: 3, name: "已取消", class: "gray" },
{ code: 4, name: "已妥投", class: "green" },
{ code: 5, name: "已完成", class: "gray" }
],
specialTabList: [
{ code: 6, name: "换货", class: "purple" },
{ code: 7, name: "退货", class: "red" }
],
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
orderState: -1
}
}
},
methods: {
back() { this.$pjaxReplace("/platform/h5/points-mall") },
async loadPoints() {
const resp = await this.$axios.post("/platform/h5/points-mall/me/points")
if (resp.code === 0) this.userPoints = resp.data || 0
},
doSearch() {
this.pageForm.pageNumber = 1
this.tableData = []
this.tableFinished = false
this.pageData()
},
onTabClick(code) {
if (Number(this.pageForm.orderState) === Number(code)) return
this.pageForm.orderState = code
this.doSearch()
},
async pageData() {
if (this.tableFinished) return
this.tableLoading = true
const resp = await this.$axios.post("/platform/h5/points-mall/order/pageData", this.pageForm)
this.tableLoading = false
if (resp.code !== 0) {
this.$toast(resp.msg || "订单加载失败")
this.tableFinished = true
return
}
const data = resp.data || {}
const rows = data.list || []
this.tableData = this.tableData.concat(rows)
this.pageForm.totalCount = data.totalCount || 0
if (!rows.length || this.tableData.length >= this.pageForm.totalCount) {
this.tableFinished = true
} else {
this.pageForm.pageNumber++
}
},
async onRefresh() {
this.pageForm.pageNumber = 1
this.tableData = []
this.tableFinished = false
try {
await this.loadPoints()
await this.pageData()
} finally {
this.refreshing = false
}
},
async openSupplierOrder(item) {
const resp = await this.$axios.post("/platform/h5/points-mall/order/supplierOrderUrl", {
supplierId: item.supplierId,
orderId: item.orderId,
orderType: item.orderType
})
const url = resp.code === 0 && resp.data ? resp.data.supplierOrderUrl : ""
if (url) window.location.href = url
else this.$toast("暂无订单跳转地址")
},
async userOrderReceive(item) {
try {
await this.$dialog.confirm({
title: "确认收货",
message: "您是否确认收货?",
cancelButtonText: "取消",
confirmButtonText: "确认"
})
} catch (e) {
return
}
const resp = await this.$axios.post("/platform/h5/points-mall/order/userOrderReceive", {
emplid: item.userId,
supplierId: item.supplierId,
orderId: item.orderId,
orderType: item.orderType
})
if (resp.code !== 0) {
this.$toast(resp.msg || "确认收货失败")
return
}
this.$toast.success("确认收货成功")
this.loadPoints()
this.doSearch()
},
orderStateName(value) {
const item = this.getOrderStatusItem(value)
return item ? item.name : "未知状态"
},
getStatusClass(status) {
const item = this.getOrderStatusItem(status)
return item ? item.class : "gray"
},
getOrderStatusItem(value) {
const list = this.normalTabList.concat(this.specialTabList)
return list.find((tab) => Number(tab.code) === Number(value))
},
getOrderTypeName(orderType) {
const typeMap = { 1: "支付单", 2: "发货单" }
return typeMap[orderType] || ""
}
},
created() {
this.loadPoints()
this.pageData()
}
})
</script>
<style>
body { height: 100%; overflow: hidden; background: #f7f8fa; }
html { height: 100%; overflow: hidden; }
#app { height: 100%; overflow: hidden; }
.order-list-page { height: calc(100vh - 46px); padding-bottom: 0; overflow: hidden; background: #f7f8fa; }
.order-header { position: relative; height: 160px; overflow: hidden; background: url("/assets/mobile/img/mall/order/my-order.png") center / cover no-repeat; }
.points-content { position: absolute; left: 24px; top: 46px; z-index: 2; }
.points-label { margin-bottom: 8px; color: #111; font-size: 14px; line-height: 20px; }
.points-value { display: flex; align-items: center; color: #333; font-weight: 700; }
.points-value span { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; margin-right: 8px; border-radius: 50%; color: #fff; background: #ffc107; font-size: 14px; box-shadow: 0 2px 4px rgba(255, 193, 7, .4); }
.points-value strong { color: #000; font-size: 28px; line-height: 32px; font-weight: 800; font-family: "DIN Alternate", Arial, sans-serif; }
.filter-bar { display: flex; align-items: center; height: 44px; background: #fff; box-shadow: 0 2px 6px rgba(0, 0, 0, .02); }
.grid-icon { flex: none; width: 40px; display: flex; align-items: center; justify-content: center; }
.custom-tab-wrap { flex: 1; display: flex; align-items: center; height: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.custom-tab-wrap::-webkit-scrollbar { display: none; }
.tab-group { flex: none; display: flex; align-items: center; height: 100%; }
.tab-group--normal { background: #eef5ff; }
.tab-group--special { background: #fff4ec; }
.tab-divider { flex: none; display: flex; align-items: center; align-self: stretch; padding: 0 2px; color: #d0d0d0; background: #fff; font-size: 16px; line-height: 44px; }
.tab-item { position: relative; display: flex; align-items: center; height: 100%; padding: 0 12px; color: #666; font-size: 13px; white-space: nowrap; }
.tab-item:active { opacity: .7; }
.tab-group--normal .tab-item--active { color: #1989fa; font-weight: 700; }
.tab-group--special .tab-item--active { color: #e65100; font-weight: 700; }
.tab-item--active:after { position: absolute; bottom: 0; left: 50%; width: 20px; height: 2px; border-radius: 2px; content: ""; transform: translateX(-50%); }
.tab-group--normal .tab-item--active:after { background: #1989fa; }
.tab-group--special .tab-item--active:after { background: #e65100; }
.list-container { height: calc(100vh - 46px - 160px - 44px); padding: 12px; overflow-y: auto; background: #f7f8fa; -webkit-overflow-scrolling: touch; box-sizing: border-box; }
.order-card { margin-bottom: 12px; padding: 16px; border-radius: 12px; background: #fff; box-shadow: 0 2px 8px rgba(0, 0, 0, .04); }
.card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid #f5f5f5; }
.shop-info { display: flex; align-items: center; min-width: 0; }
.shop-name { overflow: hidden; margin-right: 4px; color: #333; font-size: 15px; font-weight: 700; white-space: nowrap; text-overflow: ellipsis; }
.order-type-name { flex: none; margin-left: 4px; color: #666; font-size: 12px; }
.status-box { flex: none; display: flex; align-items: center; margin-left: 10px; font-size: 13px; font-weight: 500; }
.status-text { display: inline-flex; align-items: center; justify-content: center; min-width: 52px; height: 24px; padding: 0 8px; border-radius: 10px; font-size: 12px; line-height: 24px; box-sizing: border-box; }
.status-bg-gray { color: #999; background: #f5f5f5; }
.status-bg-green { color: #2e7d32; background: #e8f5e9; }
.status-bg-blue { color: #1565c0; background: #e3f2fd; }
.status-bg-orange { color: #e65100; background: #fff8e1; }
.status-bg-purple { color: #6a1b9a; background: #ede7f6; }
.status-bg-red { color: #c62828; background: #fce4ec; }
.card-body { margin-bottom: 12px; }
.multi-prod-view { display: flex; align-items: center; }
.imgs-scroll { flex: 1; display: flex; overflow: hidden; }
.img-box { flex: none; width: 70px; height: 70px; margin-right: 8px; background: #f3f4f6; border-radius: 4px; overflow: hidden; }
.single-prod-view { display: flex; }
.p-img { flex: none; width: 70px; height: 70px; margin-right: 12px; background: #f3f4f6; }
.card-body .van-image { width: 70px; height: 70px; display: block; }
.order-info { padding: 12px; margin-bottom: 12px; border-radius: 8px; background: #fafafa; }
.info-row { display: flex; justify-content: space-between; gap: 12px; margin-bottom: 8px; font-size: 13px; line-height: 18px; }
.info-row:last-child { margin-bottom: 0; }
.info-row span:first-child { flex: none; color: #999; }
.info-row .label { flex: none; color: #999; }
.info-row .value { min-width: 0; color: #333; text-align: right; word-break: break-all; }
.info-row em { min-width: 0; color: #333; font-style: normal; text-align: right; word-break: break-all; }
.info-row .points { color: #c62828; font-weight: 700; }
.card-footer { display: flex; align-items: center; justify-content: space-between; }
.order-type { flex: 1; min-width: 0; }
.action-bar { display: flex; justify-content: flex-end; gap: 10px; }
.card-footer .van-button { min-width: 86px; height: 32px; padding: 0 16px; font-size: 13px; }
.empty-tip { padding-top: 36px; }
.info-row .payment-warning { color: #e64a3b; font-size: 14px; }
</style>
<!--#
}
#-->
File diff suppressed because it is too large Load Diff