This commit is contained in:
dzc committed 2025-08-28 19:31:58 +08:00
commit 2b7700a8d6
13 files changed
+249 -217

No files matched your search

+3 -2
View File
@@ -1,6 +1,7 @@
<script lang="ts">
import { getLaunchParam } from "@/sheep/utils/app"
import { getAppInfo } from "@@/utils/app"
import { APPENV } from "./sheep/constant"
interface TabbarItem {
pagePath: string
@@ -152,7 +153,7 @@ export default {
const appInfo = await getAppInfo()
// 浙政钉环境
if (appInfo == "zzd") {
if (appInfo == APPENV.ZZD) {
if (!launchParam) return uni.hideTabBar()
setTabbar(launchParam)
@@ -161,7 +162,7 @@ export default {
}
// 诗画环境
if (appInfo == "sh") {
if (appInfo == APPENV.SH) {
if (!launchParam) return uni.hideTabBar()
const userRole = uni.getStorageSync("userRole")
+169 -168
View File
@@ -1,206 +1,207 @@
<template>
<view class="notice-detail">
<view class="detail-header">
<view class="title">
<text v-if="detail.isTop === '1'" class="top-tag">置顶</text>
{{ detail.title }}
</view>
<view class="meta">
<text class="type-tag" :class="getTypeClass(detail.type)">{{ getTypeLabel(detail.type) }}</text>
<text class="time">{{ formatTime(detail.createTime) }}</text>
<text class="view-count">浏览 {{ detail.viewCount }}</text>
</view>
</view>
<view class="detail-content">
<rich-text :nodes="detail.description || ''"></rich-text>
</view>
<view v-if="detail.attachments && detail.attachments.length > 0" class="attachments">
<view class="attachment-title">附件</view>
<view
v-for="(file, index) in detail.attachments"
:key="index"
class="attachment-item"
@click="downloadFile(file)"
>
<text class="file-name">{{ file.name }}</text>
<text class="file-size">{{ formatFileSize(file.size) }}</text>
</view>
</view>
</view>
<view class="notice-detail">
<view class="detail-header">
<view class="title">
<text v-if="detail.isTop === '1'" class="top-tag">置顶</text>
{{ detail.title }}
</view>
<view class="meta">
<text class="type-tag" :class="getTypeClass(detail.type)">{{ getTypeLabel(detail.type) }}</text>
<text class="time">{{ formatTime(detail.createTime) }}</text>
<text class="view-count">浏览 {{ detail.viewCount }}</text>
</view>
</view>
<view class="detail-content">
<rich-text :nodes="nodes || ''"></rich-text>
</view>
<view v-if="detail.attachments && detail.attachments.length > 0" class="attachments">
<view class="attachment-title">附件</view>
<view v-for="(file, index) in detail.attachments" :key="index" class="attachment-item" @click="downloadFile(file)">
<text class="file-name">{{ file.name }}</text>
<text class="file-size">{{ formatFileSize(file.size) }}</text>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { noticeApi } from '@/sheep/api'
import { computed, ref } from "vue"
import { onLoad } from "@dcloudio/uni-app"
import { noticeApi } from "@/sheep/api"
const detail = ref<any>({})
const nodes = computed(() => {
return detail.value.description?.replaceAll(`<img src="/api`, `<img src="https://stgl.pj.gov.cn/api`)
})
function getTypeLabel(type: string) {
const typeMap: Record<string, string> = {
'1': '重要',
'2': '一般',
'3': '活动'
}
return typeMap[type] || '一般'
const typeMap: Record<string, string> = {
"1": "重要",
"2": "一般",
"3": "活动",
}
return typeMap[type] || "一般"
}
function getTypeClass(type: string) {
const classMap: Record<string, string> = {
'1': 'type-important',
'2': 'type-general',
'3': 'type-activity'
}
return classMap[type] || 'type-general'
const classMap: Record<string, string> = {
"1": "type-important",
"2": "type-general",
"3": "type-activity",
}
return classMap[type] || "type-general"
}
function formatTime(time: string) {
if (!time) return ''
return new Date(time).toLocaleString()
if (!time) return ""
return new Date(time).toLocaleString()
}
function formatFileSize(size: number) {
if (size < 1024) {
return size + 'B'
} else if (size < 1024 * 1024) {
return (size / 1024).toFixed(1) + 'KB'
} else {
return (size / (1024 * 1024)).toFixed(1) + 'MB'
}
if (size < 1024) {
return size + "B"
} else if (size < 1024 * 1024) {
return (size / 1024).toFixed(1) + "KB"
} else {
return (size / (1024 * 1024)).toFixed(1) + "MB"
}
}
function downloadFile(file: any) {
uni.downloadFile({
url: file.url,
success: (res) => {
if (res.statusCode === 200) {
uni.openDocument({
filePath: res.tempFilePath,
success: () => {
console.log('打开文档成功')
},
fail: () => {
uni.showToast({ title: '无法打开此文件', icon: 'none' })
}
})
}
},
fail: () => {
uni.showToast({ title: '下载失败', icon: 'none' })
}
})
uni.downloadFile({
url: file.url,
success: (res) => {
if (res.statusCode === 200) {
uni.openDocument({
filePath: res.tempFilePath,
success: () => {
console.log("打开文档成功")
},
fail: () => {
uni.showToast({ title: "无法打开此文件", icon: "none" })
},
})
}
},
fail: () => {
uni.showToast({ title: "下载失败", icon: "none" })
},
})
}
onLoad(async (options: any) => {
const id = options.id
if (!id) return
try {
// 增加浏览量
await noticeApi.addNoticeView(id)
// 获取详情
const response: any = await noticeApi.getNoticeDetail(id)
if (response.code === 0 && response.data) {
detail.value = response.data
}
} catch (error) {
console.error('获取公告详情失败:', error)
uni.showToast({ title: '获取详情失败', icon: 'none' })
}
const id = options.id
if (!id) return
try {
// 增加浏览量
await noticeApi.addNoticeView(id)
// 获取详情
const response: any = await noticeApi.getNoticeDetail(id)
if (response.code === 0 && response.data) {
detail.value = response.data
}
} catch (error) {
console.error("获取公告详情失败:", error)
uni.showToast({ title: "获取详情失败", icon: "none" })
}
})
</script>
<style lang="scss" scoped>
.notice-detail {
padding: 32rpx;
background: #f5f5f5;
min-height: 100vh;
padding: 32rpx;
background: #f5f5f5;
min-height: 100vh;
}
.detail-header {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 24rpx;
.title {
font-size: 36rpx;
font-weight: bold;
color: #333;
line-height: 1.4;
margin-bottom: 8rpx;
display: flex;
align-items: center;
}
.top-tag {
background: #ffebee;
color: #d32f2f;
font-size: 22rpx;
border-radius: 8rpx;
padding: 2rpx 12rpx;
margin-right: 12rpx;
}
.meta {
display: flex;
justify-content: space-between;
color: #999;
font-size: 24rpx;
margin-bottom: 16rpx;
}
.view-count {
margin-left: 24rpx;
}
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 24rpx;
.title {
font-size: 36rpx;
font-weight: bold;
color: #333;
line-height: 1.4;
margin-bottom: 8rpx;
display: flex;
align-items: center;
}
.top-tag {
background: #ffebee;
color: #d32f2f;
font-size: 22rpx;
border-radius: 8rpx;
padding: 12rpx 12rpx;
margin-right: 12rpx;
white-space: nowrap;
}
.meta {
display: flex;
justify-content: space-between;
color: #999;
font-size: 24rpx;
margin-bottom: 16rpx;
}
.view-count {
margin-left: 24rpx;
}
}
.detail-content {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 24rpx;
font-size: 30rpx;
color: #333;
line-height: 1.8;
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 24rpx;
font-size: 30rpx;
color: #333;
line-height: 1.8;
overflow: auto;
}
.attachments {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
.attachment-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
margin-bottom: 16rpx;
}
.attachment-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16rpx 0;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.file-name {
font-size: 28rpx;
color: #007aff;
flex: 1;
margin-right: 16rpx;
}
.file-size {
font-size: 24rpx;
color: #999;
}
}
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
.attachment-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
margin-bottom: 16rpx;
}
.attachment-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16rpx 0;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.file-name {
font-size: 28rpx;
color: #007aff;
flex: 1;
margin-right: 16rpx;
}
.file-size {
font-size: 24rpx;
color: #999;
}
}
}
</style>
</style>
+4 -2
View File
@@ -96,13 +96,15 @@ const toDetail = async (v: any) => {
<view class="sub-text-34">{{ v.name }}</view>
<view class="m-t-10rpx">
<view class="flex justify-between items-center">
<view>
<view class="flex-1">
<view v-if="v.phone" class="flex items-center">
<image class="w-24rpx h-24rpx" src="@/static/page/phone.svg" />
<view class="second-text-28 ml-1">{{ v.phone }}</view>
</view>
</view>
<tm-tag size="xs">{{ v.deptName.length > 5 ? v.deptName.substring(0, 5) + "..." : v.deptName }}</tm-tag>
<tm-tag class="max-w-160rpx" size="xs">
{{ v.deptName.length > 5 ? v.deptName.substring(0, 5) + "..." : v.deptName }}
</tm-tag>
</view>
<view class="second-text-24">
<view v-if="v.jobTime != ',,'" class="second-text-28">营业时间:</view>
+24 -13
View File
@@ -24,19 +24,24 @@
<view class="sub-text-34">{{ state.detail.name }}</view>
<view class="m-t-10rpx">
<view class="flex justify-between items-center">
<view>
<view v-if="state.detail.phone" class="flex items-center">
<view class="flex-1">
<view
v-if="state.detail.phone"
v-for="(item, index) in state.detail.phone.split('/')"
:key="index"
class="flex items-center"
>
<image class="w-24rpx h-24rpx" src="@/static/page/phone.svg" />
<view class="second-text-28 ml-1">{{ state.detail.phone?.slice(0, 11) }}</view>
<view class="second-text-28 ml-1">{{ item.trim() }}</view>
<view
class="second-text-20 bg-#f8f8f8 ml-1 p-0.3 flex items-center rounded-1 border border-solid border-b-coolGray"
@tap="doPhone"
@tap="doPhone(item)"
>
联系商家
</view>
</view>
</view>
<tm-tag size="xs">
<tm-tag class="max-w-160rpx" size="xs">
{{ state.detail.deptName?.length > 5 ? state.detail.deptName.substring(0, 5) + "..." : state.detail.deptName }}
</tm-tag>
</view>
@@ -132,13 +137,15 @@
import { reactive, nextTick } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { isEmpty } from "radash"
import { router } from "@@/index"
import { hooks, router } from "@@/index"
import { foodApi, merchantApi, payApi, shopApi } 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"
import ProductSku from "./components/product-sku.vue"
import { getAppInfo } from "@/sheep/utils/app"
import { APPENV } from "@/sheep/constant"
// 商品详情相关数据
const state = reactive<any>({
@@ -331,15 +338,19 @@ const submitPay = async () => {
}
// 联系商家
const doPhone = () => {
if (isEmpty(state.detail.phone)) {
uni.showToast({ title: "该商户没有预留电话", icon: "none" })
const doPhone = async (item: string) => {
if (isEmpty(item)) return uni.showToast({ title: "该商户没有预留电话", icon: "none" })
if ((await getAppInfo()) == APPENV.ZZD) {
;(window as any).dd
.callPhone({ phoneNum: item.trim() })
.then((res) => {})
.catch((err) => {})
return
}
if ((await getAppInfo()) == APPENV.SH) {
hooks.useMakePhoneCall(item.trim())
return
}
;(window as any).dd
.callPhone({ phoneNum: state.detail.phone.slice(0, 11) })
.then((res) => {})
.catch((err) => {})
}
</script>
+2 -2
View File
@@ -113,13 +113,13 @@ const toDetail = async (v: any) => {
</view>
<view class="m-t-10rpx">
<view class="flex justify-between items-center">
<view>
<view class="flex-1">
<view v-if="v.phone" class="flex items-center">
<image class="w-24rpx h-24rpx" src="@/static/page/phone.svg" />
<view class="second-text-28 ml-1">{{ v.phone }}</view>
</view>
</view>
<tm-tag size="xs">{{ v.deptName.length > 5 ? v.deptName.substring(0, 5) + "..." : v.deptName }}</tm-tag>
<tm-tag class="max-w-160rpx" size="xs">{{ v.deptName.length > 5 ? v.deptName.substring(0, 5) + "..." : v.deptName }}</tm-tag>
</view>
<view class="second-text-24">
<view v-if="v.jobTime != ',,'" class="second-text-28">营业时间:</view>
+3 -2
View File
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { APPENV } from "@/sheep/constant"
import { getAppInfo, getLaunchParam, isDevEnv } from "@/sheep/utils/app"
import { onShow } from "@dcloudio/uni-app"
@@ -57,7 +58,7 @@ const jumpByParam = async () => {
const launchParam = getLaunchParam() // 获取启动参数
// 浙政钉环境
if (appInfo == "zzd") {
if (appInfo == APPENV.ZZD) {
switch (launchParam) {
case "canteen": // 食堂
uni.setStorageSync("launchParam", launchParam)
@@ -87,7 +88,7 @@ const jumpByParam = async () => {
}
// 诗画环境
if (appInfo == "sh") {
if (appInfo == APPENV.SH) {
const userRole = uni.getStorageSync("userRole") // 获取用户角色
if (!launchParam) return uni.hideTabBar()
+4 -1
View File
@@ -27,7 +27,10 @@
:quickDate="[]"
drawerSize="900"
>
<view class="w-100rpx text-center second-text-28 text-30 mb-4rpx">筛选</view>
<view class="mb-6rpx flex items-center gap-1">
<image class="w-[30rpx] h-[30rpx]" src="@/static/page/filter.svg" />
<view class="text-center second-text-28 text-30 whitespace-nowrap">筛选</view>
</view>
</tm-between-time>
</view>
</template>
+4 -3
View File
@@ -40,7 +40,7 @@
</view>
</view>
<view class="h-30rpx" />
<view v-if="state.appInfo == 'sh'" class="flex-col-center m-t-40rpx m-x-30rpx">
<view v-if="state.appInfo == APPENV.SH" class="flex-col-center m-t-40rpx m-x-30rpx">
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff" @tap="doChangeRole">
切换角色
</view>
@@ -68,6 +68,7 @@ import { getAppInfo } from "@@/utils/app"
import { hooks, router, constant } from "@@/index"
import { userApi } from "@@/api"
import { isDevEnv } from "@/sheep/utils/app"
import { APPENV } from "@/sheep/constant"
const state = reactive<{
wallet: any[]
@@ -300,11 +301,11 @@ const scanCode = {
uni.showModal({ content: "开发环境不是浙政钉和诗画环境,扫码仅供测试,收款商家为测试商户超市" })
return testQrCode
} else if (state.appInfo == "zzd") {
} else if (state.appInfo == APPENV.ZZD) {
// 浙政钉扫码
const res: any = await (window as any).dd.scan({ type: "all" })
return res.text
} else if (state.appInfo == "sh") {
} else if (state.appInfo == APPENV.SH) {
// 诗画扫码
const res = await (window as any).CP2.showScanQr({ needResult: 1 })
if (res.code == 1) return res.data.qrCode
+19 -20
View File
@@ -93,38 +93,35 @@ const doRefund = (v: any) => {
@scroll="pageHook.scroll"
>
<template #top>
<view class="h-30rpx" />
<view class="bg-white rounded-16rpx px-20rpx">
<view class="bg-white rounded-16rpx px-20rpx my-20rpx">
<z-tabs :list="data.tabs" @change="doTab" />
</view>
</template>
<view class="h-20rpx" />
<view class="flex-col justify-center card p-3 m-b-20rpx" v-for="(v, i) in pageHook.list" :key="i">
<view class="flex-row items-center">
<image class="w-110rpx h-110rpx rounded-8rpx" :src="filePath(v.logoPic)" mode="aspectFill" />
<view class="flex-1 m-l-20rpx">
<view class="flex-row">
<image class="w-80rpx h-80rpx rounded-8rpx" :src="filePath(v.logoPic)" mode="aspectFill" />
<view class="flex-1 ml-20rpx">
<view class="flex-row items-center justify-between">
<view class="flex-row items-center">
<view class="main-text-28">{{ v.merchantName }}</view>
<!-- <view class="m-l-15rpx color-#999 font-size-26rpx! iconfont icon-youjiantou" /> -->
</view>
<view class="main-text-28">{{ v.merchantName }}</view>
<!-- <view class="m-l-15rpx color-#999 font-size-26rpx! iconfont icon-youjiantou" /> -->
<tm-tag v-if="v.orderStatus == 1" color="info" size="xs">未支付</tm-tag>
<tm-tag v-if="v.orderStatus == 5" color="success" size="xs">已支付</tm-tag>
<tm-tag v-if="v.orderStatus == 10" color="error" size="xs">售后</tm-tag>
<tm-tag v-if="v.orderStatus == 20" size="xs">已核销</tm-tag>
<tm-tag v-if="v.orderStatus == 30" color="info" size="xs">已取消(智能柜)</tm-tag>
</view>
<view class="flex-row items-center justify-between m-t-5rpx">
<view class="flex-row items-center justify-between mt-5rpx">
<view class="second-text-20">
<text>下单时间</text>
<text class="m-l-10rpx">{{ hooks.useTime(v.createTime, "{y}-{m}-{d} {h}:{i}:{s}") }}</text>
</view>
<view class="second-text-22">{{ hooks.useEnum("hardwareRefundStatusEnum", v.refundStatus) }}</view>
</view>
<view class="flex-row items-center justify-between m-t-5rpx">
<view class="sub-text-24">
<view class="flex-row items-center justify-end mt-5rpx">
<!-- <view class="sub-text-24">
<text v-if="v.payType == '9'">{{ v.productDesc?.replaceAll(";", "\;\n") }}</text>
</view>
</view> -->
<view class="main-text-30 whitespace-nowrap color-red! text-right">¥ {{ hooks.useMoney(v.totalPrice) }}</view>
</view>
</view>
@@ -132,22 +129,24 @@ const doRefund = (v: any) => {
<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>
<scroll-view v-if="['5', '20'].includes(v.orderStatus) && ['1', '2'].includes(v.orderType)" scroll-x>
<view class="flex-row">
<view class="flex-col justify-start items-center mr-20rpx" v-for="(pV, pI) in v.carList" :key="pI">
<image v-if="v.payType == '9'" class="w-80rpx h-80rpx rounded-8rpx" :src="pV.productPic" mode="aspectFill" />
<image v-else class="w-80rpx h-80rpx rounded-8rpx" :src="filePath(pV.productPic)" mode="aspectFill" />
<view class="mt-10rpx sub-text-24 text-center min-w-120rpx max-w-200rpx">{{ pV.productName }}</view>
</view>
</view>
</scroll-view>
</view>
<view
v-if="v.carList?.length && ['5', '20'].includes(v.orderStatus) && v.orderType == 2"
v-if="v.carList?.length && ['5', '20'].includes(v.orderStatus) && ['1', '2'].includes(v.orderType)"
class="w-1/5 second-text-26 truncate text-center"
>
共 {{ v.carList.length }} 件
</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>
+7
View File
@@ -0,0 +1,7 @@
/**
* ZZD:浙政钉 | SH:诗画
*/
export default {
ZZD: "zzd",
SH: "sh",
}
+2
View File
@@ -1,3 +1,5 @@
export { default as serviceEnum } from "./service-enum"
export { default as storageConstant } from "./storage-constant"
export { default as APPENV } from "./app-env"
+5 -2
View File
@@ -1,4 +1,7 @@
// app运行环境
import { APPENV } from "../constant"
// export default () => import.meta.env.VITE_DEV_ENV === "dev"
export const isDevEnv = (): boolean => process.env.NODE_ENV === "development"
@@ -14,11 +17,11 @@ export const getAppInfo = async (): Promise<string> => {
// 浙政钉环境
if (res.code === 0) {
console.log("环境:浙政钉-zzd")
return "zzd"
return APPENV.ZZD
} else {
// 诗画环境
console.log("环境:诗画浦江-sh")
return "sh"
return APPENV.SH
}
} catch (error) {
return "unknown"
+3 -2
View File
@@ -4,6 +4,7 @@ import { isEmpty } from "radash"
import { getAppInfo, isDevEnv } from "@@/utils/app"
import { userApi } from "../api"
import showVerifyDeviceImpModel from "../components/verifyDeviceImpModel/verifyDeviceImpModel"
import { APPENV } from "../constant"
const TOKEN_KEY = constant.storageConstant.TOKEN
const USER_INFO_KEY = constant.storageConstant.USERINFO
@@ -210,9 +211,9 @@ async function refreshToken(): Promise<any> {
const autoLogin = async (): Promise<boolean> => {
const appInfo = await getAppInfo()
// 浙政钉环境
if (appInfo == "zzd") return await loginByZzd()
if (appInfo == APPENV.ZZD) return await loginByZzd()
// 诗画环境
if (appInfo == "sh") return await loginBySh()
if (appInfo == APPENV.SH) return await loginBySh()
uni.showToast({ title: "app环境错误", icon: "none" })
return false