wangfukang 3 months ago
parent
commit
9769a6a640
  1. 23
      src/api/campus.js
  2. 5
      src/api/index.js
  3. 53
      src/views/app/business/courier/courier.vue
  4. 42
      src/views/app/business/courier/logisticsAddress.vue
  5. 1197
      src/views/app/business/deliverytruck/deliveryTruck.vue
  6. 156
      src/views/app/business/logistics/logistics.vue
  7. 50
      src/views/app/business/luckey/luckey.vue
  8. 86
      src/views/app/data-statistics/data-statistics.vue
  9. 42
      src/views/app/shop/logisticsAddress.vue
  10. 60
      src/views/sys/order-manage/logisticsAddress.vue
  11. 45
      src/views/sys/order-manage/orderManage.vue

23
src/api/campus.js

@ -0,0 +1,23 @@
import { getRequest, postBodyRequest } from '@/libs/axios';
const PREFIX = '/app/campus/admin';
export const campusCategoryList = (params) => getRequest(`${PREFIX}/categories`, params);
export const campusCategorySave = (params) => postBodyRequest(`${PREFIX}/categories`, params);
export const campusCategoryDelete = (id) => postBodyRequest(`${PREFIX}/categories/delete?id=${id}`, {});
export const campusCategoryStatus = (params) => postBodyRequest(`${PREFIX}/categories/status`, params);
export const campusWallPostPage = (params) => postBodyRequest(`${PREFIX}/wall/posts/page`, params);
export const campusPartnerPostPage = (params) => postBodyRequest(`${PREFIX}/partner/posts/page`, params);
export const campusPostStatus = (params) => postBodyRequest(`${PREFIX}/posts/status`, params);
export const campusPostComments = (id, params) => getRequest(`${PREFIX}/posts/${id}/comments/page`, params);
export const campusCommentDelete = (id) => postBodyRequest(`${PREFIX}/comments/${id}/delete`, {});
export const campusComplaintPage = (params) => postBodyRequest(`${PREFIX}/complaints/page`, params);
export const campusComplaintHandle = (params) => postBodyRequest(`${PREFIX}/complaints/handle`, params);
export const campusComplaintUnblock = (params) => postBodyRequest(`${PREFIX}/complaints/unblock`, params);
export const campusUserLimitPage = (params) => postBodyRequest(`${PREFIX}/users/limits/page`, params);
export const campusUserLimitSave = (params) => postBodyRequest(`${PREFIX}/users/limits`, params);
export const campusAdminLogPage = (params) => postBodyRequest(`${PREFIX}/logs/page`, params);
export const campusAuditLogPage = (params) => getRequest(`${PREFIX}/audit-logs/page`, params);

5
src/api/index.js

