Merge remote-tracking branch 'origin/master'

This commit is contained in:
zjf committed 2026-06-22 17:35:54 +08:00
commit f2d8863ec2
19 files changed
+630 -241

No files matched your search

+16 -4
View File
@@ -1,4 +1,4 @@
import request, {CommonHeaderEnum} from "/@/utils/request"
import request, { CommonHeaderEnum } from "/@/utils/request"
/**
* 同步用户食堂信息
@@ -17,6 +17,18 @@ export const list = () => {
})
}
export const pageSelect = (query: any) => {
return request({
url: "/admin/user/select",
method: "post",
params: {
current: query.current,
size: query.size,
},
data: query,
})
}
export const pageList = (query: any) => {
return request({
url: "/admin/user/page",
@@ -41,9 +53,9 @@ export const getObj = (id: String) => {
return request({
url: "/admin/user/details/" + id,
method: "get",
headers: {
[CommonHeaderEnum.ENC_FLAG]: true,
},
headers: {
[CommonHeaderEnum.ENC_FLAG]: true,
},
})
}
+7
View File
@@ -11,6 +11,13 @@ export function offlinePay(data: any) {
})
}
export function adminRserveCancelOrder(orderId: string) {
return request({
url: "/hard/hard-order/rserve-cancel-order/" + orderId,
method: "put",
})
}
export function pageJoin(query: any) {
return request({
url: "/hard/hard-order/page-join",
+15
View File
@@ -56,6 +56,21 @@ export function canteenProudctQuery(query: any) {
})
}
/**
* 菜品出售商户统计
*/
export function canteenProudctMchQuery(query: any) {
return request({
url: "/hard/hard-canteen-total-log/proudct-mch-query",
method: "post",
params: {
current: query.current,
size: query.size,
},
data: query,
})
}
export function pageListDept(query?: any) {
return request({
url: "/hard/hard-total/page-list-dept",
+92 -16
View File
@@ -1,7 +1,7 @@
<template>
<el-select style="width: 100%" v-model="selectId" :placeholder="props.placeholder" clearable remote-show-suffix
filterable remote :disabled="props.disabled" :remote-method="remoteSearch" :loading="state.loading"
@visible-change="(e: boolean) => e && getOptions()" @clear="clear" :multiple="props.multiple">
@visible-change="handleVisibleChange" @clear="clear" :multiple="props.multiple">
<el-option v-for="v in state.options" :key="v.id" :label="`${v.name}`" :value="v.id">
<div class="flex items-center">
<div class="color-primary">{{ v.name }}</div>
@@ -57,27 +57,75 @@ const state = reactive<any>({
options: [],
})
async function getOptions() {
try {
let parentDeptId = null
if (props.deptMch) {
const currentUser = userInfos.value.user
if (userInfos.value.roleCodes.includes("DEPT_ADMIN")) {
parentDeptId = currentUser?.deptId
}
let requestSeq = 0
let lastRequestKey = ""
let inflightKey = ""
let inflightPromise: Promise<void> | null = null
let refreshScheduled = false
function stableStringify(value: any): string {
if (value == null) return ""
if (typeof value !== "object") return String(value)
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`
const keys = Object.keys(value).sort()
return `{${keys.map((k) => `${k}:${stableStringify(value[k])}`).join(",")}}`
}
function buildRequestKey(parentDeptId: any) {
return [
parentDeptId ?? "",
queryForm.name ?? "",
queryForm.current ?? "",
queryForm.size ?? "",
stableStringify(props.apiParams),
props.deptMch ? "1" : "0",
].join("|")
}
async function getOptions(force = false) {
const seq = ++requestSeq
let parentDeptId = null
if (props.deptMch) {
const currentUser = userInfos.value.user
if (userInfos.value.roleCodes.includes("DEPT_ADMIN")) {
parentDeptId = currentUser?.deptId
}
state.loading = true
}
const requestKey = buildRequestKey(parentDeptId)
if (!force) {
if (inflightPromise && inflightKey === requestKey) return inflightPromise
if (lastRequestKey === requestKey) return
}
state.loading = true
inflightKey = requestKey
try {
const _pa = {
parentDeptId,
...queryForm,
...props.apiParams,
}
const { data } = await mchApi.select(_pa)
state.options = data.records ?? []
if (props.autoFirst && !selectId.value && state.options.length !== 0) {
selectId.value = state.options[0].id
}
inflightPromise = mchApi
.select(_pa)
.then(({ data }) => {
if (seq !== requestSeq) return
state.options = data.records ?? []
lastRequestKey = requestKey
const isEmptySelection = Array.isArray(selectId.value) ? selectId.value.length === 0 : !selectId.value
const isSelectionInOptions = Array.isArray(selectId.value)
? selectId.value.every((id: any) => state.options.some((item: any) => item?.id === id))
: state.options.some((item: any) => item?.id === selectId.value)
if (props.autoFirst && (isEmptySelection || !isSelectionInOptions) && state.options.length !== 0) {
selectId.value = state.options[0].id
}
})
return inflightPromise
} finally {
if (inflightKey === requestKey) {
inflightPromise = null
}
setTimeout(() => (state.loading = false), 500)
}
}
@@ -90,10 +138,38 @@ function remoteSearch(params: string) {
}
function clear() {
selectId.value = null
selectId.value = props.multiple ? [] : null
getOptions()
}
function handleVisibleChange(visible: boolean) {
if (!visible) return
if (!state.options.length) {
getOptions()
}
}
function scheduleRefreshOptions() {
if (refreshScheduled) return
refreshScheduled = true
Promise.resolve().then(() => {
refreshScheduled = false
lastRequestKey = ""
getOptions()
})
}
watch(
() => [stableStringify(props.apiParams), props.deptMch],
(newVal, oldVal) => {
if (oldVal && newVal[0] === oldVal[0] && newVal[1] === oldVal[1]) return
if (props.autoFirst) {
scheduleRefreshOptions()
}
},
{ flush: "post" },
)
onMounted(() => {
props.autoFirst && getOptions()
})
+4 -14
View File
@@ -1,17 +1,7 @@
<template>
<el-select
style="width: 100%"
v-model="state.selectId"
:placeholder="props.placeholder"
clearable
remote-show-suffix
filterable
remote
:remote-method="remoteSearch"
:loading="state.loading"
@visible-change="(e: boolean) => e && getOptions()"
@change="change"
>
<el-select style="width: 100%" v-model="state.selectId" :placeholder="props.placeholder" clearable
remote-show-suffix filterable remote :remote-method="remoteSearch" :loading="state.loading"
@visible-change="(e: boolean) => e && getOptions()" @change="change">
<el-option v-for="v in state.options" :key="v.userId" :label="`${v.name}-${v.phone}`" :value="v.userId">
<div class="flex items-center">
<div class="color-primary">{{ v.name }}</div>
@@ -64,7 +54,7 @@ async function getOptions() {
userId: selectId.value,
...queryForm,
}
const { data } = await userApi.pageList(_pa)
const { data } = await userApi.pageSelect(_pa)
state.options = data.records ?? []
} finally {
setTimeout(() => (state.loading = false), 500)
+116 -116
View File
@@ -10,45 +10,45 @@
* @returns 返回拼接后的时间字符串
*/
export function formatDate(date: Date, format: string): string {
let we = date.getDay(); // 星期
let z = getWeek(date); // 周
let qut = Math.floor((date.getMonth() + 3) / 3).toString(); // 季度
let we = date.getDay() // 星期
let z = getWeek(date) // 周
let qut = Math.floor((date.getMonth() + 3) / 3).toString() // 季度
const opt: { [key: string]: string } = {
'Y+': date.getFullYear().toString(), // 年
'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1)
'd+': date.getDate().toString(), // 日
'H+': date.getHours().toString(), // 时
'M+': date.getMinutes().toString(), // 分
'S+': date.getSeconds().toString(), // 秒
'q+': qut, // 季度
};
"Y+": date.getFullYear().toString(), // 年
"m+": (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1)
"d+": date.getDate().toString(), // 日
"H+": date.getHours().toString(), // 时
"M+": date.getMinutes().toString(), // 分
"S+": date.getSeconds().toString(), // 秒
"q+": qut, // 季度
}
// 中文数字 (星期)
const week: { [key: string]: string } = {
'0': '日',
'1': '一',
'2': '二',
'3': '三',
'4': '四',
'5': '五',
'6': '六',
};
"0": "日",
"1": "一",
"2": "二",
"3": "三",
"4": "四",
"5": "五",
"6": "六",
}
// 中文数字(季度)
const quarter: { [key: string]: string } = {
'1': '一',
'2': '二',
'3': '三',
'4': '四',
};
if (/(W+)/.test(format))
format = format.replace(RegExp.$1, RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? '星期' + week[we] : '周' + week[we]) : week[we]);
if (/(Q+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 4 ? '第' + quarter[qut] + '季度' : quarter[qut]);
if (/(Z+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 3 ? '第' + z + '周' : z + '');
for (let k in opt) {
let r = new RegExp('(' + k + ')').exec(format);
// 若输入的长度不为1,则前面补零
if (r) format = format.replace(r[1], RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, '0'));
"1": "一",
"2": "二",
"3": "三",
"4": "四",
}
return format;
if (/(W+)/.test(format))
format = format.replace(RegExp.$1, RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? "星期" + week[we] : "周" + week[we]) : week[we])
if (/(Q+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 4 ? "第" + quarter[qut] + "季度" : quarter[qut])
if (/(Z+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 3 ? "第" + z + "周" : z + "")
for (let k in opt) {
let r = new RegExp("(" + k + ")").exec(format)
// 若输入的长度不为1,则前面补零
if (r) format = format.replace(r[1], RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, "0"))
}
return format
}
/**
@@ -57,19 +57,19 @@ export function formatDate(date: Date, format: string): string {
* @returns 返回第几周数字值
*/
export function getWeek(dateTime: Date): number {
let temptTime = new Date(dateTime.getTime());
let temptTime = new Date(dateTime.getTime())
// 周几
let weekday = temptTime.getDay() || 7;
let weekday = temptTime.getDay() || 7
// 周1+5天=周六
temptTime.setDate(temptTime.getDate() - weekday + 1 + 5);
let firstDay = new Date(temptTime.getFullYear(), 0, 1);
let dayOfWeek = firstDay.getDay();
let spendDay = 1;
if (dayOfWeek != 0) spendDay = 7 - dayOfWeek + 1;
firstDay = new Date(temptTime.getFullYear(), 0, 1 + spendDay);
let d = Math.ceil((temptTime.valueOf() - firstDay.valueOf()) / 86400000);
let result = Math.ceil(d / 7);
return result;
temptTime.setDate(temptTime.getDate() - weekday + 1 + 5)
let firstDay = new Date(temptTime.getFullYear(), 0, 1)
let dayOfWeek = firstDay.getDay()
let spendDay = 1
if (dayOfWeek != 0) spendDay = 7 - dayOfWeek + 1
firstDay = new Date(temptTime.getFullYear(), 0, 1 + spendDay)
let d = Math.ceil((temptTime.valueOf() - firstDay.valueOf()) / 86400000)
let result = Math.ceil(d / 7)
return result
}
/**
@@ -83,38 +83,38 @@ export function getWeek(dateTime: Date): number {
* @description param 3天: 60 * 60* 24 * 1000 * 3
* @returns 返回拼接后的时间字符串
*/
export function formatPast(param: string | Date, format: string = 'YYYY-mm-dd'): string {
export function formatPast(param: string | Date, format: string = "YYYY-mm-dd"): string {
// 传入格式处理、存储转换值
let t: any, s: number;
let t: any, s: number
// 获取js 时间戳
let time: number = new Date().getTime();
let time: number = new Date().getTime()
// 是否是对象
typeof param === 'string' || 'object' ? (t = new Date(param).getTime()) : (t = param);
typeof param === "string" || "object" ? (t = new Date(param).getTime()) : (t = param)
// 当前时间戳 - 传入时间戳
time = Number.parseInt(`${time - t}`);
time = Number.parseInt(`${time - t}`)
if (time < 10000) {
// 10秒内
return '刚刚';
return "刚刚"
} else if (time < 60000 && time >= 10000) {
// 超过10秒少于1分钟内
s = Math.floor(time / 1000);
return `${s}秒前`;
s = Math.floor(time / 1000)
return `${s}秒前`
} else if (time < 3600000 && time >= 60000) {
// 超过1分钟少于1小时
s = Math.floor(time / 60000);
return `${s}分钟前`;
s = Math.floor(time / 60000)
return `${s}分钟前`
} else if (time < 86400000 && time >= 3600000) {
// 超过1小时少于24小时
s = Math.floor(time / 3600000);
return `${s}小时前`;
s = Math.floor(time / 3600000)
return `${s}小时前`
} else if (time < 259200000 && time >= 86400000) {
// 超过1天少于3天内
s = Math.floor(time / 86400000);
return `${s}天前`;
s = Math.floor(time / 86400000)
return `${s}天前`
} else {
// 超过3天
let date = typeof param === 'string' || 'object' ? new Date(param) : param;
return formatDate(date, format);
let date = typeof param === "string" || "object" ? new Date(param) : param
return formatDate(date, format)
}
}
@@ -125,39 +125,39 @@ export function formatPast(param: string | Date, format: string = 'YYYY-mm-dd'):
* @returns 返回拼接后的时间字符串
*/
export function formatAxis(param: Date): string {
let hour: number = new Date(param).getHours();
if (hour < 6) return '凌晨好';
else if (hour < 9) return '早上好';
else if (hour < 12) return '上午好';
else if (hour < 14) return '中午好';
else if (hour < 17) return '下午好';
else if (hour < 19) return '傍晚好';
else if (hour < 22) return '晚上好';
else return '夜里好';
let hour: number = new Date(param).getHours()
if (hour < 6) return "凌晨好"
else if (hour < 9) return "早上好"
else if (hour < 12) return "上午好"
else if (hour < 14) return "中午好"
else if (hour < 17) return "下午好"
else if (hour < 19) return "傍晚好"
else if (hour < 22) return "晚上好"
else return "夜里好"
}
// 日期格式化
export function parseTime(time, pattern) {
if (arguments.length === 0 || !time) {
return null;
return null
}
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}';
let date;
if (typeof time === 'object') {
date = time;
const format = pattern || "{y}-{m}-{d} {h}:{i}:{s}"
let date
if (typeof time === "object") {
date = time
} else {
if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
time = parseInt(time);
} else if (typeof time === 'string') {
if (typeof time === "string" && /^[0-9]+$/.test(time)) {
time = parseInt(time)
} else if (typeof time === "string") {
time = time
.replace(new RegExp(/-/gm), '/')
.replace('T', ' ')
.replace(new RegExp(/\.[\d]{3}/gm), '');
.replace(new RegExp(/-/gm), "/")
.replace("T", " ")
.replace(new RegExp(/\.[\d]{3}/gm), "")
}
if (typeof time === 'number' && time.toString().length === 10) {
time = time * 1000;
if (typeof time === "number" && time.toString().length === 10) {
time = time * 1000
}
date = new Date(time);
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
@@ -167,43 +167,43 @@ export function parseTime(time, pattern) {
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay(),
};
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key];
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') {
return ['日', '一', '二', '三', '四', '五', '六'][value];
if (key === "a") {
return ["日", "一", "二", "三", "四", "五", "六"][value]
}
if (result.length > 0 && value < 10) {
value = '0' + value;
value = "0" + value
}
return value || 0;
});
return time_str;
return value || 0
})
return time_str
}
// 日期格式化
export function parseDate(time, pattern) {
if (arguments.length === 0 || !time) {
return null;
return null
}
const format = pattern || '{y}-{m}-{d}';
let date;
if (typeof time === 'object') {
date = time;
const format = pattern || "{y}-{m}-{d}"
let date
if (typeof time === "object") {
date = time
} else {
if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
time = parseInt(time);
} else if (typeof time === 'string') {
if (typeof time === "string" && /^[0-9]+$/.test(time)) {
time = parseInt(time)
} else if (typeof time === "string") {
time = time
.replace(new RegExp(/-/gm), '/')
.replace('T', ' ')
.replace(new RegExp(/\.[\d]{3}/gm), '');
.replace(new RegExp(/-/gm), "/")
.replace("T", " ")
.replace(new RegExp(/\.[\d]{3}/gm), "")
}
if (typeof time === 'number' && time.toString().length === 10) {
time = time * 1000;
if (typeof time === "number" && time.toString().length === 10) {
time = time * 1000
}
date = new Date(time);
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
@@ -213,23 +213,23 @@ export function parseDate(time, pattern) {
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay(),
};
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key];
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') {
return ['日', '一', '二', '三', '四', '五', '六'][value];
if (key === "a") {
return ["日", "一", "二", "三", "四", "五", "六"][value]
}
if (result.length > 0 && value < 10) {
value = '0' + value;
value = "0" + value
}
return value || 0;
});
return time_str;
return value || 0
})
return time_str
}
export const dateTimeStr: string = 'YYYY-MM-DD HH:mm:ss';
export const dateTimeStr: string = "YYYY-MM-DD HH:mm:ss"
export const dateStr: string = 'YYYY-MM-DD';
export const dateStr: string = "YYYY-MM-DD"
export const timeStr: string = 'HH:mm:ss';
export const timeStr: string = "HH:mm:ss"
@@ -1,6 +1,16 @@
<template>
<el-dialog title="菜品统计" width="70%" v-model="visible" :close-on-click-modal="false" draggable>
<el-form ref="queryRef" :inline="true" :model="state.queryForm" @keyup.enter="getDataList"> </el-form>
<el-form v-if="false" ref="queryRef" :inline="true" :model="state.queryForm" @keyup.enter="getDataList">
<el-form-item style="width: 250px" label="用餐时间段" prop="hourType">
<dict-select v-model="state.queryForm.hourType" dict-code="daily_menu" />
</el-form-item>
<el-form-item>
<el-button icon="Search" type="primary" @click="doSearch">
{{ $t("common.queryBtn") }}
</el-button>
<el-button icon="Refresh" @click="resetQuery">{{ $t("common.resetBtn") }}</el-button>
</el-form-item>
</el-form>
<el-table height="450px" :data="state.dataList" v-loading="state.loading" border
:cell-style="tableStyle.cellStyle" :header-cell-style="tableStyle.headerCellStyle"
:summary-method="getSummaries" :show-summary="true" @selection-change="selectionChangHandle"
@@ -111,6 +121,7 @@ const { daily_menu, hard_pay_type, hard_order_type, hard_order_status, } = useDi
// 定义变量内容
const visible = ref(false)
const queryRef = ref<any>()
// 多选变量
const selectObjs = ref([]) as any
@@ -131,6 +142,21 @@ const selectionChangHandle = (objs: { id: string }[]) => {
multiple.value = !objs.length
}
// 搜索
const doSearch = () => {
getDataList()
}
// 清空搜索条件
const resetQuery = () => {
// 清空搜索条件
queryRef.value?.resetFields()
// 清空多选
selectObjs.value = []
getDataList()
}
const openDialog = (row: any, query: any) => {
console.log("row:", row);
console.log("query:", query);
@@ -0,0 +1,129 @@
<template>
<el-dialog title="菜品统计" width="70%" v-model="visible" :close-on-click-modal="false" draggable>
<el-form ref="queryRef" :inline="true" :model="state.queryForm" @keyup.enter="getDataList">
<el-form-item style="width: 250px" label="商品名称" prop="productName">
<el-input v-model="state.queryForm.productName" />
</el-form-item>
<el-form-item>
<el-button icon="Search" type="primary" @click="doSearch">
{{ $t("common.queryBtn") }}
</el-button>
<el-button icon="Refresh" @click="resetQuery">{{ $t("common.resetBtn") }}</el-button>
</el-form-item>
</el-form>
<el-table :data="state.dataList" v-loading="state.loading" border :cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle" :summary-method="getSummaries" :show-summary="true"
@selection-change="selectionChangHandle" @sort-change="sortChangeHandle">
<el-table-column label="用餐时间" align="center" prop="createTime">
<template #default="{ row }">
<span>{{ parseTime(row.createTime, "{y}-{m}-{d}") }}</span>
</template>
</el-table-column>
<el-table-column label="商品" align="center" prop="productName" />
<el-table-column label="商品图片" align="center">
<template #default="{ row }">
<preview-image v-if="row.productPic" :src="row.productPic" />
</template>
</el-table-column>
<el-table-column label="单价" align="center">
<template #default="{ row }">
<span>{{ displayPrice(row.productPrice) }}</span>
</template>
</el-table-column>
<el-table-column label="总数量" prop="count" align="center">
<template #default="{ row }">
<span>{{ row.count }}份</span>
</template>
</el-table-column>
<el-table-column label="总价" prop="totalPrice" align="center">
<template #default="{ row }">
<span>{{ displayPrice(row.totalPrice) }}</span>
</template>
</el-table-column>
</el-table>
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
</el-dialog>
</template>
<script setup lang="ts" name="product-total-table">
import { BasicTableProps, useTable } from "/@/hooks/table"
import { displayPrice } from "/@/utils/formatMoney"
import { parseTime } from "/@/utils/formatTime"
import * as hardTotalApi from "/@/api/hard/hard-total"
// 定义变量内容
const visible = ref(false)
const queryRef = ref<any>()
// 多选变量
const selectObjs = ref([]) as any
const multiple = ref(true)
const state: BasicTableProps = reactive<BasicTableProps>({
createdIsNeed: false,
queryForm: {},
pageList: hardTotalApi.canteenProudctQuery,
})
// table hook
const { getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle } = useTable(state)
const getSummaries = (param: any) => {
const { columns, data } = param
const totalColumns = ["count", "totalPrice"]
const totalRow = Array.from({ length: columns.length })
totalRow[0] = "合计"
columns.forEach((i: any, index: number) => {
if (totalColumns.includes(i.property)) {
const total = data
.filter((v: any) => v[i.property] != null)
.map((v: any) => Number(v[i.property]))
.reduce((pren: number, next: number) => pren + next, 0)
if (i.property.includes("Price")) {
totalRow[index] = displayPrice(total)
} else {
totalRow[index] = total
}
}
})
return totalRow
}
// 搜索
const doSearch = () => {
getDataList()
}
// 清空搜索条件
const resetQuery = () => {
// 清空搜索条件
queryRef.value?.resetFields()
// 清空多选
selectObjs.value = []
getDataList()
}
// 多选事件
const selectionChangHandle = (objs: { id: string }[]) => {
selectObjs.value = objs.map(({ id }) => id)
multiple.value = !objs.length
}
const openDialog = (mchId: any) => {
console.log("mchId:", mchId);
visible.value = true
state.queryForm.merchantId = mchId
getDataList()
}
const updateQueryParams = (params: any) => {
if (!params) return
state.queryForm.merchantId = params.merchantId ?? null
getDataList()
}
// 暴露变量
defineExpose({
openDialog,
updateQueryParams,
})
</script>
@@ -74,6 +74,10 @@
<el-button icon="Tickets" text type="primary" @click="doOpenDialog(row)">
购买人员
</el-button>
<el-button icon="Tickets" text type="primary"
@click="productTotalTableRef.openDialog(row.mchId)">
购买统计
</el-button>
</template>
</el-table-column>
</el-table>
@@ -113,12 +117,17 @@
<el-button icon="Tickets" text type="primary" @click="doOpenDialog(row)">
购买人员
</el-button>
<el-button icon="Tickets" text type="primary"
@click="productTotalTableRef.openDialog(row.mchId)">
购买统计
</el-button>
</template>
</el-table-column>
</el-table>
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle"
v-bind="state.pagination" />
<dept-person-total-table ref="deptPersonTotalTableRef" />
<product-total-table ref="productTotalTableRef" />
</div>
</div>
</template>
@@ -129,15 +138,18 @@ import { BasicTableProps, useTable } from "/@/hooks/table"
import { displayPrice } from "/@/utils/formatMoney"
import { useDict } from "/@/hooks/dict"
import { getDicts } from "/@/api/admin/dict"
import { parseTime } from "/@/utils/formatTime"
import * as hardTotalApi from "/@/api/hard/hard-total"
const DeptPersonTotalTable = defineAsyncComponent(() => import("./components/dept-person-total-table.vue"))
const ProductTotalTable = defineAsyncComponent(() => import("./components/product-total-table.vue"))
// 定义查询字典
const { daily_menu } = useDict("daily_menu")
const deptPersonTotalTableRef = ref()
const productTotalTableRef = ref()
// 搜索变量
const queryRef = ref()
@@ -1,9 +1,20 @@
<template>
<el-dialog title="菜品统计" width="70%" v-model="visible" :close-on-click-modal="false" draggable>
<el-form ref="queryRef" :inline="true" :model="state.queryForm" @keyup.enter="getDataList"> </el-form>
<el-form ref="queryRef" :inline="true" :model="state.queryForm" @keyup.enter="getDataList">
<el-form-item style="width: 250px" label="商品名称" prop="productName">
<el-input v-model="state.queryForm.productName" />
</el-form-item>
<el-form-item>
<el-button icon="Search" type="primary" @click="doSearch">
{{ $t("common.queryBtn") }}
</el-button>
<el-button icon="Refresh" @click="resetQuery">{{ $t("common.resetBtn") }}</el-button>
</el-form-item>
</el-form>
<el-table :data="state.dataList" v-loading="state.loading" border :cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle" :summary-method="getSummaries" :show-summary="true"
@selection-change="selectionChangHandle" @sort-change="sortChangeHandle">
<el-table-column label="商户" align="center" prop="merchantName" />
<el-table-column label="用餐时间" align="center" prop="createTime">
<template #default="{ row }">
<span>{{ parseTime(row.createTime, "{y}-{m}-{d}") }}</span>
@@ -43,6 +54,7 @@ import { parseTime } from "/@/utils/formatTime"
import * as hardTotalApi from "/@/api/hard/hard-total"
// 定义变量内容
const visible = ref(false)
const queryRef = ref<any>()
// 多选变量
const selectObjs = ref([]) as any
const multiple = ref(true)
@@ -50,7 +62,7 @@ const multiple = ref(true)
const state: BasicTableProps = reactive<BasicTableProps>({
createdIsNeed: false,
queryForm: {},
pageList: hardTotalApi.canteenProudctQuery,
pageList: hardTotalApi.canteenProudctMchQuery,
})
// table hook
@@ -77,6 +89,20 @@ const getSummaries = (param: any) => {
return totalRow
}
// 搜索
const doSearch = () => {
getDataList()
}
// 清空搜索条件
const resetQuery = () => {
// 清空搜索条件
queryRef.value?.resetFields()
// 清空多选
selectObjs.value = []
getDataList()
}
// 多选事件
const selectionChangHandle = (objs: { id: string }[]) => {
selectObjs.value = objs.map(({ id }) => id)
@@ -15,8 +15,8 @@
:apiParams="{ parentIsNull: false, types: '1' }" />
</el-form-item>
<el-form-item style="width: 450px" label="创建时间" prop="createTimeList">
<el-date-picker v-model="state.queryForm.createTimeList" value-format="YYYY-MM-DD"
type="daterange" />
<el-date-picker v-model="state.queryForm.createTimeList"
value-format="YYYY-MM-DD HH:mm:ss" type="daterange" />
</el-form-item>
<el-form-item>
<el-button icon="Search" type="primary" @click="doSearch">{{ $t("common.queryBtn")
+2 -2
View File
@@ -69,11 +69,11 @@
@click="formDialogRef.openDialog(row.id)">
编辑
</el-button>
<el-button v-if="!['10', '11'].includes(row.type)" icon="TrendCharts" link type="primary"
<el-button v-if="!['9', '10', '11'].includes(row.type)" icon="TrendCharts" link type="primary"
v-auth="'hard_hardManager_edit'" @click="mqformDialogRef.openDialog(row.id)">
mq设置
</el-button>
<el-button v-if="!['10', '11'].includes(row.type)" icon="SetUp" link type="primary"
<el-button v-if="!['9', '10', '11'].includes(row.type)" icon="SetUp" link type="primary"
v-auth="'hard_hardManager_edit'" @click="faceFormDialogRef.openDialog(row.id)">
人脸sdk设置
</el-button>
+72 -34
View File
@@ -68,7 +68,7 @@
</div>
<el-table :data="state.dataList" v-loading="state.loading" border :cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle" @selection-change="selectionChangHandle"
@sort-change="sortChangeHandle" :show-summary="false" :summary-method="getSummaries"
@sort-change="sortChangeHandle" :show-summary="true" :summary-method="getSummaries"
row-key="id">
<el-table-column type="selection" width="60" align="center" />
<el-table-column type="index" label="#" width="50" />
@@ -221,39 +221,61 @@
</div>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="200">
<el-table-column fixed="right" label="操作" width="220">
<template #default="{ row }">
<div class="grid grid-cols-2 justify-evenly">
<el-button v-if="row.orderType !== '5'" icon="Printer" text type="primary"
@click="print(row)" v-auth="'hard_hardInvoice_admin_print'">
打印发票
</el-button>
<el-button v-if="row.orderType !== '5'" icon="ShoppingCartFull" text type="primary"
@click="shopCarDialogRef.openDialog(row.id)">
购物车
</el-button>
<el-button icon="Money" link type="primary" v-if="row.userId && row.userId != '-1'"
@click="transactionListRef.openDialog(row.walletId)">
钱包明细
</el-button>
<el-button icon="InfoFilled" link type="primary"
v-auth="'wallet_walletTransaction_update_balance'" @click="doRefund(row)">
订单详情
</el-button>
<el-button v-if="false" icon="edit-pen" text type="primary"
v-auth="'hard_hardwareOrder_edit'" @click="formDialogRef.openDialog(row.id)">
编辑
</el-button>
<el-button
v-if="row.orderType !== '5' && !['5', '6', '8'].includes(row.payType) && row.refundPrice != row.payPrice"
icon="stamp" text type="primary" v-auth="'hard_hardAfterSale_adminRefund'"
@click="adminRefundDailogRef.openDialog(row.id)">
退款
</el-button>
<el-button v-if="false" icon="delete" text type="primary"
v-auth="'hard_hardwareOrder_del'" @click="handleDelete([row.id])">
删除
</el-button>
<div class="flex flex-wrap items-start -mx-1">
<div v-if="row.orderType !== '5'"
class="w-1/2 h-[30px] flex justify-start items-center"
v-auth="'hard_hardInvoice_admin_print'">
<el-button icon="Printer" text type="primary" style="margin: 0"
@click="print(row)">
打印发票
</el-button>
</div>
<div v-if="row.orderType !== '5'"
class="w-1/2 h-[30px] flex justify-start items-center">
<el-button icon="ShoppingCartFull" text type="primary" style="margin: 0"
@click="shopCarDialogRef.openDialog(row.id)">
购物车
</el-button>
</div>
<div v-if="row.userId && row.userId != '-1'"
class="w-1/2 h-[30px] flex justify-start items-center">
<el-button icon="Money" link type="primary" style="margin: 0"
@click="transactionListRef.openDialog(row.walletId)">
钱包明细
</el-button>
</div>
<div class="w-1/2 h-[30px] flex justify-start items-center"
v-auth="'wallet_walletTransaction_update_balance'">
<el-button icon="InfoFilled" link type="primary" style="margin: 0"
@click="doRefund(row)">
订单详情
</el-button>
</div>
<div v-if="row.orderType !== '5' && !['5', '6', '8'].includes(row.payType) && row.refundPrice != row.payPrice"
class="w-1/2 h-[30px] flex justify-start items-center"
v-auth="'hard_hardAfterSale_adminRefund'">
<el-button icon="stamp" text type="primary" style="margin: 0"
@click="adminRefundDailogRef.openDialog(row.id)">
退款
</el-button>
</div>
<div v-if="row.orderStatus == '5' && ['2'].includes(row.orderType)"
class="w-1/2 h-[30px] flex justify-start items-center"
v-auth="'hard_hardAfterSale_cancel'">
<el-button icon="Promotion" text type="primary" style="margin: 0"
@click="doCancel(row)">
手动核销
</el-button>
</div>
<div v-if="false" class="w-1/2 h-[30px] flex justify-start items-center"
v-auth="'hard_hardwareOrder_del'">
<el-button icon="delete" text type="primary" style="margin: 0"
@click="handleDelete([row.id])">
删除
</el-button>
</div>
</div>
</template>
</el-table-column>
@@ -385,6 +407,22 @@ const selectionChangHandle = (objs: { id: string }[]) => {
multiple.value = !objs.length
}
// 手动核销
const doCancel = async (row: any) => {
try {
await useMessageBox().confirm(`是否手动核销用户'${row.username}',总价为${displayPrice(row.totalPrice)}的预约订单?`)
} catch {
return
}
try {
await hardOrder.adminRserveCancelOrder(row.id)
getDataList()
useMessage().success("核销成功")
} catch (err: any) {
useMessage().error(err.msg)
}
}
// 删除操作
const handleDelete = async (ids: string[]) => {
try {
@@ -422,7 +460,7 @@ const getSummaries = (param: SummaryMethodProps) => {
{ column: "totalPrice", columnName: "商品总价", color: "#67C260" },
{ column: "extraPrice", columnName: "额外支付", color: "#F56E9B" },
{ column: "payPrice", columnName: "实际支付", color: "#355CF6" },
].forEach(({ column, columnName, color }: any, index: number) => {
].forEach(({ column, columnName, color }: any) => {
const values = data.map((item) => Number(item[column]))
if (!values.every((value) => Number.isNaN(value))) {
priceVNode.push(
+4 -4
View File
@@ -151,8 +151,8 @@ const resetQuery = () => {
queryRef.value?.resetFields()
// 清空多选
selectObjs.value = []
state.queryForm.deptId = ""
state.queryForm.deptIds = []
state.queryForm.userDeptId = ""
state.queryForm.userDeptIds = []
draggableDepTreeRef.value.resetData()
getDataList()
}
@@ -165,8 +165,8 @@ const selectionChangHandle = (objs: { id: string }[]) => {
// 树节点选择时
const handleGetSelectNode = (curClickedNode: any, checkedTreeNodes: any, isDeepTree: any) => {
state.queryForm.deptId = curClickedNode.id
state.queryForm.deptIds = checkedTreeNodes.map((item: any) => item.id)
state.queryForm.userDeptId = curClickedNode.id
state.queryForm.userDeptIds = checkedTreeNodes.map((item: any) => item.id)
state.queryForm.isInclude = !isDeepTree
getDataList()
}
+3
View File
@@ -7,6 +7,9 @@
<el-form-item style="width: 300px" label="设备名称" prop="hardName">
<el-input v-model="state.queryForm.hardName" />
</el-form-item>
<el-form-item style="width: 300px" label="设备唯一编号" prop="uniqueCode">
<el-input v-model="state.queryForm.uniqueCode" />
</el-form-item>
<el-form-item style="width: 300px" label="设备类型" prop="hardType">
<dict-select v-model="state.queryForm.hardType" dict-code="hard_type" />
</el-form-item>
@@ -5,7 +5,7 @@
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item label="商户" prop="mchId">
<select-mch disabled v-model="form.mchId" />
<select-mch autoFirst disabled v-model="form.mchId" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
@@ -71,7 +71,7 @@
</div>
</div>
<div class="take-card__placeholder">
预留报餐配置内容区,后续可以继续补充统计规则、时段限制、报餐说明等配置项。
暂无
</div>
</div>
@@ -150,6 +150,32 @@
</div>
</div>
<div class="take-card take-card--placeholder">
<div class="take-card__header">
<div class="take-card__intro">
<div class="take-card__title">商城配置</div>
</div>
<div class="take-card__toolbar">
<div class="take-card__switch-panel">
<div class="take-card__switch-copy">
<div class="take-card__switch-title">启用商城</div>
<div class="take-card__switch-desc">控制商户商城相关能力开关</div>
</div>
<el-switch v-model="state.mchDetail.functionScope.mallEnable"
:loading="state.switchLoadingKey === 'mallEnable'" inline-prompt active-text="开"
inactive-text="关" @change="handleMallSwitchChange" />
</div>
<div class="take-card__actions">
<el-button icon="Edit" circle @click="handleMallEdit()" />
<el-button icon="RefreshRight" circle @click="getMallDataList" />
</div>
</div>
</div>
<div class="take-card__placeholder">
暂无
</div>
</div>
<take-out-form ref="takeOutFormRef" @refresh="getTakeOutSettingDetail" />
<hard-appt-form ref="formDialogRef" @refresh="getDataList" />
</div>
@@ -176,8 +202,10 @@ const props = withDefaults(defineProps<{
// 定义变量内容
const formDialogRef = ref()
const selectObjs = ref([])
const mallSelectIds = ref<string[]>([])
const createDefaultFunctionScope = () => ({
mallEnable: false,
orderingFoodEnable: false,
tableReservation: false,
takeOutEnable: false,
@@ -201,7 +229,30 @@ const apptState: BasicTableProps = reactive<BasicTableProps>({
pageList: hardApptApi.fetchList,
})
const { getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, downBlobFile, tableStyle } = useTable(apptState)
const { getDataList, sortChangeHandle, tableStyle } = useTable(apptState)
const fetchMallConfigList = async () => {
return {
data: {
records: [],
total: 0,
},
}
}
const mallTableState: BasicTableProps = reactive<BasicTableProps>({
createdIsNeed: false,
queryForm: {
merchId: null,
},
pageList: fetchMallConfigList,
})
const {
getDataList: getMallDataList,
sortChangeHandle: mallSortChangeHandle,
tableStyle: mallTableStyle,
} = useTable(mallTableState)
const { yes_no_type, daily_menu } = useDict('yes_no_type', 'daily_menu')
watch(
@@ -242,6 +293,8 @@ async function getMchDetail() {
state.mchDetail = data
apptState.queryForm.deptId = data.deptId
getDataList()
mallTableState.queryForm.merchId = data.id
getMallDataList(false)
} catch (error) {
console.log("获取食堂详情详情", error);
}
@@ -280,6 +333,28 @@ function handleReportFoodSwitchChange(value: boolean) {
return handleFunctionScopeChange("orderingFoodEnable", value, "报餐配置")
}
function handleMallSwitchChange(value: boolean) {
return handleFunctionScopeChange("mallEnable", value, "商城配置")
}
function handleMallSelectionChange(rows: any[]) {
mallSelectIds.value = (rows || []).map((row: any) => row?.id).filter(Boolean)
}
function handleMallAdd() {
useMessage().info("商城配置功能开发中")
}
function handleMallEdit(id?: string) {
void id
useMessage().info("商城配置功能开发中")
}
function handleMallDelete(ids: string[]) {
if (!ids?.length) return
useMessage().info("商城配置功能开发中")
}
// 删除操作
const handleDelete = async (ids: string[]) => {
try {
+22 -43
View File
@@ -15,14 +15,9 @@
<el-col :span="24" class="mb20">
<el-form-item label="商户类型:" prop="types">
<el-radio-group v-model="form.types">
<el-radio
:label="item.value"
:disabled="merchantTypeRadioDisabled"
v-for="(item, index) in merchant_type"
border
:key="index"
@click="handleClickRadio(item.value)"
>
<el-radio :label="item.value" :disabled="merchantTypeRadioDisabled"
v-for="(item, index) in merchant_type" border :key="index"
@click="handleClickRadio(item.value)">
{{ item.label }}
</el-radio>
</el-radio-group>
@@ -30,15 +25,17 @@
</el-col>
<el-col v-if="form.types == '2'" :span="24" class="mb20">
<el-form-item label="合作商家类型:" prop="supType">
<el-radio-group v-model="form.supType" @change="(value:string) => form.supType = value">
<el-radio border :label="item.value" v-for="(item, index) in wallet_mch_sup_type" :key="index">{{ item.label }}</el-radio>
<el-radio-group v-model="form.supType" @change="(value: string) => form.supType = value">
<el-radio border :label="item.value" v-for="(item, index) in wallet_mch_sup_type"
:key="index">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col v-if="form.types == '2' && form.supType == '20'" :span="24" class="mb20">
<el-form-item label="合作状态:" prop="cooperationStatus">
<el-radio-group v-model="form.cooperationStatus">
<el-radio border :label="item.value" v-for="(item, index) in yes_no_type" :key="index">{{ item.label }}</el-radio>
<el-radio border :label="item.value" v-for="(item, index) in yes_no_type" :key="index">{{
item.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
@@ -101,40 +98,20 @@
<el-col v-if="form.types == '1'" :span="24" class="mb20">
<el-form-item label="营业时间:" class="my-0" />
<el-form-item class="ml-2" label="早上" prop="jobTime">
<el-time-picker
v-model="jobTime.morning"
placeholder="请选择营业时间"
is-range
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="HH:mm"
format="HH:mm"
/>
<el-time-picker v-model="jobTime.morning" placeholder="请选择营业时间" is-range range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" value-format="HH:mm" format="HH:mm" />
</el-form-item>
<el-form-item class="ml-2" label="中午" prop="jobTime">
<el-time-picker
v-model="jobTime.noon"
placeholder="请选择营业时间"
is-range
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="HH:mm"
format="HH:mm"
/>
<el-time-picker v-model="jobTime.noon" placeholder="请选择营业时间" is-range range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" value-format="HH:mm" format="HH:mm" />
</el-form-item>
<el-form-item class="ml-2" label="晚上" prop="jobTime">
<el-time-picker
v-model="jobTime.evening"
placeholder="请选择营业时间"
is-range
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="HH:mm"
format="HH:mm"
/>
<el-time-picker v-model="jobTime.evening" placeholder="请选择营业时间" is-range range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" value-format="HH:mm" format="HH:mm" />
</el-form-item>
<el-form-item class="ml-2" label="夜间餐" prop="jobTime">
<el-time-picker v-model="jobTime.night" placeholder="请选择营业时间" is-range range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" value-format="HH:mm" format="HH:mm" />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
@@ -193,6 +170,7 @@ const jobTime = reactive<{
morning: [],
noon: [],
evening: [],
night: [],
})
const merchantTypeRadioDisabled = ref(false)
@@ -239,13 +217,13 @@ watch(
// 提交
const onSubmit = async () => {
const valid = await dataFormRef.value.validate().catch(() => {})
const valid = await dataFormRef.value.validate().catch(() => { })
if (!valid) return false
const _pa = {
...form,
isSyncZy: form.isSyncZy ? "1" : "0",
jobTime: (jobTime.morning?.join("-") || "") + "," + (jobTime.noon?.join("-") || "") + "," + (jobTime.evening?.join("-") || ""),
jobTime: (jobTime.morning?.join("-") || "") + "," + (jobTime.noon?.join("-") || "") + "," + (jobTime.evening?.join("-") || "") + "," + (jobTime.night?.join("-") || ""),
cooperationStatus: form.types == "2" && form.supType == "10" ? null : form.cooperationStatus,
parentId: form.types == "2" && form.supType == "10" ? -1 : form.parentId,
}
@@ -284,6 +262,7 @@ const getmerchantInfoData = async (id: string) => {
jobTime.morning = resJobTime[0]?.split("-") ?? []
jobTime.noon = resJobTime[1]?.split("-") ?? []
jobTime.evening = resJobTime[2]?.split("-") ?? []
jobTime.night = resJobTime[3]?.split("-") ?? []
}
} catch (error: any) {
useMessage().error(error.msg)
+1
View File
@@ -120,6 +120,7 @@
<div>{{ row.jobTime?.split(",")[0] ? "早上:" + row.jobTime?.split(",")[0] : "" }}</div>
<div>{{ row.jobTime?.split(",")[1] ? "中午:" + row.jobTime?.split(",")[1] : "" }}</div>
<div>{{ row.jobTime?.split(",")[2] ? "晚上:" + row.jobTime?.split(",")[2] : "" }}</div>
<div>{{ row.jobTime?.split(",")[3] ? "夜间餐:" + row.jobTime?.split(",")[3] : "" }}</div>
</template>
</el-table-column>
<el-table-column prop="addressDes" label="地址" :min-width="160" align="center" />