Merge branch 'master' of https://gitea.zianxin.com/zax/biz-mobile-react-cloud
# Conflicts: # .env
This commit is contained in:
commit
219e7bcc00
12 files changed
+553
-315
No files matched your search
@@ -135,9 +135,7 @@ const cfu = {
|
||||
},
|
||||
// 食堂喜好分析饼图提示格式化
|
||||
canteenPieTooltip: function (item, category, index, opts) {
|
||||
return `分类:${item.classifyName || ""}/br/购买总人数:${item.payUserCount}/br/购买总金额:${
|
||||
Number(item.totalPrice) / 100.0
|
||||
}元/br/购买总次数:${item.timeCount}`
|
||||
return `分类:${item.classifyName || ""}/br/购买总数量:${item.payCount}/br/购买总金额:${Number(item.totalPrice) / 100.0}元`
|
||||
},
|
||||
// 食堂喜好分析折现图提示格式化
|
||||
canteenAreaTooltip: function (item, category, index, opts) {
|
||||
|
||||
+214
-51
@@ -6,23 +6,12 @@ import { isEmpty } from "radash"
|
||||
import { hooks, router } from "@@/index"
|
||||
import { boxesApi } from "@@/api"
|
||||
|
||||
import zTabs from "@@/components/other/z-tabs/z-tabs.vue"
|
||||
import CustomWaterfallsFlow from "@@/components/other/custom-waterfalls-flow/custom-waterfalls-flow.vue"
|
||||
import { filePath } from "@/sheep/utils/file"
|
||||
|
||||
const data = {
|
||||
tabs: [
|
||||
{ id: null, name: "全部", disabled: false },
|
||||
{ id: 0, name: "空闲", disabled: false },
|
||||
{ id: 1, name: "部分空闲", disabled: false },
|
||||
{ id: 2, name: "已满", disabled: false },
|
||||
],
|
||||
}
|
||||
|
||||
const state = reactive<any>({
|
||||
detailId: null,
|
||||
pa: {
|
||||
status: data.tabs[0].id,
|
||||
status: null, // 移除状态筛选
|
||||
},
|
||||
})
|
||||
|
||||
@@ -35,35 +24,47 @@ const boxesList = computed(() => {
|
||||
|
||||
const pageRef = ref<any>(null)
|
||||
const pageHook = hooks.usePage(pageRef, boxesApi.page, null, "data.records")
|
||||
|
||||
const query = (page: number, pagesize: number) => {
|
||||
pageHook.startPage(page, pagesize, false, state.pa)
|
||||
}
|
||||
|
||||
// 获取状态样式类
|
||||
const getStatusClass = (status: number) => {
|
||||
const statusMap: Record<number, string> = {
|
||||
0: 'status-available',
|
||||
1: 'status-partial',
|
||||
2: 'status-full'
|
||||
}
|
||||
return statusMap[status] || 'status-available'
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: number) => {
|
||||
const statusMap: Record<number, string> = {
|
||||
0: '空闲',
|
||||
1: '部分空闲',
|
||||
2: '已满'
|
||||
}
|
||||
return statusMap[status] || '空闲'
|
||||
}
|
||||
|
||||
onLoad((options: any) => {
|
||||
if (!isEmpty(options.id)) {
|
||||
state.detailId = options.id
|
||||
state.pa.merchantId = options.id
|
||||
}
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
if (pageRef.value) {
|
||||
pageRef.value.reload(false)
|
||||
}
|
||||
})
|
||||
|
||||
const doTab = (e: any) => {
|
||||
const target = data.tabs[e]
|
||||
state.pa.status = target.id
|
||||
pageRef.value.reload(true)
|
||||
}
|
||||
|
||||
const doCard = (v: any) => {
|
||||
router.jump("/pages/boxes/detail", { id: v.id })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<crud-layout title="包厢预定" bg="/static/page/blue-bg.png">
|
||||
<crud-layout title="餐桌列表" bg="/static/page/blue-bg.png">
|
||||
<z-paging
|
||||
ref="pageRef"
|
||||
height="100%"
|
||||
@@ -79,41 +80,203 @@ const doCard = (v: any) => {
|
||||
@scroll="pageHook.scroll"
|
||||
>
|
||||
<template #top>
|
||||
<view class="bg-white rounded-16rpx px-20rpx m-x-10rpx m-t-20rpx">
|
||||
<z-tabs :list="data.tabs" @change="doTab" />
|
||||
<view class="header-section">
|
||||
<view class="welcome-text">餐厅餐桌列表</view>
|
||||
</view>
|
||||
</template>
|
||||
<view class="h-20rpx" />
|
||||
<custom-waterfalls-flow v-model="boxesList" imageKey="images" :columnSpace="1" @wapperClick="doCard" @imageClick="doCard">
|
||||
<template v-slot:default="v">
|
||||
<view class="p-x-15rpx p-t-10rpx p-b-20rpx">
|
||||
<view class="sub-text-28 show-line">{{ v.name }}</view>
|
||||
<view class="m-t-10rpx flex-row items-center">
|
||||
<view class="second-text-32 color-#f63a36!"> ¥{{ v.price }} </view>
|
||||
</view>
|
||||
<view class="flex-row items-center justify-between m-t-10rpx">
|
||||
<view class="second-text-26">可容纳{{ v.capacity }}人</view>
|
||||
<view class="w-2rpx h-20rpx m-x-5rpx bg-#999" />
|
||||
<view class="flex-row-center">
|
||||
<tm-tag v-if="v.status == 0" size="xs" color="success">
|
||||
{{ hooks.useEnum("boxesBxStatusEnum", v.status.toString()) }}
|
||||
</tm-tag>
|
||||
<tm-tag v-if="v.status == 1" size="xs">
|
||||
{{ hooks.useEnum("boxesBxStatusEnum", v.status) }}
|
||||
</tm-tag>
|
||||
<tm-tag v-if="v.status == 2" size="xs" color="error">
|
||||
{{ hooks.useEnum("boxesBxStatusEnum", v.status) }}
|
||||
</tm-tag>
|
||||
|
||||
<view class="boxes-container">
|
||||
<view class="boxes-grid">
|
||||
<view v-for="item in boxesList" :key="item.id" class="box-card">
|
||||
<view class="card-image-section">
|
||||
<image v-if="item.images" :src="item.images" class="box-image" mode="aspectFill" />
|
||||
<view v-else class="image-placeholder">
|
||||
<text class="placeholder-icon">🍴</text>
|
||||
<text class="placeholder-text">餐桌</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-row items-center m-t-10rpx">
|
||||
<view class="second-text-26">{{ v.address }}</view>
|
||||
|
||||
<view class="card-content">
|
||||
<view class="box-title">{{ item.name }}</view>
|
||||
<view class="box-subtitle">{{ item.tableNumber }}</view>
|
||||
|
||||
<view class="box-features">
|
||||
<view class="feature-item">
|
||||
<text class="feature-icon">👥</text>
|
||||
<text class="feature-text">{{ item.tableTypeName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</custom-waterfalls-flow>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
.header-section {
|
||||
padding: 40rpx 24rpx 32rpx 24rpx;
|
||||
background: linear-gradient(135deg, #3B82F6 0%, #1D4ED8 100%);
|
||||
border-radius: 0 0 32rpx 32rpx;
|
||||
margin: 0 24rpx 32rpx 24rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.welcome-text {
|
||||
color: #fff;
|
||||
font-size: 38rpx;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
.boxes-container {
|
||||
padding: 0 24rpx;
|
||||
background: transparent;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.boxes-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(59, 130, 246, 0.08);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border: 1rpx solid #DBEAFE;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.box-card:hover {
|
||||
transform: translateY(-4rpx);
|
||||
box-shadow: 0 8rpx 24rpx rgba(59, 130, 246, 0.15);
|
||||
border-color: #93C5FD;
|
||||
}
|
||||
|
||||
.card-image-section {
|
||||
position: relative;
|
||||
height: 160rpx;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.box-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.box-card:hover .box-image {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.image-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, #EFF6FF 0%, #DBEAFE 100%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.placeholder-icon {
|
||||
font-size: 48rpx;
|
||||
margin-bottom: 8rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.placeholder-text {
|
||||
font-size: 24rpx;
|
||||
color: #3B82F6;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 24rpx;
|
||||
background: #fff;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card-content::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 24rpx;
|
||||
right: 24rpx;
|
||||
height: 1rpx;
|
||||
background: linear-gradient(90deg, transparent 0%, #E5E7EB 20%, #E5E7EB 80%, transparent 100%);
|
||||
}
|
||||
|
||||
.box-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #1E40AF;
|
||||
margin-bottom: 12rpx;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.box-subtitle {
|
||||
font-size: 26rpx;
|
||||
color: #3B82F6;
|
||||
margin-bottom: 20rpx;
|
||||
display: inline-block;
|
||||
background: linear-gradient(135deg, #EFF6FF 0%, #DBEAFE 100%);
|
||||
padding: 10rpx 18rpx;
|
||||
border-radius: 12rpx;
|
||||
color: #1D4ED8;
|
||||
font-weight: 600;
|
||||
border: 1rpx solid #DBEAFE;
|
||||
box-shadow: 0 2rpx 8rpx rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.box-features {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.feature-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0;
|
||||
padding: 8rpx 0;
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 32rpx;
|
||||
margin-right: 16rpx;
|
||||
width: 48rpx;
|
||||
text-align: center;
|
||||
background: linear-gradient(135deg, #60A5FA 0%, #3B82F6 100%);
|
||||
padding: 12rpx;
|
||||
border-radius: 12rpx;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2rpx 8rpx rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
.feature-text {
|
||||
font-size: 28rpx;
|
||||
color: #1E40AF;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -30,7 +30,9 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
|
||||
// 点击加
|
||||
const add = () => {
|
||||
// if (props.modelValue + 1 >= props.stock) return uni.showToast({ title: "库存不足", icon: "none" })
|
||||
if (props.mode !== "cart") {
|
||||
if (props.modelValue + 1 > props.stock) return uni.showToast({ title: "库存不足", icon: "none" })
|
||||
}
|
||||
emit("update:modelValue", props.modelValue + 1)
|
||||
emit("add")
|
||||
}
|
||||
@@ -58,7 +60,8 @@ const remove = () => {
|
||||
<view v-if="props.mode == 'cart'" class="m-l-40rpx second-text-24">x{{ props.count }}</view>
|
||||
</view>
|
||||
<view v-if="props.mode == 'default'" class="flex-row items-center MT 10rpx">
|
||||
<view class="second-text-24">{{ hooks.useFormatNum("库存", "", props.stock) }}</view>
|
||||
<!-- <view class="second-text-24">{{ hooks.useFormatNum("库存", "", props.stock) }}</view> -->
|
||||
<view class="second-text-24">库存:{{ props.stock }}</view>
|
||||
<view class="w-1px h-20rpx m-x-5rpx bg-#999 mx-2" />
|
||||
<view class="second-text-24">{{ hooks.useFormatNum("销量", "", props.salesCount) }}</view>
|
||||
</view>
|
||||
|
||||
@@ -5,7 +5,6 @@ import { isEmpty } from "radash"
|
||||
import { hooks, router } from "@@/index"
|
||||
import { merchantApi, foodApi, payApi, orderApi } from "@@/api"
|
||||
import { filePath } from "@@/utils/file"
|
||||
|
||||
import ProductRow from "./components/product-row.vue"
|
||||
import ProductCart from "./components/product-cart.vue"
|
||||
import ProductTotal from "./components/product-total.vue"
|
||||
@@ -99,6 +98,7 @@ const getFoodList = async () => {
|
||||
const _pa = {
|
||||
merchantId: state.detailId,
|
||||
categoryId: target.id,
|
||||
time: hooks.useTime(new Date(), "{y}-{m}-{d}"),
|
||||
}
|
||||
const { data } = await foodApi.product(_pa)
|
||||
data.forEach((i: any) => {
|
||||
@@ -111,7 +111,7 @@ const getFoodList = async () => {
|
||||
// 切换选中的分类
|
||||
const doClassify = (i: any) => {
|
||||
state.currentClassifyIndex = i
|
||||
getClassify()
|
||||
getFoodList()
|
||||
}
|
||||
|
||||
// 设置购物车商品的数量
|
||||
@@ -119,8 +119,11 @@ const chentProductCartCount = async (item: any) => {
|
||||
try {
|
||||
uni.showLoading({ title: "加载中", mask: true })
|
||||
// 检查商品库存
|
||||
const { code }: any = await foodApi.checkSpuStock({ spuId: item.productId, incrCount: item.productCount })
|
||||
if (code != 0) throw new Error()
|
||||
const { code, msg }: any = await foodApi.checkSpuStock({ spuId: item.productId, incrCount: item.productCount })
|
||||
if (code != 0) {
|
||||
item.productCount--
|
||||
return uni.showToast({ title: msg, icon: "none" })
|
||||
}
|
||||
|
||||
const targetIndex = state.cartList.findIndex((v) => v.productId == item.productId)
|
||||
if (targetIndex !== -1) {
|
||||
@@ -292,7 +295,7 @@ const doFood = () => {
|
||||
<view class="flex-row-center w-50rpx h-50rpx bg-#167aff rounded-full">
|
||||
<text class="font-size-28rpx! color-white iconfont icon-yuding" />
|
||||
</view>
|
||||
<text class="m-l-20rpx sub-text-28">包厢预订</text>
|
||||
<text class="m-l-20rpx sub-text-28">餐桌信息</text>
|
||||
</view>
|
||||
<view class="w-1px h-30rpx bg-#c4c4c4" />
|
||||
<view class="flex-row-center" @tap="doFood">
|
||||
|
||||
@@ -32,7 +32,7 @@ const getDetail = async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<crud-layout title="订单详情" tabBarType="just-flot-back" bg="/static/page/blue-bg.png">
|
||||
<crud-layout title="菜品详情" tabBarType="just-flot-back" bg="/static/page/blue-bg.png">
|
||||
<view v-if="state.detail">
|
||||
<image class="w-100vw" :src="filePath(state.detail.picUrl)" mode="widthFix" />
|
||||
<view class="p-x-15rpx">
|
||||
|
||||
@@ -1,220 +1,230 @@
|
||||
<template>
|
||||
<view class="notice-header">
|
||||
<view class="back-btn" @click="goBack">
|
||||
<text class="iconfont icon-fanhui"></text> 返回
|
||||
</view>
|
||||
</view>
|
||||
<view class="notice-container">
|
||||
<view class="notice-list">
|
||||
<view v-if="noticeList.length === 0" class="empty-tip">
|
||||
暂无公告
|
||||
</view>
|
||||
<view v-else>
|
||||
<view
|
||||
v-for="item in noticeList"
|
||||
:key="item.id"
|
||||
class="notice-item"
|
||||
>
|
||||
<view class="item-main">
|
||||
<view class="icon-area">
|
||||
<view class="icon-bell">
|
||||
<view class="red-dot"></view>
|
||||
<text class="iconfont icon-lingdang"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content-area">
|
||||
<view class="title">{{ item.title }}</view>
|
||||
<view class="summary">{{ item.summary || item.content }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-footer">
|
||||
<view class="detail-btn" @click="viewDetail(item)">查看详情 ></view>
|
||||
<view class="date">{{ formatDate(item.createTime) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="hasMore" class="load-more" @click="loadMore">
|
||||
<text>加载更多</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="notice-header">
|
||||
<view class="back-btn" @click="goBack"> <text class="iconfont icon-fanhui"></text> 返回 </view>
|
||||
</view>
|
||||
<view class="notice-container">
|
||||
<view class="notice-list">
|
||||
<view v-if="noticeList.length === 0" class="empty-tip"> 暂无公告 </view>
|
||||
<view v-else>
|
||||
<view v-for="item in noticeList" :key="item.id" class="notice-item">
|
||||
<view class="item-main">
|
||||
<view class="icon-area">
|
||||
<view class="icon-bell">
|
||||
<view class="red-dot"></view>
|
||||
<text class="iconfont">
|
||||
<svg
|
||||
t="1755142010765"
|
||||
class="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="12392"
|
||||
width="24"
|
||||
height="24"
|
||||
>
|
||||
<path
|
||||
d="M512 960c49.6 0 88-38.4 88-88H424c0 49.6 38.4 88 88 88z m288.8-286.4V444.8c0-137.6-97.6-252.8-224.8-283.2v-28.8c0-32-17.6-60.8-48-67.2-44-10.4-80 23.2-80 66.4v30.4C320.8 192 223.2 307.2 223.2 444.8v228.8L136 763.2v44.8h752v-44.8l-87.2-89.6z"
|
||||
p-id="12393"
|
||||
data-spm-anchor-id="a313x.search_index.0.i6.61dc3a812gyLD2"
|
||||
class="selected"
|
||||
fill="#3976DA"
|
||||
></path>
|
||||
</svg>
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content-area">
|
||||
<view class="title">{{ item.title }}</view>
|
||||
<view class="summary">{{ item.summary || item.content }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-footer">
|
||||
<view class="detail-btn" @click="viewDetail(item)">查看详情 ></view>
|
||||
<view class="date">{{ formatDate(item.createTime) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="hasMore" class="load-more" @click="loadMore">
|
||||
<text>加载更多</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { noticeApi } from '@/sheep/api'
|
||||
import { ref, onMounted } from "vue"
|
||||
import { noticeApi } from "@/sheep/api"
|
||||
|
||||
const noticeList = ref<any[]>([])
|
||||
const currentPage = ref(1)
|
||||
const hasMore = ref(true)
|
||||
|
||||
function formatDate(time: string) {
|
||||
if (!time) return ''
|
||||
const d = new Date(time)
|
||||
const y = d.getFullYear()
|
||||
const m = (d.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = d.getDate().toString().padStart(2, '0')
|
||||
return `${y}.${m}.${day}`
|
||||
if (!time) return ""
|
||||
const d = new Date(time)
|
||||
const y = d.getFullYear()
|
||||
const m = (d.getMonth() + 1).toString().padStart(2, "0")
|
||||
const day = d.getDate().toString().padStart(2, "0")
|
||||
return `${y}.${m}.${day}`
|
||||
}
|
||||
|
||||
function viewDetail(item: any) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/integrated/notice/detail?id=${item.id}`
|
||||
})
|
||||
uni.navigateTo({
|
||||
url: `/pages/integrated/notice/detail?id=${item.id}`,
|
||||
})
|
||||
}
|
||||
|
||||
async function loadNoticeList(reset = false) {
|
||||
try {
|
||||
const params = {
|
||||
current: currentPage.value,
|
||||
size: 10
|
||||
}
|
||||
const response: any = await noticeApi.getNoticePage(params)
|
||||
if (response.code === 0 && response.data) {
|
||||
const records = response.data.records || []
|
||||
if (reset) {
|
||||
noticeList.value = records
|
||||
} else {
|
||||
noticeList.value = [...noticeList.value, ...records]
|
||||
}
|
||||
hasMore.value = records.length === 10
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载公告列表失败:', error)
|
||||
}
|
||||
try {
|
||||
const params = {
|
||||
current: currentPage.value,
|
||||
size: 10,
|
||||
}
|
||||
const response: any = await noticeApi.getNoticePage(params)
|
||||
if (response.code === 0 && response.data) {
|
||||
const records = response.data.records || []
|
||||
if (reset) {
|
||||
noticeList.value = records
|
||||
} else {
|
||||
noticeList.value = [...noticeList.value, ...records]
|
||||
}
|
||||
hasMore.value = records.length === 10
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("加载公告列表失败:", error)
|
||||
}
|
||||
}
|
||||
|
||||
function loadMore() {
|
||||
if (!hasMore.value) return
|
||||
currentPage.value++
|
||||
loadNoticeList()
|
||||
if (!hasMore.value) return
|
||||
currentPage.value++
|
||||
loadNoticeList()
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
uni.navigateBack()
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadNoticeList(true)
|
||||
loadNoticeList(true)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import url('//at.alicdn.com/t/font_1234567_lingdang.css'); /* 替换为你的iconfont地址 */
|
||||
.notice-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 88rpx;
|
||||
background: #fff;
|
||||
padding-left: 24rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
|
||||
margin-bottom: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 88rpx;
|
||||
background: #fff;
|
||||
padding-left: 24rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
.back-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 30rpx;
|
||||
color: #1976d2;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 30rpx;
|
||||
color: #1976d2;
|
||||
cursor: pointer;
|
||||
}
|
||||
.back-btn .iconfont {
|
||||
font-size: 36rpx;
|
||||
margin-right: 8rpx;
|
||||
font-size: 36rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
.notice-container {
|
||||
background: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.notice-list {
|
||||
padding: 24rpx;
|
||||
padding: 24rpx;
|
||||
}
|
||||
.notice-item {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 32rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
|
||||
padding: 32rpx 32rpx 24rpx 32rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 32rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
padding: 32rpx 32rpx 24rpx 32rpx;
|
||||
}
|
||||
.item-main {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.icon-area {
|
||||
margin-right: 24rpx;
|
||||
position: relative;
|
||||
margin-right: 24rpx;
|
||||
position: relative;
|
||||
}
|
||||
.icon-bell {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
background: #e3f2fd;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
background: #e3f2fd;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
.icon-bell .iconfont {
|
||||
font-size: 32rpx;
|
||||
color: #1976d2;
|
||||
font-size: 32rpx;
|
||||
color: #1976d2;
|
||||
}
|
||||
.red-dot {
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
background: #f44336;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 4rpx;
|
||||
right: 4rpx;
|
||||
border: 2rpx solid #fff;
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
background: #f44336;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 4rpx;
|
||||
right: 4rpx;
|
||||
border: 2rpx solid #fff;
|
||||
}
|
||||
.content-area {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #222;
|
||||
margin-bottom: 8rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #222;
|
||||
margin-bottom: 8rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.summary {
|
||||
font-size: 26rpx;
|
||||
color: #888;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 26rpx;
|
||||
color: #888;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.item-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
margin-top: 18rpx;
|
||||
padding-top: 12rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
margin-top: 18rpx;
|
||||
padding-top: 12rpx;
|
||||
}
|
||||
.detail-btn {
|
||||
color: #1976d2;
|
||||
font-size: 26rpx;
|
||||
cursor: pointer;
|
||||
color: #1976d2;
|
||||
font-size: 26rpx;
|
||||
cursor: pointer;
|
||||
}
|
||||
.date {
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
padding: 60rpx 0;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
padding: 60rpx 0;
|
||||
}
|
||||
.load-more {
|
||||
text-align: center;
|
||||
color: #007aff;
|
||||
font-size: 28rpx;
|
||||
padding: 32rpx 0;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
color: #007aff;
|
||||
font-size: 28rpx;
|
||||
padding: 32rpx 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -1,85 +1,97 @@
|
||||
<template>
|
||||
<crud-layout title="详情" mainIsScroll bg="/static/page/blue-bg.png">
|
||||
<tm-sheet :margin="[0, 20]">
|
||||
<view class="prefix_title mb-15rpx">菜品分类购买次数占比图</view>
|
||||
<view class="w-full h-600rpx">
|
||||
<qiun-data-charts type="pie" :opts="pieChartOpts" :chartData="pieChartData" tooltipFormat="canteenPieTooltip" />
|
||||
</view>
|
||||
</tm-sheet>
|
||||
|
||||
<tm-sheet :margin="[0, 20]">
|
||||
<view class="prefix_title mb-15rpx">近一周食堂购买人数趋势图</view>
|
||||
<view class="w-full h-600rpx">
|
||||
<!-- tooltipFormat="canteenAreaTooltip" -->
|
||||
<qiun-data-charts type="area" :opts="areaChartOpts" :chartData="areaChartData" />
|
||||
</view>
|
||||
</tm-sheet>
|
||||
|
||||
<tm-sheet :margin="[0, 20]">
|
||||
<view class="prefix_title mb-15rpx">个人菜品喜好排行</view>
|
||||
<view v-if="productLikeTopList.length > 0" class="rounded-16rpx bg-white overflow-hidden card">
|
||||
<view class="bg-gradient-to-r from-blue-100 to-blue-100 px-20rpx py-16rpx flex items-center border-b border-gray-100">
|
||||
<view class="w-80rpx text-center text-24rpx font-bold text-gray-600">排名</view>
|
||||
<view class="flex-1 px-16rpx text-24rpx font-bold text-gray-600">菜品名称</view>
|
||||
<view class="w-120rpx text-center text-24rpx font-bold text-gray-600">分类</view>
|
||||
<view class="w-100rpx text-center text-24rpx font-bold text-gray-600">购买次数</view>
|
||||
<crud-layout title="喜好分析" bg="/static/page/blue-bg.png">
|
||||
<z-paging
|
||||
ref="pageRef"
|
||||
height="100%"
|
||||
:fixed="false"
|
||||
safe-area-inset-bottom
|
||||
:loading-more-enabled="true"
|
||||
:show-scrollbar="false"
|
||||
@query="query"
|
||||
:auto-clean-list-when-reload="false"
|
||||
:auto-scroll-to-top-when-reload="false"
|
||||
hide-empty-view
|
||||
>
|
||||
<tm-sheet :margin="[0, 20]">
|
||||
<view class="prefix_title mb-15rpx">菜品分类购买数量占比图</view>
|
||||
<view class="w-full h-600rpx">
|
||||
<qiun-data-charts type="pie" :opts="pieChartOpts" :chartData="pieChartData" tooltipFormat="canteenPieTooltip" />
|
||||
</view>
|
||||
</tm-sheet>
|
||||
|
||||
<view
|
||||
v-for="(item, index) in productLikeTopList"
|
||||
:key="item.productId"
|
||||
class="flex items-center px-20rpx py-16rpx border-b border-gray-50 hover:bg-gray-50 transition-colors"
|
||||
:class="{
|
||||
'bg-gradient-to-r from-yellow-50 to-orange-50': index === 0,
|
||||
'bg-gradient-to-r from-gray-50 to-gray-100': index === 1,
|
||||
'bg-gradient-to-r from-orange-50 to-red-50': index === 2,
|
||||
}"
|
||||
>
|
||||
<view class="w-80rpx flex justify-center items-center">
|
||||
<view
|
||||
v-if="index < 3"
|
||||
class="w-40rpx h-40rpx rounded-1 flex items-center justify-center text-20rpx font-bold text-white"
|
||||
:class="{
|
||||
'bg-gradient-to-r from-yellow-400 to-yellow-500': index === 0,
|
||||
'bg-gradient-to-r from-gray-400 to-gray-500': index === 1,
|
||||
'bg-gradient-to-r from-orange-400 to-orange-500': index === 2,
|
||||
}"
|
||||
>
|
||||
{{ index + 1 }}
|
||||
<tm-sheet :margin="[0, 20]">
|
||||
<view class="prefix_title mb-15rpx">近一周各食堂购买数量趋势图</view>
|
||||
<view class="w-full h-600rpx">
|
||||
<!-- tooltipFormat="canteenAreaTooltip" -->
|
||||
<qiun-data-charts type="area" :opts="areaChartOpts" :chartData="areaChartData" />
|
||||
</view>
|
||||
</tm-sheet>
|
||||
|
||||
<tm-sheet :margin="[0, 20]">
|
||||
<view class="prefix_title mb-15rpx">个人菜品喜好排行</view>
|
||||
<view v-if="productLikeTopList.length > 0" class="rounded-16rpx bg-white overflow-hidden card">
|
||||
<view class="bg-gradient-to-r from-blue-100 to-blue-100 px-20rpx py-16rpx flex items-center border-b border-gray-100">
|
||||
<view class="w-80rpx text-center text-24rpx font-bold text-gray-600">排名</view>
|
||||
<view class="flex-1 px-16rpx text-24rpx font-bold text-gray-600">菜品名称</view>
|
||||
<view class="w-120rpx text-center text-24rpx font-bold text-gray-600">分类</view>
|
||||
<view class="w-160rpx text-center text-24rpx font-bold text-gray-600">购买数量</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-for="(item, index) in productLikeTopList"
|
||||
:key="item.productId"
|
||||
class="flex items-center px-20rpx py-16rpx border-b border-gray-50 hover:bg-gray-50 transition-colors"
|
||||
:class="{
|
||||
'bg-gradient-to-r from-yellow-50 to-orange-50': index === 0,
|
||||
'bg-gradient-to-r from-gray-50 to-gray-100': index === 1,
|
||||
'bg-gradient-to-r from-orange-50 to-red-50': index === 2,
|
||||
}"
|
||||
>
|
||||
<view class="w-80rpx flex justify-center items-center">
|
||||
<view
|
||||
v-if="index < 3"
|
||||
class="w-40rpx h-40rpx rounded-1 flex items-center justify-center text-20rpx font-bold text-white"
|
||||
:class="{
|
||||
'bg-gradient-to-r from-yellow-400 to-yellow-500': index === 0,
|
||||
'bg-gradient-to-r from-gray-400 to-gray-500': index === 1,
|
||||
'bg-gradient-to-r from-orange-400 to-orange-500': index === 2,
|
||||
}"
|
||||
>
|
||||
{{ index + 1 }}
|
||||
</view>
|
||||
<view v-else class="text-24rpx font-medium text-gray-500">{{ index + 1 }}</view>
|
||||
</view>
|
||||
<view v-else class="text-24rpx font-medium text-gray-500">{{ index + 1 }}</view>
|
||||
</view>
|
||||
|
||||
<view class="flex-1 px-16rpx">
|
||||
<view class="text-26rpx font-medium text-gray-800 truncate">{{ item.productName || "未知菜品" }}</view>
|
||||
</view>
|
||||
<view class="flex-1 px-16rpx">
|
||||
<view class="text-26rpx font-medium text-gray-800 truncate">{{ item.productName || "未知菜品" }}</view>
|
||||
</view>
|
||||
|
||||
<view class="w-120rpx text-center">
|
||||
<view class="text-22rpx text-gray-600 truncate">{{ item.classifyName || "未分类" }}</view>
|
||||
</view>
|
||||
<view class="w-120rpx text-center">
|
||||
<view class="text-22rpx text-gray-600 truncate">{{ item.classifyName || "未分类" }}</view>
|
||||
</view>
|
||||
|
||||
<view class="w-100rpx text-center">
|
||||
<view class="inline-block px-12rpx py-6rpx bg-blue-100 text-blue-600 text-22rpx font-medium rounded-1">
|
||||
{{ item.payCount }}次
|
||||
<view class="w-160rpx text-center">
|
||||
<view class="px-12rpx py-6rpx bg-blue-100 text-blue-600 text-22rpx rounded-1 whitespace-nowrap">{{ item.payCount }}次</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="bg-white rounded-16rpx p-60rpx flex flex-col items-center justify-center">
|
||||
<view class="text-28rpx text-gray-500">暂无菜品喜好数据</view>
|
||||
</view>
|
||||
</tm-sheet>
|
||||
<view v-else class="bg-white rounded-16rpx p-60rpx flex flex-col items-center justify-center">
|
||||
<view class="text-28rpx text-gray-500">暂无菜品喜好数据</view>
|
||||
</view>
|
||||
</tm-sheet>
|
||||
|
||||
<view class="h-80rpx" />
|
||||
<view class="h-80rpx" />
|
||||
</z-paging>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app"
|
||||
import { onLoad } from "@dcloudio/uni-app"
|
||||
import { ref } from "vue"
|
||||
import qiunDataCharts from "@/components/qiun-data-charts/qiun-data-charts.vue"
|
||||
import { totalApi } from "@/sheep/api"
|
||||
import { hooks } from "@/sheep"
|
||||
|
||||
const pieChartData = ref({})
|
||||
const areaChartData = ref({})
|
||||
@@ -88,7 +100,7 @@ const COLORLIST = ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA2
|
||||
|
||||
const pieChartOpts = {
|
||||
color: COLORLIST,
|
||||
padding: [15, 15, 15, 15],
|
||||
padding: [20, 20, 20, 20],
|
||||
enableScroll: false,
|
||||
legend: {
|
||||
float: "center",
|
||||
@@ -118,7 +130,7 @@ const areaChartOpts = {
|
||||
enableScroll: false,
|
||||
legend: {
|
||||
float: "center",
|
||||
padding: 2,
|
||||
padding: 10,
|
||||
margin: 2,
|
||||
fontSize: 12,
|
||||
lineHeight: 12,
|
||||
@@ -133,9 +145,9 @@ const areaChartOpts = {
|
||||
showTitle: true,
|
||||
data: [
|
||||
{
|
||||
title: "购买人数",
|
||||
title: "购买数量",
|
||||
titleOffsetX: 10,
|
||||
titleOffsetY: -4,
|
||||
titleOffsetY: -6,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -154,16 +166,16 @@ const areaChartOpts = {
|
||||
onLoad(() => {
|
||||
const app = getApp()
|
||||
app._initTabbar()
|
||||
})
|
||||
|
||||
const pageRef = ref<any>(null)
|
||||
const pageHook = hooks.usePage(pageRef, () => [], null, "")
|
||||
const query = () => {
|
||||
getPieChartData()
|
||||
getAreaChartData()
|
||||
getProductLikeTop()
|
||||
})
|
||||
onShow(() => {
|
||||
getPieChartData()
|
||||
getAreaChartData()
|
||||
getProductLikeTop()
|
||||
})
|
||||
pageHook.startPage()
|
||||
}
|
||||
|
||||
const getPieChartData = async () => {
|
||||
const { code, data, msg } = await totalApi.productByClassify({})
|
||||
@@ -173,7 +185,7 @@ const getPieChartData = async () => {
|
||||
index,
|
||||
name: item.classifyName || "",
|
||||
value: Number(item.payCount),
|
||||
labelText: `${item.classifyName || ""} ${item.payUserCount}次`,
|
||||
labelText: `${item.classifyName || ""} ${item.payCount}次`,
|
||||
...item,
|
||||
}
|
||||
})
|
||||
@@ -194,9 +206,9 @@ const getAreaChartData = async () => {
|
||||
|
||||
const series = data.map((item: any) => {
|
||||
const name = Object.keys(item)[0]
|
||||
const data = item[name].map((item: any) => Number(item.payUserCount))
|
||||
const data = item[name].map((item: any) => Number(item.payCount))
|
||||
return {
|
||||
name,
|
||||
name: name.replace("食堂", ""),
|
||||
data,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -11,6 +11,7 @@ const state = reactive<any>({
|
||||
pa: {
|
||||
pagesize: 10,
|
||||
type: "2",
|
||||
name: "",
|
||||
catgoryId: null,
|
||||
},
|
||||
tabs: [{ id: null, name: "全部", disabled: false }],
|
||||
@@ -40,6 +41,10 @@ onShow(() => {
|
||||
pageRef.value && pageRef.value.reload(false)
|
||||
})
|
||||
|
||||
const search = () => {
|
||||
pageRef.value && pageRef.value.reload(true)
|
||||
}
|
||||
|
||||
const doTab = (e: any) => {
|
||||
const target = state.tabs[e]
|
||||
state.pa.catgoryId = target.id
|
||||
@@ -74,12 +79,31 @@ const toDetail = async (v: any) => {
|
||||
>
|
||||
<template #top>
|
||||
<view class="h-30rpx" />
|
||||
<view class="bg-white rounded-16rpx px-20rpx">
|
||||
<view class="flex w-full">
|
||||
<tm-input
|
||||
class="flex-1"
|
||||
v-model="state.pa.name"
|
||||
type="text"
|
||||
height="66"
|
||||
minHeight="66"
|
||||
fontSize="28"
|
||||
:showClear="true"
|
||||
:focus="false"
|
||||
placeholder="请输入商家名称"
|
||||
style="border: 2rpx solid #0779fd; border-radius: 14rpx"
|
||||
:focus-highlight="false"
|
||||
color="#ECF4F9"
|
||||
>
|
||||
<template v-slot:rightIcon>
|
||||
<tm-button class="mr-1 py-3" size="xs" @click="search">搜索</tm-button>
|
||||
</template>
|
||||
</tm-input>
|
||||
</view>
|
||||
<view class="bg-white rounded-16rpx px-20rpx my-20rpx">
|
||||
<z-tabs :list="state.tabs" @change="doTab" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<view class="h-20rpx" />
|
||||
<view class="flex-row card p-20rpx m-b-20rpx" v-for="(v, i) in pageHook.list" :key="i" @tap="toDetail(v)">
|
||||
<image class="w-180rpx h-180rpx rounded-12rpx my-auto" :src="filePath(v.logoPic)" mode="aspectFill" />
|
||||
<view class="flex-1 flex-col m-l-20rpx">
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
class="flex-1"
|
||||
v-model="state.params.keyword"
|
||||
type="text"
|
||||
:height="66"
|
||||
:minHeight="66"
|
||||
:fontSize="28"
|
||||
height="66"
|
||||
minHeight="66"
|
||||
fontSize="28"
|
||||
:showClear="true"
|
||||
:focus="false"
|
||||
placeholder="请输入商品名称"
|
||||
|
||||
+16
-13
@@ -129,25 +129,28 @@ const doRefund = (v: any) => {
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex items-center ml-[120rpx]">
|
||||
<scroll-view v-if="['5', '20'].includes(v.orderStatus) && v.orderType == 2" scroll-x class="flex-row-center whitespace-nowrap">
|
||||
<view class="flex-row items-center">
|
||||
<view class="inline-flex! flex-col items-center m-r-20rpx" v-for="(pV, pI) in v.carList" :key="pI">
|
||||
<image class="w-60rpx h-60rpx rounded-8rpx" :src="filePath(pV.productPic)" mode="aspectFill" />
|
||||
<view class="w-80rpx m-t-10rpx sub-text-24 show-line text-center">{{ pV.productName }}</view>
|
||||
|
||||
<view class="flex items-center ml-[20rpx] overflow-hidden gap-2">
|
||||
<view class="w-4/5">
|
||||
<scroll-view v-if="['5', '20'].includes(v.orderStatus) && v.orderType == 2" scroll-x>
|
||||
<view class="flex-row items-center">
|
||||
<view class="inline-flex! flex-col items-center m-r-20rpx" v-for="(pV, pI) in v.carList" :key="pI">
|
||||
<image class="w-80rpx h-80rpx rounded-8rpx" :src="filePath(pV.productPic)" mode="aspectFill" />
|
||||
<view class="m-t-10rpx sub-text-24 show-line text-center">{{ pV.productName }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view
|
||||
v-if="v.carList?.length && ['5', '20'].includes(v.orderStatus) && v.orderType == 2"
|
||||
class="w-200rpx m-x-10rpx second-text-26"
|
||||
class="w-1/5 second-text-26 truncate text-center"
|
||||
>
|
||||
共 {{ v.carList.length }} 件
|
||||
</view>
|
||||
<view v-if="['5', '10'].includes(v.orderStatus)" class="w-500rpx flex-row items-center justify-end">
|
||||
<!-- <view v-if="v.orderStatus == 5 && v.orderType == 2" class="botder_btn" @tap.stop="doRefund(v)">退款</view> -->
|
||||
<view v-if="v.orderStatus == 5 && v.orderType == 2" class="m-l-20rpx botder_btn" @tap.stop="doToDetail(v)">核销码</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="['5', '10'].includes(v.orderStatus)" class="w-full flex-row items-center justify-end my-1">
|
||||
<!-- <view v-if="v.orderStatus == 5 && v.orderType == 2" class="botder_btn" @tap.stop="doRefund(v)">退款</view> -->
|
||||
<view v-if="v.orderStatus == 5 && v.orderType == 2" class="m-l-20rpx botder_btn" @tap.stop="doToDetail(v)">核销码</view>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
|
||||
@@ -27,8 +27,8 @@ const processQueue = (error: any, token: any = null) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建http请求实例
|
||||
const http = new Request({
|
||||
// 创建https请求实例
|
||||
const https = new Request({
|
||||
baseURL: import.meta.env.VITE_API_URL,
|
||||
timeout: 15000,
|
||||
method: "GET",
|
||||
@@ -47,7 +47,21 @@ const http = new Request({
|
||||
},
|
||||
})
|
||||
|
||||
http.interceptors.request.use(async (config) => {
|
||||
// 生产环境协议安全检查
|
||||
const validateProductionSecurity = () => {
|
||||
if (isDevEnv()) return
|
||||
|
||||
const apiUrl = import.meta.env.VITE_API_URL
|
||||
if (apiUrl && !apiUrl.startsWith("https://")) {
|
||||
uni.showToast({ title: "生产环境安全错误: 必须使用 HTTPS 协议", icon: "error" })
|
||||
throw new Error("生产环境禁止使用 HTTP 协议")
|
||||
}
|
||||
}
|
||||
|
||||
https.interceptors.request.use(async (config) => {
|
||||
// 生产环境协议检查
|
||||
// validateProductionSecurity()
|
||||
|
||||
const tokenStorage = uni.getStorageSync(TOKEN_KEY)
|
||||
const config_header: any = config.header || {}
|
||||
|
||||
@@ -84,7 +98,7 @@ http.interceptors.request.use(async (config) => {
|
||||
}, Promise.reject)
|
||||
|
||||
// 响应拦截器
|
||||
http.interceptors.response.use(
|
||||
https.interceptors.response.use(
|
||||
async (response) => {
|
||||
// 账号密码登录响应结果特殊处理
|
||||
if (response.config.url === "/auth/oauth2/token") {
|
||||
@@ -113,7 +127,7 @@ http.interceptors.response.use(
|
||||
const config = error.config
|
||||
const tokenValue = getTokenValue(newTokenStorage)
|
||||
if (tokenValue && config.header) config.header.Authorization = tokenValue
|
||||
return http.request(config)
|
||||
return https.request(config)
|
||||
}
|
||||
} catch (err) {
|
||||
return Promise.reject(err)
|
||||
@@ -284,7 +298,7 @@ async function loginBySh(): Promise<boolean> {
|
||||
|
||||
// 请求函数
|
||||
const request = (config: any) => {
|
||||
return http.middleware(config)
|
||||
return https.middleware(config)
|
||||
}
|
||||
|
||||
export default request
|
||||
@@ -9,6 +9,12 @@ import { useRouter } from "./src/sheep"
|
||||
export default defineConfig(({ mode }) => {
|
||||
const { NODE_ENV, VITE_APP_DEV_SERVICE, VITE_APP_PROD_SERVICE, VITE_IMAGE_DEV_PATH } = loadEnv(mode, process.cwd(), "")
|
||||
const baseUrl = NODE_ENV == "development" ? VITE_APP_DEV_SERVICE : VITE_APP_PROD_SERVICE
|
||||
|
||||
// 生产环境强制 HTTPS 检查
|
||||
if (NODE_ENV === "production" && baseUrl && !baseUrl.startsWith("https://")) {
|
||||
throw new Error("生产环境必须使用 HTTPS 协议!请检查 VITE_APP_PROD_SERVICE 配置")
|
||||
}
|
||||
|
||||
return {
|
||||
build: {
|
||||
target: "es6",
|
||||
@@ -40,6 +46,8 @@ export default defineConfig(({ mode }) => {
|
||||
ws: true,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, ""),
|
||||
// 生产环境强制 HTTPS 验证
|
||||
...(NODE_ENV === "production" && { secure: true }),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user