@ -712,6 +712,11 @@ export const disableWorker = (id, params) => {
return postRequest(`/worker/admin/disable/${id}`, params)
}
// 设置配送员全职状态
export const setWorkerFullTime = (id, params) => {
return postRequest(`/worker/admin/fullTime/${id}`, params)
}
// 抢单工上线
export const onLineWorker = (params) => {
return postRequest(`/worker/onLine`, params)

53
src/views/app/business/courier/courier.vue

@ -102,6 +102,7 @@
deleteWorker,
offLineWorker,
onLineWorker,
setWorkerFullTime,
getWorkerRelaPriceByWorkerId,
} from "@/api/index";
import {
@ -195,6 +196,19 @@
title: "配送员名称",
key: "workerName",
minWidth: 125,
render: (h, params) => {
return h("div", [
h("div", params.row.workerName || "-"),
h("Tag", {
props: {
color: params.row.isFullTime == 1 ? "green" : "default",
},
style: {
marginTop: "4px",
},
}, params.row.isFullTime == 1 ? "全职配送员" : "非全职配送员"),
]);
},
},
{
title: "佣金余额(元)",
@ -334,7 +348,7 @@
{
title: "操作",
key: "action",
width: 260,
width: 340,
align: "center",
fixed: "right",
render: (h, params) => {
@ -396,6 +410,17 @@
"上线"
);
}
const fullTimeText = params.row.isFullTime == 1 ? "取消全职" : "设置全职";
const fullTimeAction = h(
"a", {
on: {
click: () => {
this.changeFullTime(params.row);
},
},
},
fullTimeText
);
return h("div", [
/* h(
"a", {
@ -449,6 +474,12 @@
},
"接单规则"
),
h("Divider", {
props: {
type: "vertical",
},
}),
fullTimeAction,
]);
},
},
@ -759,6 +790,26 @@
},
});
},
changeFullTime(v) {
const isFullTime = v.isFullTime == 1 ? 0 : 1;
const actionText = isFullTime == 1 ? "设置为全职配送员" : "取消全职配送员";
this.$Modal.confirm({
title: actionText,
content: "您确认要将配送员 " + v.workerName + " " + actionText + "吗?",
loading: true,
onOk: () => {
setWorkerFullTime(v.workerId, {
isFullTime
}).then((res) => {
this.$Modal.remove();
if (res.success) {
this.$Message.success("操作成功");
this.getDataList();
}
});
},
});
},
remove(v) {
this.$Modal.confirm({
title: "确认删除",

42
src/views/app/business/courier/logisticsAddress.vue

@ -15,6 +15,14 @@
<Button type="text" @click="replyVisible = false">关闭</Button>
</div>
</Modal>
<Modal title="评价图" v-model="previewVisible" :mask-closable="true" :width="800">
<div class="comment-image-preview">
<img :src="previewImage" />
</div>
<div slot="footer">
<Button type="text" @click="previewVisible = false">关闭</Button>
</div>
</Modal>
</div>
</template>
@ -24,8 +32,6 @@
deleteComment,
getCommentList
} from "@/api/app";
import "viewerjs/dist/viewer.css";
import Viewer from "viewerjs";
export default {
name: "logisticsAddress",
components: {
@ -45,6 +51,8 @@
selectList: [], // 多选数据
replyVisible: false,
replyList: [],
previewVisible: false,
previewImage: "",
replyColumns: [
{
type: "index",
@ -111,7 +119,7 @@
key: "picture",
minWidth: 125,
render: (h, params) => {
if (!params.row.picture) return h("span", "-");
return h("img", {
attrs: {
src: params.row.picture,
@ -124,7 +132,8 @@
"object-fit": "contain",
},
on: {
click: () => {
click: (event) => {
event.stopPropagation();
this.showPic(params.row.picture);
},
},
@ -239,17 +248,12 @@
return content;
},
showPic(v) {
if(v == ''){
this.$Message.warning("没有评价图");return
if (!v) {
this.$Message.warning("没有评价图");
return;
}
let image = new Image();
image.src = v;
let viewer = new Viewer(image, {
hidden: function() {
viewer.destroy();
},
});
viewer.show();
this.previewImage = v;
this.previewVisible = true;
},
formatDateTime(isoString) {
if (!isoString) return ''
@ -289,4 +293,14 @@
</script>
<style lang="less">
//@import "@/styles/table-common.less";
.comment-image-preview {
text-align: center;
}
.comment-image-preview img {
max-width: 100%;
max-height: 70vh;
object-fit: contain;
}
</style>

1197
src/views/app/business/deliverytruck/deliveryTruck.vue

File diff suppressed because it is too large

156
src/views/app/business/logistics/logistics.vue

@ -200,6 +200,7 @@
<Tabs v-model="workerOrderTab" :animated="false" @on-click="changeWorkerOrderTab">
<TabPane :label="'待取货(' + workerOrderCounts.daiqu + ')'" name="daiqu"></TabPane>
<TabPane :label="'待送达(' + workerOrderCounts.daisong + ')'" name="daisong"></TabPane>
<TabPane :label="'指派单(' + workerOrderCounts.zhipai + ')'" name="zhipai"></TabPane>
</Tabs>
<Table :loading="workerOrderLoading" border :columns="workerOrderColumns" :data="workerOrderList" :size="tableSize"
:row-class-name="getOrderRowClassName"></Table>
@ -342,7 +343,8 @@
},
workerOrderCounts: {
daiqu: 0,
daisong: 0
daisong: 0,
zhipai: 0
},
workerList: this.buildWorkerColumns(),
workerOrderColumns: [],
@ -354,6 +356,8 @@
workerOrderTotal: 0,
orderId: "",
currentTransferWorkerId: "",
currentTransferOrder: null,
currentTransferHallTab: "",
transferActionTitle: "指派配送员",
transferActionType: "assign",
currentRegionId: regionId,
@ -396,6 +400,20 @@
key: "workerName",
width: 200,
align: "center",
render: (h, params) => {
const isFullTime = params.row.isFullTime == 1;
return h("div", [
h("div", params.row.workerName || "-"),
h("Tag", {
props: {
color: isFullTime ? "green" : "orange"
},
style: {
marginTop: "4px"
}
}, isFullTime ? "全职配送员" : "兼职配送员")
]);
}
}, {
title: "联系电话",
key: "mobile",
@ -446,33 +464,31 @@
key: "createTime",
width: 190,
align: "center",
render: (h, params) => h("div", [
h("div", this.formatDateTime(params.row.createTime)),
h("div", { class: "order-time-order-no" }, "订单编号:" + this.getOrderNo(params.row))
])
}, {
title: "配送员",
key: "workerName",
width: 170,
align: "center",
render: (h, params) => this.renderWorkerInfo(h, params.row, clickableWorker)
render: (h, params) => {
const children = [];
if (this.tabName === "delivery" && params.row.numberCode) {
children.push(h("div", {
style: { color: "#515a6e", fontWeight: 700, marginBottom: "4px" }
}, "编号:" + params.row.numberCode));
}
children.push(h("div", this.formatDateTime(params.row.createTime)));
children.push(h("div", { class: "order-time-order-no" }, "订单编号:" + this.getOrderNo(params.row)));
return h("div", children);
}
},{
title: "送达时间",
key: "mustFinishTime",
width: 210,
width: 260,
align: "center",
render: (h, params) => h("div", [
h("span", { class: "must-finish-time" }, this.formatDateTime(params.row.mustFinishTime)),
params.row.shopMakeTime ? h("div", {
style: { color: "red", marginTop: "4px", fontWeight: 700 }
}, "商家已出餐:" + this.formatDateTime(params.row.shopMakeTime)) : null,
h("span", { class: "must-finish-time" }, "要求送达:" +this.formatDateTime(params.row.mustFinishTime)),
this.isNearMustFinishTime(params.row) ? h("div", {
style: { color: "#515a6e", marginTop: "4px", fontWeight: 700 }
}, this.getMustFinishWarningText(params.row)) : null
])
},{
title: "配送类型",
key: "deliveryType",
width: 140,
align: "center",
render: (h, params) => this.renderDeliveryTags(h, params.row)
}, {
title: "商家/取货地址",
key: "shopAddress",
@ -483,6 +499,18 @@
key: "receiverAddress",
minWidth: 240,
render: (h, params) => this.renderAddressBlock(h, params.row, "receiver")
}, {
title: "配送员",
key: "workerName",
width: 170,
align: "center",
render: (h, params) => this.renderWorkerInfo(h, params.row, clickableWorker)
},{
title: "配送类型",
key: "deliveryType",
width: 140,
align: "center",
render: (h, params) => this.renderDeliveryTags(h, params.row)
}, {
title: "操作",
key: "action",
@ -675,10 +703,13 @@
this.getWorkerOrders();
},
buildWorkerOrderParams() {
if (this.workerOrderTab === "zhipai") {
return this.buildWorkerAssignedOrderParams();
}
const source = this.activeSearchForm || {};
const params = {
regionId: source.regionId || this.currentRegionId,
deliveryType: source.deliveryType,
deliveryType: this.getWorkerDeliveryTypeFilter(source),
getAreaId: source.getAreaId,
putAreaId: source.putAreaId,
shopName: source.shopName,
@ -690,31 +721,71 @@
};
return this.cleanParams(params);
},
buildWorkerAssignedOrderParams() {
const source = this.activeSearchForm || {};
const params = {
regionId: source.regionId || this.currentRegionId,
deliveryType: HALL_TYPE_MAP.zhipai,
workerId: this.selectedWorker.workerId,
getAreaId: source.getAreaId,
putAreaId: source.putAreaId,
shopName: source.shopName,
order: source.order,
pageNum: this.workerOrderForm.pageNum,
pageSize: this.workerOrderForm.pageSize
};
return this.cleanParams(params);
},
buildWorkerOrderCountParams(status) {
return Object.assign({}, this.buildWorkerOrderParams(), {
const source = this.activeSearchForm || {};
const params = {
regionId: source.regionId || this.currentRegionId,
deliveryType: this.getWorkerDeliveryTypeFilter(source),
getAreaId: source.getAreaId,
putAreaId: source.putAreaId,
shopName: source.shopName,
order: source.order,
workerId: this.selectedWorker.workerId,
status: status,
pageNum: 1,
pageSize: 1
};
return this.cleanParams(params);
},
getWorkerDeliveryTypeFilter(source) {
return source.deliveryType === HALL_TYPE_MAP.zhipai ? null : source.deliveryType;
},
buildWorkerAssignedOrderCountParams() {
return Object.assign({}, this.buildWorkerAssignedOrderParams(), {
pageNum: 1,
pageSize: 1
});
},
getWorkerOrderCounts() {
if (!this.selectedWorker.workerId) return;
Promise.all([
orderSchedulingAdmin(this.buildWorkerOrderCountParams(DELIVERY_STATUS_MAP.daiqu)),
orderSchedulingAdmin(this.buildWorkerOrderCountParams(DELIVERY_STATUS_MAP.daisong))
]).then(([daiqu, daisong]) => {
orderSchedulingAdmin(this.buildWorkerOrderCountParams(DELIVERY_STATUS_MAP.daisong)),
getAdminDeliveryHallData(this.buildWorkerAssignedOrderCountParams())
]).then(([daiqu, daisong, zhipai]) => {
if (this.isSuccess(daiqu)) {
this.workerOrderCounts.daiqu = Number((daiqu.result || {}).total) || 0;
}
if (this.isSuccess(daisong)) {
this.workerOrderCounts.daisong = Number((daisong.result || {}).total) || 0;
}
if (this.isSuccess(zhipai)) {
this.workerOrderCounts.zhipai = Number((zhipai.result || {}).total) || 0;
}
});
},
getWorkerOrders() {
if (!this.selectedWorker.workerId) return;
this.workerOrderLoading = true;
orderSchedulingAdmin(this.buildWorkerOrderParams()).then((res) => {
const request = this.workerOrderTab === "zhipai"
? getAdminDeliveryHallData(this.buildWorkerOrderParams())
: orderSchedulingAdmin(this.buildWorkerOrderParams());
request.then((res) => {
if (this.isSuccess(res)) {
const result = res.result || {};
this.workerOrderList = this.normalizeRecords(result.records);
@ -895,6 +966,7 @@
this.$Message.success(actionText + "配送员成功");
this.transferOrder = false;
this.workerOrderVisible = false;
this.updateHallCountsAfterReassign();
this.getLogisticsInfo();
}
});
@ -932,10 +1004,29 @@
this.searchWorker.orderType = this.getWorkerOrderType(v);
this.orderId = v.id;
this.currentTransferWorkerId = v.workerId || "";
this.currentTransferOrder = v;
this.currentTransferHallTab = this.tabName === "hall" ? this.hallTab : "";
this.transferActionType = v.workerId ? "reassign" : "assign";
this.transferActionTitle = v.workerId ? "改派配送员" : "指派配送员";
this.getWorker();
},
updateHallCountsAfterReassign() {
if (this.transferActionType !== "reassign") return;
if (["waimai", "kuaidi", "paotui"].indexOf(this.currentTransferHallTab) === -1) return;
const sourceKey = this.getHallCountKeyByDeliveryType(this.currentTransferOrder && this.currentTransferOrder.deliveryType);
if (sourceKey !== this.currentTransferHallTab) return;
this.hallCounts[sourceKey] = Math.max(this.toNumber(this.hallCounts[sourceKey]) - 1, 0);
this.hallCounts.zhipai = this.toNumber(this.hallCounts.zhipai) + 1;
},
getHallCountKeyByDeliveryType(deliveryType) {
const map = {
1: "waimai",
2: "kuaidi",
3: "paotui",
4: "zhipai"
};
return map[Number(deliveryType)] || "";
},
getWorker() {
this.workerLoading = true;
getMatchingWorkerListData(this.cleanParams(this.searchWorker)).then((res) => {
@ -1005,7 +1096,7 @@
]);
},
getDeliveryTypeText(type, row) {
if (row && row.workerId && this.tabName === "hall") return "指派单";
if (row && row.workerId && (this.tabName === "hall" || (this.workerOrderVisible && this.workerOrderTab === "zhipai"))) return "指派单";
if (type == 1) return "外卖";
if (type == 2) return "快递";
if (type == 3) return "跑腿";
@ -1031,7 +1122,7 @@
return h("div", tags);
},
getDeliveryTypeColor(type, row) {
if (row && row.workerId && this.tabName === "hall") return "blue";
if (row && row.workerId && (this.tabName === "hall" || (this.workerOrderVisible && this.workerOrderTab === "zhipai"))) return "blue";
if (type == 1) return "gold";
if (type == 2) return "orange";
if (type == 3) return "green";
@ -1069,19 +1160,26 @@
]);
},
renderActions(h, row) {
const children = [h("a", {
const actions = [h("a", {
on: {
click: () => this.orderDetail(row)
}
}, "订单详情")];
if (this.canAssignWorker(row)) {
children.push(h("Divider", { props: { type: "vertical" } }));
children.push(h("a", {
actions.push(h("Divider", { props: { type: "vertical" } }));
actions.push(h("a", {
on: {
click: () => this.unbind(row)
}
}, row.workerId ? "改派订单" : "指派订单"));
}
const children = [];
if (row.isReturn == 1) {
children.push(h("div", {
style: { color: "#ed4014", fontWeight: 700, marginBottom: "4px" }
}, "订单申请退款"));
}
children.push(h("div", actions));
return h("div", children);
},
canAssignWorker(row) {

50
src/views/app/business/luckey/luckey.vue

@ -12,7 +12,7 @@
<Alert show-icon>
现金奖池采用“主动投入”规则:用户投入星球券后即从余额扣除并回收,本期按投入券数加权开奖,投入越多中奖概率越高
</Alert>
<Button type="primary" icon="md-add" @click="openPool()" style="margin-bottom:12px">新建奖池</Button>
<Button v-if="canCreatePool" type="primary" icon="md-add" @click="openPool()" style="margin-bottom:12px">新增奖池</Button>
<Table border :loading="pool.loading" :columns="poolColumns" :data="pool.data"></Table>
<Row type="flex" justify="end" class="page">
<Page :current="pool.pageNumber" :total="pool.total" :page-size="pool.pageSize"
@ -199,16 +199,20 @@
<Modal v-model="poolModal" :title="poolForm.id ? '编辑奖池' : '新建奖池'" :mask-closable="false">
<Form :label-width="90">
<FormItem label="所属校区"><Input v-model="regionName" disabled placeholder="自动取自登录校区" /></FormItem>
<FormItem label="期号"><Input v-model="poolForm.periodNo" /></FormItem>
<FormItem label="期号日期">
<DatePicker type="date" v-model="poolForm.poolDate" format="yyyy-MM-dd" style="width:100%"
placeholder="选择期号日期" @on-change="onPoolDateChange"></DatePicker>
</FormItem>
<FormItem label="期号"><Input v-model="poolForm.periodNo" disabled placeholder="选择日期后自动生成" /></FormItem>
<FormItem label="标题"><Input v-model="poolForm.title" /></FormItem>
<FormItem label="奖池金额"><InputNumber v-model="poolForm.poolAmount" :min="0" style="width:100%" /></FormItem>
<FormItem label="开奖时间"><Input v-model="poolForm.drawTime" disabled placeholder="固定每天22:00开奖" /></FormItem>
<FormItem label="状态">
<!-- <FormItem label="状态">
<Select v-model="poolForm.status">
<Option :value="0">进行中</Option>
<Option :value="1">已开奖</Option>
</Select>
</FormItem>
</FormItem> -->
<FormItem label="备注"><Input v-model="poolForm.remark" type="textarea" /></FormItem>
</Form>
<div slot="footer">
@ -453,6 +457,9 @@
}
},
computed: {
canCreatePool() {
return !this.pool.loading && Number(this.pool.total || 0) === 0 && this.pool.data.length === 0
},
operateTitle() {
const map = { tree: '券树周期', warehouse: '仓库等级', tower: '防御塔等级', search: '搜查规则', stamina: '体力道具', landmark: '校园地标', college: '学院配置' }
return (this.operateForm.id ? '编辑' : '新增') + (map[this.operateType] || '经营配置')
@ -934,18 +941,21 @@
},
// ---- 奖池 ----
openPool(row) {
this.poolForm = row ? Object.assign({}, row) : {
this.poolForm = row ? Object.assign({}, row, {
poolDate: this.poolDateFromRow(row)
}) : {
regionId: this.regionId || '',
poolAmount: 0,
drawTime: this.defaultDrawTime(),
status: 0
}
if (this.poolForm.status !== 1) this.poolForm.drawTime = this.defaultDrawTime(this.poolForm.drawTime)
this.syncPoolPeriodNo(this.poolForm)
this.poolModal = true
},
savePool() {
const form = Object.assign({}, this.poolForm)
if (form.status !== 1) form.drawTime = this.defaultDrawTime(form.drawTime)
this.syncPoolPeriodNo(form)
delete form.poolDate
planetPoolSave(form).then(r => {
if (r && r.success) {
this.$Message.success('保存成功')
@ -1114,6 +1124,32 @@
const p = n => (n < 10 ? '0' + n : n)
return `${value.getFullYear()}-${p(value.getMonth() + 1)}-${p(value.getDate())}`
},
parseDate(value) {
if (!value) return null
if (value instanceof Date) return value
if (typeof value === 'string' && value.length >= 10) {
return new Date(value.slice(0, 10).replace(/-/g, '/'))
}
return null
},
poolDateFromRow(row) {
if (row.drawTime) return this.formatDateParam(this.parseDate(row.drawTime))
if (row.periodNo && row.periodNo.length >= 8) {
const periodNo = row.periodNo.replace(/^P/, '').slice(0, 8)
return `${periodNo.slice(0, 4)}-${periodNo.slice(4, 6)}-${periodNo.slice(6, 8)}`
}
return this.formatDateParam(this.parseDate(this.defaultDrawTime()))
},
onPoolDateChange(value) {
this.poolForm.poolDate = value
this.syncPoolPeriodNo(this.poolForm)
},
syncPoolPeriodNo(form) {
const poolDate = this.formatDateParam(this.parseDate(form.poolDate || form.drawTime) || new Date())
form.poolDate = poolDate
form.periodNo = poolDate.replace(/-/g, '')
form.drawTime = `${poolDate} 22:00:00`
},
defaultDrawTime(value) {
let d = value instanceof Date ? value : new Date()
if (!(value instanceof Date) && typeof value === 'string' && value.length >= 10) {

86
src/views/app/data-statistics/data-statistics.vue

@ -54,7 +54,7 @@
</Card>
<Card v-if="tabName !== '3' && tabName !== '4'">
<Alert show-icon style="margin-bottom: 16px;">
统计口径:订单按支付时间,退款/售后按成功时间,抽奖按开奖时间;流水和配送费已扣微信0.6%手续费,内容审核按每100次0.05元向上取整。
统计口径:订单按支付时间,退款/售后按成功时间,抽奖按开奖时间;流水总金额=商品价格+打包费+配送费,有效流水总金额=订单商品价格+打包费后扣微信0.6%手续费。
</Alert>
<div style="border-left: 5px solid rgb(70, 130, 180);height: 20px;line-height: 20px;font-size: 18px;font-weight: 700;margin-bottom: 20px;padding-left: 7px;">
订单数据
@ -67,7 +67,15 @@
</div>
<div>
<div style="font-weight:700;font-size:22px;">{{ statistics.orderCount }}</div>
<div>订单数量(单)</div>
<div>
订单数量(单)
<Tooltip transfer placement="top-start">
<Icon type="md-help-circle" size="14" />
<div slot="content" style="max-width: 320px; white-space: normal;">
订单记录数(待支付、已取消不计入)
</div>
</Tooltip>
</div>
</div>
</div>
</Col>
@ -78,7 +86,15 @@
</div>
<div>
<div style="font-weight:700;font-size:22px;">{{ statistics.grossFlowAmount }}</div>
<div>流水总金额(元)</div>
<div>
流水总金额(元)
<Tooltip transfer placement="top-start">
<Icon type="md-help-circle" size="14" />
<div slot="content" style="max-width: 320px; white-space: normal;">
商品价格+打包费+配送费
</div>
</Tooltip>
</div>
</div>
</div>
</Col>
@ -89,7 +105,15 @@
</div>
<div>
<div style="font-weight:700;font-size:22px;">{{ statistics.effectiveFlowAmount }}</div>
<div>有效流水总金额(元)</div>
<div>
有效流水总金额(元)
<Tooltip transfer placement="top-start">
<Icon type="md-help-circle" size="14" />
<div slot="content" style="max-width: 320px; white-space: normal;">
订单商品价格+打包费,再扣微信0.6%手续费
</div>
</Tooltip>
</div>
</div>
</div>
</Col>
@ -174,8 +198,17 @@
<Icon type="md-bookmarks" color="#fff" size="40" />
</div>
<div>
<div style="font-weight:700;font-size:22px;">{{ statistics.actualIncomeAmount }}</div>
<div>实际所得(元)</div>
<div v-if="statistics.actualIncomeAvailable" style="font-weight:700;font-size:22px;">{{ statistics.actualIncomeAmount }}</div>
<div v-else style="font-weight:700;font-size:22px;">--</div>
<div>
实际所得(元)
<Tooltip transfer placement="top-start">
<Icon type="md-help-circle" size="14" />
<div slot="content" style="max-width: 320px; white-space: normal;">
仅结束时间小于今天且范围不超过31天时计算
</div>
</Tooltip>
</div>
</div>
</div>
</Col>
@ -186,7 +219,15 @@
</div>
<div>
<div style="font-weight:700;font-size:22px;">{{ statistics.headquartersDeductionAmount }}</div>
<div>总部扣点(元)</div>
<div>
总部扣点(元)
<Tooltip transfer placement="top-start">
<Icon type="md-help-circle" size="14" />
<div slot="content" style="max-width: 320px; white-space: normal;">
有效流水总金额 * 0.01
</div>
</Tooltip>
</div>
</div>
</div>
</Col>
@ -482,6 +523,8 @@
couponDiscountAmount: "0.00",
customerAverageAmount: "0.00",
actualIncomeAmount: "0.00",
actualIncomeAvailable: false,
settledAmount: "0.00",
headquartersDeductionAmount: "0.00",
deliveryFeeAmount: "0.00",
smsCount: 0,
@ -753,7 +796,36 @@
this.searchForm.startDate = v && v[0] ? v[0] : ""
this.searchForm.endDate = v && v[1] ? v[1] : ""
},
parseDateString(value) {
return value ? new Date(value.replace(/-/g, "/")) : null
},
rangeDays(startDate, endDate) {
const start = this.parseDateString(startDate)
const end = this.parseDateString(endDate)
if (!start || !end) {
return 0
}
return Math.floor((end.getTime() - start.getTime()) / (24 * 60 * 60 * 1000)) + 1
},
validateStatisticsRange() {
if (!this.searchForm.startDate || !this.searchForm.endDate) {
return true
}
const days = this.rangeDays(this.searchForm.startDate, this.searchForm.endDate)
if (days < 1) {
this.$Message.warning("结束日期不能小于开始日期")
return false
}
if (days > 31) {
this.$Message.warning("日期范围最多选择31天")
return false
}
return true
},
getDataList() {
if (!this.validateStatisticsRange()) {
return
}
this.statisticsLoading = true
const params = Object.assign({}, this.searchForm, {
regionId: this.resolveRegionId()

42
src/views/app/shop/logisticsAddress.vue

@ -15,6 +15,14 @@
<Button type="text" @click="replyVisible = false">关闭</Button>
</div>
</Modal>
<Modal title="评价图" v-model="previewVisible" :mask-closable="true" :width="800">
<div class="comment-image-preview">
<img :src="previewImage" />
</div>
<div slot="footer">
<Button type="text" @click="previewVisible = false">关闭</Button>
</div>
</Modal>
</div>
</template>
@ -24,8 +32,6 @@
deleteComment,
getCommentList
} from "@/api/app";
import "viewerjs/dist/viewer.css";
import Viewer from "viewerjs";
export default {
name: "logisticsAddress",
components: {
@ -46,6 +52,8 @@
selectList: [], // 多选数据
replyVisible: false,
replyList: [],
previewVisible: false,
previewImage: "",
replyColumns: [
{
type: "index",
@ -112,7 +120,7 @@
key: "picture",
minWidth: 125,
render: (h, params) => {
if (!params.row.picture) return h("span", "-");
return h("img", {
attrs: {
src: params.row.picture,
@ -125,7 +133,8 @@
"object-fit": "contain",
},
on: {
click: () => {
click: (event) => {
event.stopPropagation();
this.showPic(params.row.picture);
},
},
@ -240,17 +249,12 @@
return content;
},
showPic(v) {
if(v == ''){
this.$Message.warning("没有评价图");return
if (!v) {
this.$Message.warning("没有评价图");
return;
}
let image = new Image();
image.src = v;
let viewer = new Viewer(image, {
hidden: function() {
viewer.destroy();
},
});
viewer.show();
this.previewImage = v;
this.previewVisible = true;
},
formatDateTime(isoString) {
if (!isoString) return ''
@ -290,4 +294,14 @@
</script>
<style lang="less">
//@import "@/styles/table-common.less";
.comment-image-preview {
text-align: center;
}
.comment-image-preview img {
max-width: 100%;
max-height: 70vh;
object-fit: contain;
}
</style>

60
src/views/sys/order-manage/logisticsAddress.vue

@ -32,16 +32,7 @@
<div>备注:<span class="blue-color">{{orderData.deliveryInfo.remark}}</span></div>
</div>
</div>
<div style="display: flex;" v-if="orderData.status ==7 || orderData.status ==11">
<div style="background: #fff;padding: 10px;border-radius: 10px;width: 100%;margin: 10;">
<div class="title">售后原因说明</div>
<textarea name="" id="" cols="30" rows="10" style="width: 100%;border: 1px solid #eee;" :value="getCurrentRefundRecord().reason || orderData.reason || ''" readonly></textarea>
<div style="display: flex;">
<div class='btn' @click="returnAmountAllow(getCurrentRefundRecord(),1)">同意退款</div>
<div class='btn' @click="returnAmountAllow(getCurrentRefundRecord(),0)">拒绝退款</div>
</div>
</div>
</div>
<div style="display: flex;">
<div class="waiceng" style="width: 69%;">
<div class="title">商品信息</div>
@ -125,7 +116,8 @@
<div class="refund-row" v-if="item.pictures != null && item.pictures != ''">
<span>图片</span>
<span>
<img class="refund-image" v-for="(picture,pictureIndex) in getPictureList(item.pictures)" :key="pictureIndex" :src="picture" />
<img class="refund-image" v-for="(picture,pictureIndex) in getPictureList(item.pictures)"
:key="pictureIndex" :src="picture" @click="showRefundPicture(picture)" />
</span>
</div>
<div class="refund-row">
@ -150,6 +142,24 @@
</div>
</div>
</div>
<div style="display: flex;" v-if="orderData.status ==7 || orderData.status ==11">
<div style="background: #fff;padding: 10px;border-radius: 10px;width: 100%;margin: 10;">
<div class="title">售后原因说明</div>
<textarea name="" id="" cols="30" rows="10" style="width: 100%;border: 1px solid #eee;" :value="getCurrentRefundRecord().reason || orderData.reason || ''" readonly></textarea>
<div style="display: flex;">
<div class='btn' @click="returnAmountAllow(getCurrentRefundRecord(),1)">同意退款</div>
<div class='btn' @click="returnAmountAllow(getCurrentRefundRecord(),0)">拒绝退款</div>
</div>
</div>
</div>
<Modal title="退款/售后图片" v-model="previewVisible" :mask-closable="true" :width="800">
<div class="refund-image-preview">
<img :src="previewImage" />
</div>
<div slot="footer">
<Button type="text" @click="previewVisible = false">关闭</Button>
</div>
</Modal>
</div>
</template>
@ -174,7 +184,9 @@
returnFormData:{},
refundReason: "",
refundLoading: false,
refundActionData: null
refundActionData: null,
previewVisible: false,
previewImage: ""
};
},
filters: {
@ -351,6 +363,14 @@
}
return String(pictures).split(",").map((item) => item.trim()).filter((item) => item !== "");
},
showRefundPicture(picture) {
if (!picture) {
this.$Message.warning("没有退款/售后图片");
return;
}
this.previewImage = picture;
this.previewVisible = true;
},
getRefundTypeText(item) {
if (!item) return "";
if (item.refundType == 1) return "退商品";
@ -555,6 +575,22 @@
margin-right: 8px;
border-radius: 4px;
object-fit: cover;
cursor: pointer;
}
.refund-image-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
max-height: 70vh;
overflow: hidden;
}
.refund-image-preview img {
max-width: 100%;
max-height: 70vh;
object-fit: contain;
}
.text {

45
src/views/sys/order-manage/orderManage.vue

@ -81,6 +81,21 @@
margin-top: 12px;
gap: 12px;
}
.refund-type-status-wrap {
display: inline-block;
max-width: 100%;
white-space: normal;
word-break: break-all;
line-height: 20px;
}
.search-date-row {
display: inline-flex;
align-items: flex-start;
white-space: nowrap;
vertical-align: top;
}
</style>
<template>
<div class="search">
@ -105,6 +120,16 @@
<Input type="text" v-model="searchForm.receiverPhone" clearable placeholder="请输入收货人手机号"
style="width: 200px" />
</FormItem>
<div class="search-date-row">
<FormItem label="开始日期" prop="startDate">
<DatePicker type="date" v-model="searchForm.startDate" clearable placeholder="请选择开始日期"
format="yyyy-MM-dd" @on-change="searchForm.startDate = $event" style="width: 200px" />
</FormItem>
<FormItem label="结束日期" prop="endDate">
<DatePicker type="date" v-model="searchForm.endDate" clearable placeholder="请选择结束日期"
format="yyyy-MM-dd" @on-change="searchForm.endDate = $event" style="width: 200px" />
</FormItem>
</div>
<FormItem style="margin-left: -35px" class="br">
<Button @click="handleSearch" type="primary" icon="ios-search">搜索</Button>
<Button @click="handleReset">重置</Button>
@ -481,6 +506,8 @@
searchStatus:10,
shopName: "",
receiverPhone: "",
startDate: "",
endDate: "",
mobile:'',
userId: ""
},
@ -683,7 +710,9 @@
width: 210,
align: "center",
render: (h, params) => {
return h("span", this.getRefundTypeStatusText(params.row.refundTypeStatus));
return h("span", {
class: "refund-type-status-wrap"
}, this.getRefundTypeStatusText(params.row.refundTypeStatus));
}
},
{
@ -722,7 +751,7 @@
render: (h, params) => {
const order = params.row.mallOrder || {};
const actionList = [
h("a", {
/* h("a", {
style: {
marginRight: "8px"
},
@ -731,7 +760,7 @@
this.showRefundGoods(params.row);
},
},
}, order.otherOrder == 1 ? "快递/跑腿单" : "查看商品"),
}, order.otherOrder == 1 ? "快递/跑腿单" : "查看商品"), */
];
if (order && order.orderType != 1) {
actionList.push(
@ -757,8 +786,8 @@
this.showOrderDetail(params.row);
},
},
}, "查看详情"),
h("a", {
}, "查看详情")
/* h("a", {
style: {
marginRight: "8px"
},
@ -774,7 +803,7 @@
this.returnAmountAllow(params.row, 1);
},
},
}, this.isSubmittingRefund(params.row) ? "处理中" : "同意"),
}, this.isSubmittingRefund(params.row) ? "处理中" : "同意"), */
);
return h("div", actionList);
},
@ -1144,6 +1173,8 @@
this.searchForm.id = "";
this.searchForm.shopName = "";
this.searchForm.receiverPhone = "";
this.searchForm.startDate = "";
this.searchForm.endDate = "";
this.searchForm.pageNum = 1;
this.searchForm.pageSize = 10;
this.returnForm.pageNum = 1;
@ -1190,7 +1221,7 @@
getRefundTypeStatusText(status) {
if (status == 1) return "商家原因";
if (status == 2) return "配送原因";
if (status == 3) return "商家/配送都有原因(需要退配送费)";
if (status == 3) return "商家/配送都有原因";
if (status == 4) return "平台退款";
return "";
},

Loading…
Cancel
Save