This commit is contained in:
feie9456 2025-09-29 10:26:26 +08:00
commit 15d5b70973
49 changed files with 5482 additions and 0 deletions

21
.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
*.local
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

1698
bun.lock Normal file

File diff suppressed because it is too large Load Diff

20
index.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

68
package.json Normal file
View File

@ -0,0 +1,68 @@
{
"name": "uni-preset-vue",
"version": "0.0.0",
"scripts": {
"dev:custom": "uni -p",
"dev:h5": "uni",
"dev:h5:ssr": "uni --ssr",
"dev:mp-alipay": "uni -p mp-alipay",
"dev:mp-baidu": "uni -p mp-baidu",
"dev:mp-jd": "uni -p mp-jd",
"dev:mp-kuaishou": "uni -p mp-kuaishou",
"dev:mp-lark": "uni -p mp-lark",
"dev:mp-qq": "uni -p mp-qq",
"dev:mp-toutiao": "uni -p mp-toutiao",
"dev:mp-weixin": "uni -p mp-weixin",
"dev:mp-xhs": "uni -p mp-xhs",
"dev:quickapp-webview": "uni -p quickapp-webview",
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
"build:custom": "uni build -p",
"build:h5": "uni build",
"build:h5:ssr": "uni build --ssr",
"build:mp-alipay": "uni build -p mp-alipay",
"build:mp-baidu": "uni build -p mp-baidu",
"build:mp-jd": "uni build -p mp-jd",
"build:mp-kuaishou": "uni build -p mp-kuaishou",
"build:mp-lark": "uni build -p mp-lark",
"build:mp-qq": "uni build -p mp-qq",
"build:mp-toutiao": "uni build -p mp-toutiao",
"build:mp-weixin": "uni build -p mp-weixin",
"build:mp-xhs": "uni build -p mp-xhs",
"build:quickapp-webview": "uni build -p quickapp-webview",
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
"type-check": "vue-tsc --noEmit"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-4030620241128001",
"@dcloudio/uni-app-harmony": "3.0.0-4030620241128001",
"@dcloudio/uni-app-plus": "3.0.0-4030620241128001",
"@dcloudio/uni-components": "3.0.0-4030620241128001",
"@dcloudio/uni-h5": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-alipay": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-baidu": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-jd": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-kuaishou": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-lark": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-qq": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-toutiao": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-weixin": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-xhs": "3.0.0-4030620241128001",
"@dcloudio/uni-quickapp-webview": "3.0.0-4030620241128001",
"vue": "^3.4.21",
"vue-i18n": "^9.1.9"
},
"devDependencies": {
"@dcloudio/types": "^3.4.8",
"@dcloudio/uni-automator": "3.0.0-4030620241128001",
"@dcloudio/uni-cli-shared": "3.0.0-4030620241128001",
"@dcloudio/uni-stacktracey": "3.0.0-4030620241128001",
"@dcloudio/vite-plugin-uni": "3.0.0-4030620241128001",
"@vue/tsconfig": "^0.1.3",
"@vue/runtime-core": "^3.4.21",
"typescript": "^4.9.4",
"vite": "5.2.8",
"vue-tsc": "^1.0.24"
}
}

10
shims-uni.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
/// <reference types='@dcloudio/types' />
import 'vue'
declare module '@vue/runtime-core' {
type Hooks = App.AppInstance & Page.PageInstance;
interface ComponentCustomOptions extends Hooks {
}
}

13
src/App.vue Normal file
View File

@ -0,0 +1,13 @@
<script setup lang="ts">
import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
onLaunch(() => {
console.log("App Launch");
});
onShow(() => {
console.log("App Show");
});
onHide(() => {
console.log("App Hide");
});
</script>
<style></style>

112
src/config.js Normal file
View File

@ -0,0 +1,112 @@
export default {
// baseUrl: "http://localhost:8088",
// picturePath: "http://localhost:8088/DBfile",
//baseUrl: "http://10.3.1.212:8089",
//picturePath: "http://47.92.206.121/DBfile",
//baseUrl: "https://zhukangservice.info",
baseUrl: "https://zhukangservice.info/StretcherAPI",
picturePath: "https://zhukangservice.info/StretcherAPI/DBfile",
// 数据字典获取URL
dataDictionaryUrl: "/API/DataDictionary/getDataDictionary/",
defaultHospId: "40288083894a5a3801898bf9db146c99",
defaultOpenId: "10000000000000000000000000000001",
urls: {
// 登录
//login: "/User/login",
login: "/API/stretchertransport/login",
// 退出
logout: "/API/stretchertransport/logout",
// 查询微信用户的OpenID
queryOpenId: "/API/stretchertransport/weixin/queryOpenId/",
//查询全部
queryMedicineNewTask: "/API/drugtransport/list",
//获取网格列表
getGridFromRedis: "/API/stretchertransport/grid/redis/list",
// 获取APP滑动广告图片文件路径
getAppSlipPicUrl: "/API/stretchertransport/appslippic/get",
//获取指定运单ID或运单号查找已接单的担架运单完整信息
getStretcherTransportTaskById: "/API/stretchertransport/getbyid/",
//生成订单微信支付二维码
getStretcherTransportPayQRcode: "/API/stretchertransport/pay/QRcode/create",
// 获取担架运送收费标准图片文件路径
getStretcherTransportFeeImageUrl: "/API/stretchertransport/fee/pic/get",
// 获取担架运送收费标准图片文件路径
getNewWaybillPromptWaveUrl: "/API/stretchertransport/newwaybillprompt/wav/get",
//查询担架运送新任务列表
queryStretcherNewTask: "/API/stretchertransport/redis/list",
//查询担架运单列表
queryStretcherTask: "/API/stretchertransport/list",
//从redis中获取指定条件的接单担架运单列表
queryStretcherReceiptTask: "/API/stretchertransport/redis/receipt/list",
//担架运送接单
receiptStretcherNewTask: "/API/stretchertransport/receipt",
//启动担架运单
startStretcherTransport: "/API/stretchertransport/starttransport",
//完工担架运单
finishStretcherTransport: "/API/stretchertransport/finish",
//完工担架运单
confirmStretcherPay: "/API/stretchertransport/confirmpay",
//评价担架运单服务
evaluateStretcherTransport: "/API/stretchertransport/evaluate",
// 药品运送接单
receiptMedicineNewTask: "/API/drugtransport/receipt",
// 分页查询药品工单
pageQueryMedicineTask: "/API/drugtransport/page",
// 开始运送
startMedicineTransport: "/API/drugtransport/starttransport/",
// 完单图片上传
unloadPic: "/API/drugtransport/finishpic/upload/",
// 完单签字图片上传
unloadSign: "/API/drugtransport/signpic/upload/",
// 完成要药品运送工单
completeMedicineTask: "/API/drugtransport/finish",
// 获取指定工单id
getMedicineTaskById: "/API/drugtransport/getbyid/",
// 取消药品运单
cancelMedicineTask: "/API/drugtransport/cancel",
// 查询药品类型列表
getMedicineType: "/API/drugtransport/getdsordertype",
// 查询全部待接单标本
querySampleNewTask: "/API/specimentransport/specimen/list",
// 分页查询待接单标本
pageQuerySampleNewTask: "/API/specimentransport/specimen/page",
// 查询全部标本运单
querySampleTasksheet: "/API/specimentransport/list",
// 分页查询标本运单
pageQuerySampleTasksheet: "/API/specimentransport/page",
// 核验待运送标本
receiptSampleNewTask: "/API/specimentransport/verificationSpecimens/",
// 创建标本运单
createSampleTaskSheet: "/API/specimentransport/create",
// 分页查询药品工单
pageQueryAcceptedSampleTask: "/API/specimentransport/page",
// 获取指定工单id
getSampleTaskById: "/API/specimentransport/getbyid/",
// 取消标本运单
cancelSample: "/API/specimentransport/cancel",
// 标本完单图片上传
unloadSpecimentPic: "/API/specimentransport/finishpic/upload/",
},
};

View File

@ -0,0 +1,8 @@
// 药品运送
export const MEDICINE_CONVEY = new Set(['026', '0261']);
// 标本运送
export const SAMPLE_CONVEY = new Set(['027', '0271']);
// 担架运送
export const STRETCHER_CONVEY = new Set(['028', '0281']);

View File

@ -0,0 +1,5 @@
export const APPLIED = '01'; // 申请
export const DISPATCH = '02' //派工
export const ACCEPTED = '03'; // 接单
export const FINISHED = '06'; // 完成
export const TRANSPORT = '11' // 运送

47
src/constant/taskType.js Normal file
View File

@ -0,0 +1,47 @@
const taskTypeMapping = {
'药品运送': '031',
'异类药品运单': '0310',
'药品交接配送': '0311',
'药房退药确认': '0312',
'药房退药驳回': '0313',
'病区发起退药': '0314',
'病区撤销退药': '0315',
'取消药品运单': '0319',
'标本运送': '032',
'031': '药品运送',
'0310': '异类药品运单',
'0311': '药品交接配送',
'0312': '药房退药确认',
'0313': '药房退药驳回',
'0314': '病区发起退药',
'0315': '病区撤销退药',
'0319': '取消药品运单',
'032': '标本运送',
MEDICINE_CONVEY: '药品运送',
SAMPLE_CONVEY: '标本运送',
MEDICINE_NEW_CONVEY: '药品交接配送',
MEDICINE_RETURN: '病区发起退药'
}
export default taskTypeMapping

8
src/env.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}

8
src/main.ts Normal file
View File

@ -0,0 +1,8 @@
import { createSSRApp } from "vue";
import App from "./App.vue";
export function createApp() {
const app = createSSRApp(App);
return {
app,
};
}

73
src/manifest.json Normal file
View File

@ -0,0 +1,73 @@
{
"name" : "卓越智慧配送",
"appid" : "__UNI__AF09C81",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "3",
"locale" : "zh-Hans"
}

85
src/pages.json Normal file
View File

@ -0,0 +1,85 @@
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "卓越智慧配送"
}
},
{
"path": "pages/login/index",
"style": {
"navigationBarTitleText": "登录"
}
},
{
"path": "pages/stretcher/apply/index",
"style": {
"navigationBarTitleText": "担架运送申请"
}
},
{
"path": "pages/stretcher/apply-agent/index",
"style": {
"navigationBarTitleText": "担架运送代申请"
}
},
{
"path": "pages/message/index",
"style": {
"navigationBarTitleText": "消息"
}
},
{
"path": "pages/task/index",
"style": {
"navigationBarTitleText": "任务"
}
},
{
"path": "pages/mine/index",
"style": {
"navigationBarTitleText": "我的"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "卓越智慧配送",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"tabBar": {
"color": "#666666",
"selectedColor": "#007AFF",
"backgroundColor": "#ffffff",
"borderStyle": "black",
"height": "64px",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "static/icons/home.png",
"selectedIconPath": "static/icons/home.png"
},
{
"pagePath": "pages/message/index",
"text": "消息",
"iconPath": "static/icons/messages.png",
"selectedIconPath": "static/icons/messages.png"
},
{
"pagePath": "pages/task/index",
"text": "任务",
"iconPath": "static/icons/tasks.png",
"selectedIconPath": "static/icons/tasks.png"
},
{
"pagePath": "pages/mine/index",
"text": "我的",
"iconPath": "static/icons/profile.png",
"selectedIconPath": "static/icons/profile.png"
}
]
}
}

333
src/pages/index/index.vue Normal file
View File

@ -0,0 +1,333 @@
<template>
<scroll-view scroll-y class="page">
<!-- 顶部欢迎与Logo -->
<view class="header card">
<view class="header-left">
<text class="hello">您好欢迎使用</text>
<text class="app-name">卓越智慧配送</text>
<text class="sub">高效 · 可视 · 可追溯</text>
</view>
<view class="header-right">
<image class="logo" src="/static/logo.png" mode="aspectFit" />
<button v-if="!isLoggedIn" class="login-btn" @tap="goLogin">登录</button>
<view v-else class="user-box">
<text class="user-name">{{ staff?.name || '已登录' }}</text>
<button class="logout-btn" @tap="logout">退出</button>
</view>
</view>
</view>
<!-- 顶部滑动轮播图 -->
<view class="swiper-card card">
<swiper
class="swiper"
:current="swiperIndex"
@change="changeSwiper"
previous-margin="50rpx"
next-margin="50rpx"
:indicator-dots="true"
indicator-color="rgba(51,51,51,0.25)"
indicator-active-color="#4b7aff"
autoplay
interval="3000"
circular
>
<swiper-item v-for="(img, idx) in swiperImgs" :key="idx">
<view class="swiper-item-wrap" :class="{ 'swiper-scale': swiperIndex !== idx }">
<image class="swiper-img" :src="img" mode="aspectFill" />
</view>
</swiper-item>
</swiper>
</view>
<!-- 主操作 -->
<view class="ops">
<view class="op-btn" v-show="isPatient || isDoctor" @tap="goStretcherApply">
<image class="op-icon" src="/static/icons/stretcher-loan.png" />
<text class="op-text">担架申请</text>
</view>
<view class="op-btn" v-show="isPatient || isDoctor || isRider" @tap="goStretcherApplyFor">
<image class="op-icon" src="/static/icons/stretcher-temp-loan.png" />
<text class="op-text">担架代申请</text>
</view>
</view>
<!-- 资产管理可折叠卡片明确从属关系 -->
<view class="section-card card">
<view class="section-header" @tap="toggleAsset">
<view class="section-left">
<image class="section-icon" src="/static/icons/asset-management.png" />
<view class="section-text">
<text class="section-title-text">资产管理</text>
<text class="section-sub">固定资产借用盘点扫码易耗品</text>
</view>
</view>
<view class="chevron" :class="{ open: assetOpen }"></view>
</view>
<view class="asset-grid" v-show="assetOpen">
<view class="grid-item" v-for="item in assetItems" :key="item.key" @tap="handleAsset(item.key)">
<image class="grid-icon" :src="item.icon" />
<text class="grid-text">{{ item.text }}</text>
</view>
</view>
</view>
<!-- 公告担架收费标准 -->
<view class="section-title">
<text>公告 · 担架收费标准</text>
</view>
<view class="notice card" @tap="previewCharge">
<image class="notice-img" src="/static/stretcher-charge-standard.png" mode="widthFix" />
<text class="notice-tip">点击可放大查看</text>
</view>
</scroll-view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
// @ts-ignore JS
import queryService from '@/service/queryService.js'
// @ts-ignore JS
import config from '@/config.js'
// @ts-ignore JS
import * as staffRole from '@/constant/staffRole.js'
// @ts-ignore JS
import util from '@/utils/util.js'
//
const assetOpen = ref(true)
//
const assetItems = [
{ key: 'fixed', text: '固定资产', icon: '/static/icons/fixed-assets.png' },
{ key: 'mine', text: '我的借用', icon: '/static/icons/my-loans.png' },
{ key: 'inventory', text: '资产盘点', icon: '/static/icons/asset-inventory.png' },
{ key: 'scan', text: '扫码查看信息', icon: '/static/icons/scan-to-view-info.png' },
{ key: 'plan', text: '易耗品领用计划', icon: '/static/icons/consumables-plan.png' },
{ key: 'consumable', text: '易耗品盘点', icon: '/static/icons/consumables-inventory.png' }
]
//
const swiperIndex = ref(0)
const swiperImgs = ref<string[]>([
'/static/logo.png',
'/static/logo.png',
'/static/logo.png'
])
function changeSwiper(e: any) {
swiperIndex.value = e.detail.current || 0
}
async function loadSwiperImages() {
try {
const res = await queryService.getAppSlipPicUrl()
if (res?.data?.code === 0) {
const dataStr: string = res?.data?.data || ''
const pics = dataStr ? dataStr.split(';').filter(Boolean) : []
if (pics.length >= 3) {
//
const last3 = pics.slice(-3)
swiperImgs.value = last3.map(p => `${config.picturePath}${p}`)
} else if (pics.length > 0) {
// 3
const filled: string[] = []
while (filled.length < 3) {
const next = pics[filled.length % pics.length]
filled.push(`${config.picturePath}${next}`)
}
swiperImgs.value = filled
}
}
} catch (err) {
//
console.warn('加载轮播图失败:', err)
}
}
onMounted(() => {
loadSwiperImages()
syncLoginState()
})
//
const isLoggedIn = ref(false)
const staff = ref<any | null>(null)
const isWeixin = ref(false)
const isPatient = ref(true)
const isDoctor = ref(false)
const isRider = ref(false)
function syncLoginState() {
staff.value = uni.getStorageSync('staff') || null
isLoggedIn.value = !!staff.value
//
// @ts-ignore
isWeixin.value = process?.env?.VUE_APP_PLATFORM === 'mp-weixin'
// /
if (staff.value && Array.isArray(staff.value.role_ids)) {
const set = staffRole.STRETCHER_CONVEY
const hasRider = staff.value.role_ids.some((r: string) => set.has(r))
isRider.value = hasRider
isPatient.value = !hasRider
isDoctor.value = !hasRider
} else {
isRider.value = false
isPatient.value = true
isDoctor.value = false
}
}
function goLogin() {
uni.navigateTo({ url: '/pages/login/index' })
}
function logout() {
uni.setStorageSync('staff', null)
uni.setStorageSync('token', null)
isLoggedIn.value = false
staff.value = null
isRider.value = false
isPatient.value = true
isDoctor.value = false
uni.showToast({ title: '已退出登录', icon: 'none' })
}
//
function goStretcherApply() {
uni.navigateTo({ url: '/pages/stretcher/apply/index' })
}
function goStretcherApplyFor() {
uni.navigateTo({ url: '/pages/stretcher/apply-agent/index' })
}
function toggleAsset() {
assetOpen.value = !assetOpen.value
}
function handleAsset(key: string) {
uni.showToast({ title: `打开:${assetItems.find(i => i.key === key)?.text}`, icon: 'none' })
}
function previewCharge() {
uni.previewImage({
urls: ['/static/stretcher-charge-standard.png']
})
}
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f5f7fb;
}
.card {
background: #fff;
border-radius: 20rpx;
box-shadow: 0 8rpx 24rpx rgba(0,0,0,0.06);
}
.header {
margin: 32rpx;
padding: 32rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.header-left {
display: flex;
flex-direction: column;
gap: 10rpx;
}
.hello { color: #7a7a7a; font-size: 26rpx; }
.app-name { color: #111; font-size: 40rpx; font-weight: 600; }
.sub { color: #9a9aa0; font-size: 24rpx; }
.logo { width: 120rpx; height: 120rpx; }
.header-right { display: flex; align-items: center; gap: 12rpx; }
.login-btn, .logout-btn { padding: 10rpx 20rpx; background: #3a5ddd; color: #fff; border-radius: 10rpx; font-size: 24rpx; }
.logout-btn { background: #ef5350; }
.user-box { display: flex; align-items: center; gap: 12rpx; }
.user-name { font-size: 24rpx; color: #333; }
/* 轮播卡片 */
.swiper-card { margin: 0 32rpx 24rpx; padding: 12rpx; }
.swiper { width: 100%; height: 300rpx; }
.swiper-item-wrap {
width: 100%; height: 100%; border-radius: 24rpx; overflow: hidden;
transition: transform 0.25s ease, box-shadow 0.25s ease;
box-shadow: 0 6rpx 18rpx rgba(0,0,0,0.08);
}
.swiper-scale { transform: scale(0.95); box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.06); }
.swiper-img { width: 100%; height: 100%; display: block; }
.ops {
margin: 0 32rpx 24rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24rpx;
}
.op-btn {
background: linear-gradient(180deg, #f9fbff 0%, #ffffff 100%);
border-radius: 20rpx;
padding: 28rpx 12rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12rpx;
box-shadow: 0 6rpx 16rpx rgba(0,0,0,0.04);
}
.op-icon { width: 80rpx; height: 80rpx; }
.op-text { font-size: 26rpx; color: #333; }
.section-card { margin: 0 32rpx 24rpx; }
.section-header {
padding: 24rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.section-left { display: flex; align-items: center; gap: 16rpx; }
.section-icon { width: 48rpx; height: 48rpx; }
.section-text { display: flex; flex-direction: column; }
.section-title-text { font-size: 30rpx; color: #222; font-weight: 600; }
.section-sub { font-size: 22rpx; color: #9a9aa0; }
.chevron {
width: 24rpx;
height: 24rpx;
border-right: 4rpx solid #b5b6ba;
border-bottom: 4rpx solid #b5b6ba;
transform: rotate(45deg);
transition: transform 0.2s ease;
}
.chevron.open { transform: rotate(135deg); }
.asset-grid {
padding: 0 24rpx 24rpx;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20rpx;
}
.grid-item {
background: #fafbff;
border-radius: 16rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 24rpx 12rpx;
gap: 10rpx;
}
.grid-icon { width: 64rpx; height: 64rpx; }
.grid-text { font-size: 24rpx; color: #444; text-align: center; line-height: 1.4; }
.section-title {
padding: 0 32rpx 12rpx;
color: #666;
font-size: 26rpx;
}
.notice {
margin: 0 32rpx 32rpx;
padding: 20rpx;
text-align: center;
}
.notice-img { width: 100%; border-radius: 12rpx; }
.notice-tip { display: block; color: #9a9aa0; font-size: 22rpx; margin-top: 12rpx; }
</style>

128
src/pages/login/index.vue Normal file
View File

@ -0,0 +1,128 @@
<template>
<view class="page">
<view class="card header">
<text class="title">账号登录</text>
<text class="sub">系统仅供内部使用请使用内部账号密码登录</text>
</view>
<view class="card form">
<view class="field">
<text class="label">用户名/电话</text>
<input class="input" v-model="loginRequest.username" placeholder="请输入用户名或电话" :focus="usernameFocus" />
</view>
<view class="field">
<text class="label">密码</text>
<input class="input" v-model="loginRequest.password" placeholder="请输入密码" password />
</view>
<view class="actions">
<button class="btn primary" @tap="onLogin">登录</button>
<button class="btn" @tap="onCancel">取消</button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted } from 'vue'
// @ts-ignore
import md5 from '@/utils/md5.js'
// @ts-ignore
import util from '@/utils/util.js'
// @ts-ignore
import config from '@/config.js'
const usernameFocus = ref(true)
const loginRequest = reactive({
username: '',
password: '',
typecode: '007'
})
onMounted(() => {
//
const userName = uni.getStorageSync('userName')
const userPsw = uni.getStorageSync('userPsw')
if (userName && userPsw) {
loginRequest.username = userName
loginRequest.password = userPsw
}
})
async function onLogin() {
if (!loginRequest.username) {
uni.showToast({ title: '用户名不能为空', icon: 'none' })
return
}
if (!loginRequest.password) {
uni.showToast({ title: '密码不能为空', icon: 'none' })
return
}
//
uni.setStorageSync('userName', loginRequest.username)
uni.setStorageSync('userPsw', loginRequest.password)
uni.showToast({ title: '登录中', icon: 'loading', duration: 2000 })
//
uni.setStorageSync('staff', null)
try {
const res = await util.request({
url: config.urls.login,
method: 'POST',
data: {
username: loginRequest.username,
password: md5.hexMD5(loginRequest.username + loginRequest.password),
typecode: loginRequest.typecode
}
})
if (res?.data?.code === 0) {
const staff = res.data.data.userLoginInfo
// role
uni.setStorageSync('staff', { ...staff, role: staff.role_ids })
uni.setStorageSync('token', res.data.data.token)
uni.showToast({ title: '登录成功', icon: 'success', duration: 1000 })
// tabBar
setTimeout(() => {
uni.switchTab({ url: '/pages/index/index' })
}, 300)
} else {
uni.showToast({ title: res?.data?.msg || '登录失败', icon: 'error', duration: 3000 })
}
} catch (err:any) {
// util.request
console.warn('login error', err)
}
}
function onCancel() {
uni.switchTab({ url: '/pages/index/index' })
}
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f5f7fb;
padding: 24rpx 0 40rpx;
}
.card {
background: #fff;
border-radius: 20rpx;
margin: 0 32rpx 24rpx;
box-shadow: 0 8rpx 24rpx rgba(0,0,0,0.06);
}
.header { padding: 28rpx 24rpx; }
.title { font-size: 36rpx; color: #111; font-weight: 600; }
.sub { display: block; margin-top: 8rpx; font-size: 24rpx; color: #888; }
.form { padding: 20rpx 24rpx; }
.field { margin-bottom: 20rpx; }
.label { display: block; color: #666; font-size: 26rpx; margin-bottom: 10rpx; }
.input {
width: 100%; height: 80rpx; padding: 0 20rpx;
border: 2rpx solid #e6e8ef; border-radius: 12rpx; background: #fcfdff;
}
.actions { display: flex; gap: 20rpx; margin-top: 8rpx; }
.btn { flex: 1; height: 84rpx; border-radius: 14rpx; background: #eef2ff; color: #3a5ddd; }
.btn.primary { background: #3a5ddd; color: #fff; }
</style>

View File

@ -0,0 +1,32 @@
<template>
<view class="page">
<view class="empty">
<image src="/static/icons/messages.png" class="icon" />
<text>暂无消息</text>
</view>
</view>
</template>
<script setup lang="ts">
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f7f8fa;
}
.empty {
height: 60vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #999;
font-size: 28rpx;
}
.icon {
width: 120rpx;
height: 120rpx;
margin-bottom: 24rpx;
}
</style>

View File

@ -0,0 +1,32 @@
<template>
<view class="page">
<view class="empty">
<image src="/static/icons/messages.png" class="icon" />
<text>暂无消息</text>
</view>
</view>
</template>
<script setup lang="ts">
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f7f8fa;
}
.empty {
height: 60vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #999;
font-size: 28rpx;
}
.icon {
width: 120rpx;
height: 120rpx;
margin-bottom: 24rpx;
}
</style>

75
src/pages/mine/index.vue Normal file
View File

@ -0,0 +1,75 @@
<template>
<view class="page">
<view class="profile">
<image class="avatar" src="/static/logo.png" />
<view class="info" v-if="!isLoggedIn">
<text class="name">未登录</text>
<text class="sub" @tap="goLogin">点击登录/注册</text>
</view>
<view class="info" v-else>
<text class="name">{{ staff?.name || '已登录' }}</text>
<text class="sub">工号{{ staff?.job_no || '-' }}</text>
<view class="actions">
<button class="btn" @tap="logout">退出登录</button>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const isLoggedIn = ref(false)
const staff = ref<any | null>(null)
function sync() {
staff.value = uni.getStorageSync('staff') || null
isLoggedIn.value = !!staff.value
}
function goLogin() {
uni.navigateTo({ url: '/pages/login/index' })
}
function logout() {
uni.setStorageSync('staff', null)
uni.setStorageSync('token', null)
sync()
uni.showToast({ title: '已退出登录', icon: 'none' })
}
onMounted(sync)
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f7f8fa;
}
.profile {
display: flex;
align-items: center;
gap: 24rpx;
padding: 40rpx 32rpx;
background: #fff;
}
.avatar {
width: 96rpx;
height: 96rpx;
border-radius: 50%;
}
.info {
display: flex;
flex-direction: column;
}
.name {
font-size: 32rpx;
color: #333;
}
.sub {
font-size: 24rpx;
color: #999;
}
.actions { margin-top: 16rpx; }
.btn { padding: 12rpx 20rpx; background: #ef5350; color: #fff; border-radius: 10rpx; font-size: 24rpx; }
</style>

View File

@ -0,0 +1,640 @@
<template>
<view class="page">
<view class="card">
<view class="title">担架运送代申请</view>
<!-- 代申报科室 -->
<view class="form-item">
<text class="label required">代申报科室</text>
<picker mode="multiSelector" :range="needDeptMultiRange"
:value="needDeptMultiValue" @columnchange="onNeedDeptColumnChange"
@change="onNeedDeptChange">
<view class="picker-value"
:class="{ placeholder: !form.needcontactDept }">
{{ currentNeedDeptName || '请选择代申报科室' }}
</view>
</picker>
</view>
<!-- 代申报人 -->
<view class="form-item">
<text class="label required">代申报人</text>
<input class="input" type="text" v-model="form.needcontact"
placeholder="请输入代申报人" />
</view>
<!-- 代申报电话 -->
<view class="form-item">
<text class="label required">代申报电话</text>
<input class="input" type="number" maxlength="20"
v-model="form.needcontactTel" placeholder="请输入代申报电话" />
</view>
<!-- 申报科室 -->
<view class="form-item">
<text class="label required">申报科室</text>
<picker mode="multiSelector" :range="decDeptMultiRange"
:value="decDeptMultiValue" @columnchange="onDecDeptColumnChange"
@change="onDecDeptChange">
<view class="picker-value" :class="{ placeholder: !form.decDept }">
{{ currentDecDeptName || '请选择所在科室' }}
</view>
</picker>
</view>
<!-- 申报床号 -->
<view class="form-item">
<text class="label required">申报床号</text>
<input class="input" type="text" v-model="form.decBedNo"
placeholder="请输入床号" />
</view>
<!-- 申报手机 -->
<view class="form-item">
<text class="label required">申报手机</text>
<input class="input" type="number" maxlength="11" v-model="form.decTel"
placeholder="请输入手机号码" />
</view>
<!-- 运送工具 -->
<view class="form-item">
<text class="label required">运送工具</text>
<picker mode="selector" :range="toolNames" @change="onPickTool">
<view class="picker-value"
:class="{ placeholder: !form.carryingtools }">
{{ currentToolName || '请选择所需运送工具' }}
</view>
</picker>
</view>
<!-- 工具数量 -->
<view class="form-item">
<text class="label required">工具数量</text>
<input class="input" type="number"
v-model.number="form.carryingtoolsCount" placeholder="请输入工具数量" />
</view>
<!-- 承运人数量 -->
<view class="form-item">
<text class="label required">承运人数量</text>
<input class="input" type="number" v-model.number="form.outnumber"
placeholder="请输入所需运送人员数量" />
</view>
<!-- 是否预约 -->
<view class="form-item">
<text class="label">是否预约</text>
<switch :checked="form.isOrdered" @change="onToggleOrdered" />
</view>
<!-- 预约时间 -->
<view v-if="form.isOrdered" class="form-item">
<text class="label">预约时间</text>
<picker mode="time" :value="form.orderedDatetime || ''"
@change="onPickTime">
<view class="picker-value"
:class="{ placeholder: !form.orderedDatetime }">
{{ form.orderedDatetime || '请选择' }}
</view>
</picker>
</view>
<!-- 支付方式保留但界面可隐藏按需要开启-->
<view class="form-item" v-if="showPayment">
<text class="label required">支付方式</text>
<picker mode="selector" :range="payNames" @change="onPickPay">
<view class="picker-value"
:class="{ placeholder: !form.paymentMethod }">
{{ currentPayName || '请选择支付方式' }}
</view>
</picker>
</view>
<view class="btns">
<button class="btn outline" :disabled="!isCancelSubmit"
@tap="onCancelAgent">取消申请</button>
<button class="btn primary" :disabled="!isSubmit"
@tap="onSubmit">申请</button>
</view>
</view>
<!-- 等待接单浮层 -->
<view v-if="isShowProgress" class="overlay">
<view class="overlay-card">
<text class="overlay-title">等待骑手接单</text>
<text class="overlay-time">{{ countdownText }}</text>
<button class="btn outline" @tap="onCancelAgent">取消申请</button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { computed, onMounted, onUnmounted, reactive, ref } from 'vue'
// @ts-ignore
import util from '@/utils/util.js'
// @ts-ignore
import queryService from '@/service/queryService.js'
// @ts-ignore
import getService from '@/service/getService.js'
// @ts-ignore
import config from '@/config.js'
const showPayment = ref(true) // false
const form = reactive({
needcontactDept: null as any,
needcontactDepts: null as any,
needcontact: null as any,
needcontactTel: null as any,
decChannel: null as any,
decDept: null as any,
decDepts: null as any,
decBedNo: null as any,
decTel: null as any,
carryingtools: null as any,
carryingtoolsCount: 1,
outnumber: 1,
openid: null as any,
isOrdered: false,
orderedDatetime: null as any,
paymentMethod: '01'
})
const formData = reactive({
decDeptDatas: [] as any[],
needcontactDeptDatas: [] as any[],
carryingtoolsDatas: [] as any[],
paymentMethodDatas: [] as any[]
})
// UI &
const isSubmit = ref(true)
const isCancelSubmit = ref(false)
const isShowProgress = ref(false)
const list = ref<any[] | null>(null)
const COUNTDOWN_MAX = 5 * 60 * 1000
const countdown = ref(COUNTDOWN_MAX)
let pollTimer: any = null
let cdTimer: any = null
//
const needDeptParents = ref<any[]>([])
const needDeptChildrenMap = ref<Record<string, any[]>>({})
const needDeptMultiRange = ref<string[][]>([[], []])
const needDeptMultiValue = ref<number[]>([0, 0])
//
const decDeptParents = ref<any[]>([])
const decDeptChildrenMap = ref<Record<string, any[]>>({})
const decDeptMultiRange = ref<string[][]>([[], []])
const decDeptMultiValue = ref<number[]>([0, 0])
const toolNames = computed(() => formData.carryingtoolsDatas.map(d => d.name || d.label || ''))
const payNames = computed(() => formData.paymentMethodDatas.map(d => d.name || d.label || ''))
const currentNeedDeptName = computed(() => {
if (!form.needcontactDept) return ''
for (const p of needDeptParents.value) {
const children = needDeptChildrenMap.value[p.id] || []
const c = children.find((it: any) => it.id === form.needcontactDept)
if (c) return c.gridFullname || c.gridName || c.name || c.label || ''
}
const p = needDeptParents.value.find((it: any) => it.id === form.needcontactDept)
return p ? (p.gridFullname || p.gridName || p.name || p.label || '') : ''
})
const currentDecDeptName = computed(() => {
if (!form.decDept) return ''
for (const p of decDeptParents.value) {
const children = decDeptChildrenMap.value[p.id] || []
const c = children.find((it: any) => it.id === form.decDept)
if (c) return c.gridFullname || c.gridName || c.name || c.label || ''
}
const p = decDeptParents.value.find((it: any) => it.id === form.decDept)
return p ? (p.gridFullname || p.gridName || p.name || p.label || '') : ''
})
const currentToolName = computed(() => {
const found = formData.carryingtoolsDatas.find(d => d.id === form.carryingtools)
return found ? (found.name || found.label) : ''
})
const currentPayName = computed(() => {
const found = formData.paymentMethodDatas.find(d => d.id === form.paymentMethod)
return found ? (found.name || found.label) : ''
})
const countdownText = computed(() => {
const total = Math.max(0, countdown.value)
const h = Math.floor(total / 3600000)
const m = Math.floor((total % 3600000) / 60000)
const s = Math.floor((total % 60000) / 1000)
const pad = (n: number) => String(n).padStart(2, '0')
return `${pad(h)}:${pad(m)}:${pad(s)}`
})
function onNeedDeptColumnChange(e: any) {
const { column, value } = e.detail
if (column === 0) {
needDeptMultiValue.value[0] = value
const parent = needDeptParents.value[value]
const children = (parent && needDeptChildrenMap.value[parent.id]) || []
needDeptMultiRange.value[1] = children.length > 0
? children.map((c: any) => c.gridFullname || c.gridName || c.name || c.label || '')
: ['- 无子项 -']
needDeptMultiValue.value[1] = 0
}
}
function onNeedDeptChange(e: any) {
const [pi, ci] = e.detail.value as number[]
const parent = needDeptParents.value[pi]
const children = (parent && needDeptChildrenMap.value[parent.id]) || []
if (children.length > 0) {
const child = children[ci]
if (!child) return
form.needcontactDept = child.id
form.needcontactDepts = ['', child.id]
} else {
if (!parent) return
form.needcontactDept = parent.id
form.needcontactDepts = ['', parent.id]
}
}
function onDecDeptColumnChange(e: any) {
const { column, value } = e.detail
if (column === 0) {
decDeptMultiValue.value[0] = value
const parent = decDeptParents.value[value]
const children = (parent && decDeptChildrenMap.value[parent.id]) || []
decDeptMultiRange.value[1] = children.length > 0
? children.map((c: any) => c.gridFullname || c.gridName || c.name || c.label || '')
: ['- 无子项 -']
decDeptMultiValue.value[1] = 0
}
}
function onDecDeptChange(e: any) {
const [pi, ci] = e.detail.value as number[]
const parent = decDeptParents.value[pi]
const children = (parent && decDeptChildrenMap.value[parent.id]) || []
if (children.length > 0) {
const child = children[ci]
if (!child) return
form.decDept = child.id
form.decDepts = ['', child.id]
} else {
if (!parent) return
form.decDept = parent.id
form.decDepts = ['', parent.id]
}
}
function onPickTool(e: any) {
const idx = Number(e.detail.value)
const item = formData.carryingtoolsDatas[idx]
if (!item) return
form.carryingtools = item.id
}
function onPickPay(e: any) {
const idx = Number(e.detail.value)
const item = formData.paymentMethodDatas[idx]
if (!item) return
form.paymentMethod = item.id
}
function onPickTime(e: any) {
form.orderedDatetime = e.detail.value
}
function onToggleOrdered(e: any) {
form.isOrdered = !!e.detail.value
}
function validate(): boolean {
if (!form.needcontact) { uni.showToast({ title: '代申报人不能为空', icon: 'none' }); return false }
if (!form.needcontactTel) { uni.showToast({ title: '代申报电话不能为空', icon: 'none' }); return false }
if (!form.needcontactDept && (!form.needcontactDepts || (Array.isArray(form.needcontactDepts) && form.needcontactDepts.length <= 1))) {
uni.showToast({ title: '代申报科室不能为空', icon: 'none' }); return false
}
if (!form.decDept && (!form.decDepts || (Array.isArray(form.decDepts) && form.decDepts.length <= 1))) {
uni.showToast({ title: '申报科室不能为空', icon: 'none' }); return false
}
if (!form.decBedNo) { uni.showToast({ title: '床号不能为空', icon: 'none' }); return false }
if (!form.decTel) { uni.showToast({ title: '申报电话不能为空', icon: 'none' }); return false }
if (!form.carryingtools) { uni.showToast({ title: '运送工具不能为空', icon: 'none' }); return false }
if (!form.carryingtoolsCount) { uni.showToast({ title: '工具数量不能为空', icon: 'none' }); return false }
if (!form.outnumber) { uni.showToast({ title: '所需运送人员数量不能为空', icon: 'none' }); return false }
if (showPayment.value && !form.paymentMethod) { uni.showToast({ title: '支付方式不能为空', icon: 'none' }); return false }
if (form.isOrdered && !form.orderedDatetime) { uni.showToast({ title: '请选择预约时间', icon: 'none' }); return false }
return true
}
async function onSubmit() {
if (!validate()) return
if (form.decDepts && Array.isArray(form.decDepts) && form.decDepts.length > 1) form.decDept = form.decDepts[1]
if (form.needcontactDepts && Array.isArray(form.needcontactDepts) && form.needcontactDepts.length > 1) form.needcontactDept = form.needcontactDepts[1]
const openid = uni.getStorageSync('openid')
form.openid = openid ? openid : config.defaultOpenId
const payload: any = { ...form }
if (form.isOrdered) payload.orderedDatetime = toFullDateTime(form.orderedDatetime as any)
else payload.orderedDatetime = null
try {
const res = await util.request({ url: '/API/stretchertransport/create', method: 'POST', data: payload })
if (res.data && res.data.code === 0) {
isSubmit.value = false
isCancelSubmit.value = true
isShowProgress.value = true
startPoll()
startCountdown()
uni.showToast({ title: '担架运送申请成功,等待骑手接单!', icon: 'none' })
} else {
throw new Error('create failed')
}
} catch (err) {
uni.showToast({ title: '担架运送申请失败,请重新申请!', icon: 'none' })
isShowProgress.value = false
isSubmit.value = true
isCancelSubmit.value = false
}
}
async function onCancel() {
try {
const res = await util.request({ url: `/API/stretchertransport/redis/delete/${form.decTel}`, method: 'GET' })
if (res.data && res.data.code === 0) {
isSubmit.value = true
isCancelSubmit.value = false
isShowProgress.value = false
stopPoll()
stopCountdown()
uni.showToast({ title: '担架运送申请取消成功!', icon: 'none' })
} else {
throw new Error('cancel failed')
}
} catch (err) {
uni.showToast({ title: '担架运送申请取消失败!', icon: 'none' })
}
}
async function onCancelAgent() {
const res = await uni.showModal({ title: '确认取消申请提示', content: '您确定要取消本次申请吗?' })
const result: any = Array.isArray(res) ? res[1] : (res as any)
if (result && result.confirm) onCancel()
}
function startPoll() {
if (pollTimer) return
pollTimer = setInterval(async () => {
const data = { decDept: form.decDept, decBedNo: form.decBedNo, decTel: form.decTel }
try {
const res = await queryService.queryStretcherReceiptTask(data)
if (res.data && res.data.code === 0) {
list.value = res.data.data
if (list.value && list.value.length > 0) {
isShowProgress.value = false
isCancelSubmit.value = false
isSubmit.value = true
stopPoll(); stopCountdown(); resetForm()
uni.showToast({ title: '申请已被骑手接单!', icon: 'success', duration: 3000 })
}
}
} catch { }
}, 1000)
}
function stopPoll() { if (pollTimer) clearInterval(pollTimer); pollTimer = null }
function startCountdown() {
countdown.value = COUNTDOWN_MAX
if (cdTimer) clearInterval(cdTimer)
cdTimer = setInterval(() => {
countdown.value -= 1000
if (countdown.value <= 0) onCountdownEnd()
}, 1000)
}
function stopCountdown() { if (cdTimer) clearInterval(cdTimer); cdTimer = null }
function onCountdownEnd() {
stopPoll(); stopCountdown()
uni.showToast({ title: '申请未被接单,请重新申请!', icon: 'success', duration: 4000 })
isShowProgress.value = false
isCancelSubmit.value = false
isSubmit.value = true
}
function resetForm() {
form.needcontactDept = null
form.needcontactDepts = null
form.needcontact = null
form.needcontactTel = null
form.decDept = null
form.decDepts = null
form.decBedNo = null
form.decTel = null
form.carryingtools = null
form.carryingtoolsCount = 1
form.outnumber = 1
form.isOrdered = false
form.orderedDatetime = null
}
async function getCarryToolsType() {
try {
const res = await queryService.getDataDictionary('db_tasksheetcarryingtools_type')
if (res.data && res.data.code === 0) {
formData.carryingtoolsDatas = res.data.data.dictionaryList || []
if (formData.carryingtoolsDatas.length > 0) form.carryingtools = formData.carryingtoolsDatas[0].id
} else uni.showToast({ title: '获取运送工具类别失败!', icon: 'none' })
} catch { uni.showToast({ title: '获取运送工具类别失败!', icon: 'none' }) }
}
async function getPaymentMethodType() {
try {
const res = await queryService.getDataDictionary('db_payment_mode_type')
if (res.data && res.data.code === 0) {
formData.paymentMethodDatas = res.data.data.dictionaryList || []
if (formData.paymentMethodDatas.length > 0) form.paymentMethod = formData.paymentMethodDatas[0].id
} else uni.showToast({ title: '获取支付方式列表失败!', icon: 'none' })
} catch { uni.showToast({ title: '获取支付方式列表失败!', icon: 'none' }) }
}
async function getGrids() {
try {
const res = await queryService.getGrids()
if (res.data && res.data.code === 0) {
const list = res.data.data || []
formData.decDeptDatas = list
formData.needcontactDeptDatas = list
//
needDeptParents.value = list
needDeptChildrenMap.value = {}
needDeptParents.value.forEach((p: any) => { needDeptChildrenMap.value[p.id] = Array.isArray(p.children) ? p.children : [] })
needDeptMultiRange.value[0] = needDeptParents.value.map((p: any) => p.gridFullname || p.gridName || p.name || p.label || '')
const ndFirstParent = needDeptParents.value[0]
const ndFirstChildren = ndFirstParent ? (needDeptChildrenMap.value[ndFirstParent.id] || []) : []
needDeptMultiRange.value[1] = ndFirstChildren.length > 0 ? ndFirstChildren.map((c: any) => c.gridFullname || c.gridName || c.name || c.label || '') : ['无子项']
needDeptMultiValue.value = [0, 0]
if (ndFirstParent) {
if (ndFirstChildren.length > 0) { form.needcontactDept = ndFirstChildren[0].id; form.needcontactDepts = ['', ndFirstChildren[0].id] }
else { form.needcontactDept = ndFirstParent.id; form.needcontactDepts = ['', ndFirstParent.id] }
}
//
decDeptParents.value = list
decDeptChildrenMap.value = {}
decDeptParents.value.forEach((p: any) => { decDeptChildrenMap.value[p.id] = Array.isArray(p.children) ? p.children : [] })
decDeptMultiRange.value[0] = decDeptParents.value.map((p: any) => p.gridFullname || p.gridName || p.name || p.label || '')
const dFirstParent = decDeptParents.value[0]
const dFirstChildren = dFirstParent ? (decDeptChildrenMap.value[dFirstParent.id] || []) : []
decDeptMultiRange.value[1] = dFirstChildren.length > 0 ? dFirstChildren.map((c: any) => c.gridFullname || c.gridName || c.name || c.label || '') : ['无子项']
decDeptMultiValue.value = [0, 0]
if (dFirstParent) {
if (dFirstChildren.length > 0) { form.decDept = dFirstChildren[0].id; form.decDepts = ['', dFirstChildren[0].id] }
else { form.decDept = dFirstParent.id; form.decDepts = ['', dFirstParent.id] }
}
} else uni.showToast({ title: '获取网格列表失败!', icon: 'none' })
} catch { uni.showToast({ title: '获取网格列表失败!', icon: 'none' }) }
}
onMounted(async () => {
// #ifdef MP-WEIXIN
const openid = uni.getStorageSync('openid')
if (!openid) { try { getService.getOpenId && getService.getOpenId() } catch { } }
// #endif
await Promise.all([getCarryToolsType(), getPaymentMethodType(), getGrids()])
})
onUnmounted(() => { stopPoll(); stopCountdown() })
function toFullDateTime(timeStr: string): string {
const now = new Date()
const yyyy = now.getFullYear()
const MM = String(now.getMonth() + 1).padStart(2, '0')
const dd = String(now.getDate()).padStart(2, '0')
if (!timeStr) return `${yyyy}-${MM}-${dd} 00:00:00`
const parts = String(timeStr).split(':')
const h = String(parts[0] ?? '00').padStart(2, '0')
const m = String(parts[1] ?? '00').padStart(2, '0')
const s = String(parts[2] ?? '00').padStart(2, '0')
return `${yyyy}-${MM}-${dd} ${h}:${m}:${s}`
}
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f5f7fb;
}
.card {
margin: 24rpx;
padding: 24rpx;
background: #fff;
border-radius: 16rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
}
.title {
font-size: 34rpx;
font-weight: 600;
margin-bottom: 12rpx;
color: #111;
}
.form-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.label {
color: #333;
font-size: 28rpx;
}
.required::before {
content: '*';
color: #ff4d4f;
margin-right: 8rpx;
}
.input {
text-align: right;
flex: 1;
margin-left: 24rpx;
font-size: 28rpx;
color: #333;
}
.picker-value {
flex: 1;
text-align: right;
margin-left: 24rpx;
color: #333;
font-size: 28rpx;
}
.picker-value.placeholder {
color: #999;
}
.btns {
display: flex;
gap: 20rpx;
margin-top: 24rpx;
}
.btn {
flex: 1;
padding: 0 20rpx;
border-radius: 12rpx;
font-size: 30rpx;
}
.btn.primary {
background: #007aff;
color: #fff;
}
.btn.outline {
background: #fff;
color: #007aff;
border: 2rpx solid #007aff;
}
.overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.35);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
.overlay-card {
width: 80%;
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
text-align: center;
}
.overlay-title {
display: block;
font-size: 30rpx;
color: #333;
margin-bottom: 12rpx;
}
.overlay-time {
display: block;
font-size: 40rpx;
color: #007aff;
margin-bottom: 16rpx;
font-weight: 600;
}
</style>

View File

@ -0,0 +1,629 @@
<template>
<view class="page">
<view class="card">
<view class="title">担架运送申请</view>
<!-- 申报科室 -->
<view class="form-item">
<text class="label required">申报科室</text>
<picker mode="multiSelector" :range="deptMultiRange"
:value="deptMultiValue" @columnchange="onDeptColumnChange"
@change="onDeptChange">
<view class="picker-value" :class="{ placeholder: !form.decDept }">
{{ currentDeptName || '请选择所在科室' }}
</view>
</picker>
</view>
<!-- 申报床号 -->
<view class="form-item">
<text class="label required">申报床号</text>
<input class="input" type="text" v-model="form.decBedNo"
placeholder="请输入床号" />
</view>
<!-- 申报手机 -->
<view class="form-item">
<text class="label required">申报手机</text>
<input class="input" type="number" maxlength="11" v-model="form.decTel"
placeholder="请输入手机号码" />
</view>
<!-- 运送工具 -->
<view class="form-item">
<text class="label required">运送工具</text>
<picker mode="selector" :range="toolNames" @change="onPickTool">
<view class="picker-value"
:class="{ placeholder: !form.carryingtools }">
{{ currentToolName || '请选择所需运送工具' }}
</view>
</picker>
</view>
<!-- 工具数量 -->
<view class="form-item">
<text class="label required">工具数量</text>
<input class="input" type="number"
v-model.number="form.carryingtoolsCount" placeholder="请输入工具数量" />
</view>
<!-- 承运人数量 -->
<view class="form-item">
<text class="label required">承运人数量</text>
<input class="input" type="number" v-model.number="form.outnumber"
placeholder="请输入所需运送人员数量" />
</view>
<!-- 是否预约 -->
<view class="form-item">
<text class="label">是否预约</text>
<switch :checked="form.isOrdered" @change="onToggleOrdered" />
</view>
<!-- 预约时间 -->
<view v-if="form.isOrdered" class="form-item">
<text class="label">预约时间</text>
<picker mode="time" :value="form.orderedDatetime || ''"
@change="onPickTime">
<view class="picker-value"
:class="{ placeholder: !form.orderedDatetime }">
{{ form.orderedDatetime || '请选择' }}
</view>
</picker>
</view>
<!-- 支付方式 -->
<view class="form-item">
<text class="label required">支付方式</text>
<picker mode="selector" :range="payNames" @change="onPickPay">
<view class="picker-value"
:class="{ placeholder: !form.paymentMethod }">
{{ currentPayName || '请选择支付方式' }}
</view>
</picker>
</view>
<view class="btns">
<button class="btn outline" :disabled="!isCancelSubmit"
@tap="onCancelAgent">取消申请</button>
<button class="btn primary" :disabled="!isSubmit"
@tap="onSubmit">申请</button>
</view>
</view>
<!-- 等待接单浮层 -->
<view v-if="isShowProgress" class="overlay">
<view class="overlay-card">
<text class="overlay-title">等待骑手接单</text>
<text class="overlay-time">{{ countdownText }}</text>
<button class="btn outline" @tap="onCancelAgent">取消申请</button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { computed, onMounted, onUnmounted, reactive, ref } from 'vue'
// JS any
// @ts-ignore
import util from '@/utils/util.js'
// @ts-ignore
import queryService from '@/service/queryService.js'
// @ts-ignore
import getService from '@/service/getService.js'
// @ts-ignore
import config from '@/config.js'
//
const form = reactive({
needcontactDept: null as any,
needcontact: null as any,
needcontactTel: null as any,
decChannel: null as any,
decDepts: null as any,
decDept: null as any,
decBedNo: null as any,
decTel: null as any,
openid: null as any,
carryingtools: null as any,
carryingtoolsCount: 1,
outnumber: 1,
isOrdered: false,
orderedDatetime: null as any,
paymentMethod: '01'
})
//
const formData = reactive({
decDeptDatas: [] as Array<any>,
carryingtoolsDatas: [] as Array<any>,
paymentMethodDatas: [] as Array<any>
})
// UI &
const isSubmit = ref(true)
const isCancelSubmit = ref(false)
const isShowProgress = ref(false)
const list = ref<any[] | null>(null)
const COUNTDOWN_MAX = 5 * 60 * 1000 // 5
const countdown = ref(COUNTDOWN_MAX)
let pollTimer: any = null
let cdTimer: any = null
//
const deptMultiRange = ref<string[][]>([[], []])
const deptParents = ref<any[]>([])
const deptChildrenMap = ref<Record<string, any[]>>({}) // parentId -> children[]
const deptMultiValue = ref<number[]>([0, 0])
const toolNames = computed(() => formData.carryingtoolsDatas.map(d => d.name || d.label || ''))
const payNames = computed(() => formData.paymentMethodDatas.map(d => d.name || d.label || ''))
const currentDeptName = computed(() => {
if (!form.decDept) return ''
//
for (const p of deptParents.value) {
const children = deptChildrenMap.value[p.id] || []
const c = children.find((it: any) => it.id === form.decDept)
if (c) return c.gridFullname || c.gridName || c.name || c.label || ''
}
//
const p = deptParents.value.find((it: any) => it.id === form.decDept)
return p ? (p.gridFullname || p.gridName || p.name || p.label || '') : ''
})
const currentToolName = computed(() => {
const found = formData.carryingtoolsDatas.find(d => d.id === form.carryingtools)
return found ? (found.name || found.label) : ''
})
const currentPayName = computed(() => {
const found = formData.paymentMethodDatas.find(d => d.id === form.paymentMethod)
return found ? (found.name || found.label) : ''
})
const countdownText = computed(() => {
const total = Math.max(0, countdown.value)
const h = Math.floor(total / 3600000)
const m = Math.floor((total % 3600000) / 60000)
const s = Math.floor((total % 60000) / 1000)
const pad = (n: number) => String(n).padStart(2, '0')
return `${pad(h)}:${pad(m)}:${pad(s)}`
})
function onDeptColumnChange(e: any) {
const { column, value } = e.detail
//
if (column === 0) {
deptMultiValue.value[0] = value
const parent = deptParents.value[value]
const children = (parent && deptChildrenMap.value[parent.id]) || []
deptMultiRange.value[1] = children.length > 0
? children.map((c: any) => c.gridFullname || c.gridName || c.name || c.label || '')
: ['- 无子项 -']
deptMultiValue.value[1] = 0
}
}
function onDeptChange(e: any) {
const [pi, ci] = e.detail.value as number[]
const parent = deptParents.value[pi]
const children = (parent && deptChildrenMap.value[parent.id]) || []
if (children.length > 0) {
const child = children[ci]
if (!child) return
form.decDept = child.id
form.decDepts = ['', child.id]
} else {
if (!parent) return
form.decDept = parent.id
form.decDepts = ['', parent.id]
}
}
function onPickTool(e: any) {
const idx = Number(e.detail.value)
const item = formData.carryingtoolsDatas[idx]
if (!item) return
form.carryingtools = item.id
}
function onPickPay(e: any) {
const idx = Number(e.detail.value)
const item = formData.paymentMethodDatas[idx]
if (!item) return
form.paymentMethod = item.id
}
function onPickTime(e: any) {
form.orderedDatetime = e.detail.value
}
function onToggleOrdered(e: any) {
form.isOrdered = !!e.detail.value
}
function validate(): boolean {
//
if (!form.decDept && (!form.decDepts || (Array.isArray(form.decDepts) && form.decDepts.length <= 1))) {
uni.showToast({ title: '申报科室不能为空', icon: 'none' })
return false
}
if (!form.decBedNo) {
uni.showToast({ title: '床号不能为空', icon: 'none' })
return false
}
if (!form.decTel) {
uni.showToast({ title: '申报电话不能为空', icon: 'none' })
return false
}
if (!form.carryingtools) {
uni.showToast({ title: '运送工具不能为空', icon: 'none' })
return false
}
if (!form.carryingtoolsCount) {
uni.showToast({ title: '工具数量不能为空', icon: 'none' })
return false
}
if (!form.outnumber) {
uni.showToast({ title: '所需运送人员数量不能为空', icon: 'none' })
return false
}
if (!form.paymentMethod) {
uni.showToast({ title: '支付方式不能为空', icon: 'none' })
return false
}
if (form.isOrdered && !form.orderedDatetime) {
uni.showToast({ title: '请选择预约时间', icon: 'none' })
return false
}
return true
}
async function onSubmit() {
if (!validate()) return
// decDepts >1 decDept
if (form.decDepts && Array.isArray(form.decDepts) && form.decDepts.length > 1) {
form.decDept = form.decDepts[1]
}
const openid = uni.getStorageSync('openid')
form.openid = openid ? openid : config.defaultOpenId
// yyyy-MM-dd HH:mm:ss
const payload: any = { ...form }
if (form.isOrdered) {
payload.orderedDatetime = toFullDateTime(form.orderedDatetime as any)
} else {
payload.orderedDatetime = null
}
try {
const res = await util.request({ url: '/API/stretchertransport/create', method: 'POST', data: payload })
if (res.data && res.data.code === 0) {
isSubmit.value = false
isCancelSubmit.value = true
isShowProgress.value = true
startPoll()
startCountdown()
uni.showToast({ title: '担架运送申请成功,等待骑手接单!', icon: 'none' })
} else {
throw new Error('create failed')
}
} catch (err) {
uni.showToast({ title: '担架运送申请失败,请重新申请!', icon: 'none' })
isShowProgress.value = false
isSubmit.value = true
isCancelSubmit.value = false
}
}
async function onCancel() {
try {
const res = await util.request({ url: `/API/stretchertransport/redis/delete/${form.decTel}`, method: 'GET' })
if (res.data && res.data.code === 0) {
isSubmit.value = true
isCancelSubmit.value = false
isShowProgress.value = false
stopPoll()
stopCountdown()
uni.showToast({ title: '担架运送申请取消成功!', icon: 'none' })
} else {
throw new Error('cancel failed')
}
} catch (err) {
uni.showToast({ title: '担架运送申请取消失败!', icon: 'none' })
}
}
async function onCancelAgent() {
const res = await uni.showModal({ title: '确认取消申请提示', content: '您确定要取消本次申请吗?' })
// res: [error, result] in promise? In uni-app promise, returns [err, res]? Using any below
const result: any = Array.isArray(res) ? res[1] : (res as any)
if (result && result.confirm) {
onCancel()
}
}
function startPoll() {
if (pollTimer) return
pollTimer = setInterval(async () => {
const data = { decDept: form.decDept, decBedNo: form.decBedNo, decTel: form.decTel }
try {
const res = await queryService.queryStretcherReceiptTask(data)
if (res.data && res.data.code === 0) {
list.value = res.data.data
if (list.value && list.value.length > 0) {
//
isShowProgress.value = false
isCancelSubmit.value = false
isSubmit.value = true
stopPoll()
stopCountdown()
resetForm()
uni.showToast({ title: '申请已被骑手接单!', icon: 'success', duration: 3000 })
}
}
} catch { }
}, 1000)
}
function stopPoll() {
if (pollTimer) clearInterval(pollTimer)
pollTimer = null
}
function startCountdown() {
countdown.value = COUNTDOWN_MAX
if (cdTimer) clearInterval(cdTimer)
cdTimer = setInterval(() => {
countdown.value -= 1000
if (countdown.value <= 0) {
onCountdownEnd()
}
}, 1000)
}
function stopCountdown() {
if (cdTimer) clearInterval(cdTimer)
cdTimer = null
}
function onCountdownEnd() {
stopPoll()
stopCountdown()
uni.showToast({ title: '申请未被接单,请重新申请!', icon: 'success', duration: 4000 })
isShowProgress.value = false
isCancelSubmit.value = false
isSubmit.value = true
}
function resetForm() {
form.decDepts = null
form.decDept = null
form.decBedNo = null
form.decTel = null
form.carryingtools = null
form.carryingtoolsCount = 1
form.outnumber = 1
form.isOrdered = false
form.orderedDatetime = null
}
async function getCarryToolsType() {
try {
const res = await queryService.getDataDictionary('db_tasksheetcarryingtools_type')
if (res.data && res.data.code === 0) {
formData.carryingtoolsDatas = res.data.data.dictionaryList || []
if (formData.carryingtoolsDatas.length > 0) {
form.carryingtools = formData.carryingtoolsDatas[0].id
}
} else {
uni.showToast({ title: '获取运送工具类别失败!', icon: 'none' })
}
} catch {
uni.showToast({ title: '获取运送工具类别失败!', icon: 'none' })
}
}
async function getPaymentMethodType() {
try {
const res = await queryService.getDataDictionary('db_payment_mode_type')
if (res.data && res.data.code === 0) {
formData.paymentMethodDatas = res.data.data.dictionaryList || []
if (formData.paymentMethodDatas.length > 0) {
form.paymentMethod = formData.paymentMethodDatas[0].id
}
} else {
uni.showToast({ title: '获取支付方式列表失败!', icon: 'none' })
}
} catch {
uni.showToast({ title: '获取支付方式列表失败!', icon: 'none' })
}
}
async function getGrids() {
try {
const res = await queryService.getGrids()
if (res.data && res.data.code === 0) {
formData.decDeptDatas = res.data.data || []
//
deptParents.value = formData.decDeptDatas
deptChildrenMap.value = {}
deptParents.value.forEach((p: any) => {
deptChildrenMap.value[p.id] = Array.isArray(p.children) ? p.children : []
})
//
deptMultiRange.value[0] = deptParents.value.map((p: any) => p.gridFullname || p.gridName || p.name || p.label || '')
const firstParent = deptParents.value[0]
const firstChildren = firstParent ? (deptChildrenMap.value[firstParent.id] || []) : []
deptMultiRange.value[1] = firstChildren.length > 0
? firstChildren.map((c: any) => c.gridFullname || c.gridName || c.name || c.label || '')
: ['无子项']
deptMultiValue.value = [0, 0]
//
if (firstParent) {
if (firstChildren.length > 0) {
form.decDept = firstChildren[0].id
form.decDepts = ['', firstChildren[0].id]
} else {
form.decDept = firstParent.id
form.decDepts = ['', firstParent.id]
}
}
} else {
uni.showToast({ title: '获取网格列表失败!', icon: 'none' })
}
} catch {
uni.showToast({ title: '获取网格列表失败!', icon: 'none' })
}
}
onMounted(async () => {
// openid
// #ifdef MP-WEIXIN
const openid = uni.getStorageSync('openid')
if (!openid) {
try { getService.getOpenId && getService.getOpenId() } catch { }
}
// #endif
await Promise.all([getCarryToolsType(), getPaymentMethodType(), getGrids()])
})
onUnmounted(() => {
stopPoll()
stopCountdown()
})
function toFullDateTime(timeStr: string): string {
const now = new Date()
const yyyy = now.getFullYear()
const MM = String(now.getMonth() + 1).padStart(2, '0')
const dd = String(now.getDate()).padStart(2, '0')
if (!timeStr) return `${yyyy}-${MM}-${dd} 00:00:00`
const parts = String(timeStr).split(':')
const h = String(parts[0] ?? '00').padStart(2, '0')
const m = String(parts[1] ?? '00').padStart(2, '0')
const s = String(parts[2] ?? '00').padStart(2, '0')
return `${yyyy}-${MM}-${dd} ${h}:${m}:${s}`
}
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f5f7fb;
}
.card {
margin: 24rpx;
padding: 24rpx;
background: #fff;
border-radius: 16rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
}
.title {
font-size: 34rpx;
font-weight: 600;
margin-bottom: 12rpx;
color: #111;
}
.form-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.label {
color: #333;
font-size: 28rpx;
}
.required::before {
content: '*';
color: #ff4d4f;
margin-right: 8rpx;
}
.input {
text-align: right;
flex: 1;
margin-left: 24rpx;
font-size: 28rpx;
color: #333;
}
.picker-value {
flex: 1;
text-align: right;
margin-left: 24rpx;
color: #333;
font-size: 28rpx;
}
.picker-value.placeholder {
color: #999;
}
.btns {
display: flex;
gap: 20rpx;
margin-top: 24rpx;
}
.btn {
flex: 1;
padding: 0 20rpx;
border-radius: 12rpx;
font-size: 30rpx;
}
.btn.primary {
background: #007aff;
color: #fff;
}
.btn.outline {
background: #fff;
color: #007aff;
border: 2rpx solid #007aff;
}
.overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.35);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
.overlay-card {
width: 80%;
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
text-align: center;
}
.overlay-title {
display: block;
font-size: 30rpx;
color: #333;
margin-bottom: 12rpx;
}
.overlay-time {
display: block;
font-size: 40rpx;
color: #007aff;
margin-bottom: 16rpx;
font-weight: 600;
}
</style>

32
src/pages/task/index.vue Normal file
View File

@ -0,0 +1,32 @@
<template>
<view class="page">
<view class="empty">
<image src="/static/icons/tasks.png" class="icon" />
<text>暂无任务</text>
</view>
</view>
</template>
<script setup lang="ts">
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f7f8fa;
}
.empty {
height: 60vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #999;
font-size: 28rpx;
}
.icon {
width: 120rpx;
height: 120rpx;
margin-bottom: 24rpx;
}
</style>

32
src/pages/tasks/index.vue Normal file
View File

@ -0,0 +1,32 @@
<template>
<view class="page">
<view class="empty">
<image src="/static/icons/tasks.png" class="icon" />
<text>暂无任务</text>
</view>
</view>
</template>
<script setup lang="ts">
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f7f8fa;
}
.empty {
height: 60vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #999;
font-size: 28rpx;
}
.icon {
width: 120rpx;
height: 120rpx;
margin-bottom: 24rpx;
}
</style>

45
src/service/getService.js Normal file
View File

@ -0,0 +1,45 @@
import config from '../config.js';
import util from '../utils/util.js';
import queryService from '@/service/queryService.js';
export default {
// 获取微信用户的OpenID
getOpenId() {
let code = null;
uni.login({
provider: 'weixin',
//onlyAuthorize: true, // 微信登录仅请求授权认证
success: function (loginRes) {
//console.log('login success: ' + JSON.stringify(loginRes));
code = loginRes.code;
//console.log('code: ' + code);
queryService.queryOpenId(code).then(res => {
if(res.data.code == 0){
//console.log('save openId: ' + res.data.data);
uni.setStorageSync('openid', res.data.data);
//code = res.data.data;
//return code;
}else{
uni.showToast({
title: "获取用户OpenID失败",
icon: 'none',
duration: 4000 // 提示的延迟时间,单位毫秒,默认:1500
});
//return null;
}
});
},
fail:function(e){
console.log('微信登录失败:',e);
uni.showToast({
title: "微信登录失败:" + e,
icon: 'error',
duration: 4000 // 提示的延迟时间,单位毫秒,默认:1500
});
//return null;
}
});
}
}

490
src/service/queryService.js Normal file
View File

@ -0,0 +1,490 @@
import config from '../config.js';
import util from '../utils/util.js';
import * as taskState from '../constant/taskState.js';
import taskType from '../constant/taskType.js';
const queryTaskListUrlMapper = {
'药品运送': config.urls.queryMedicineNewTask,
'标本运送': config.urls.querySampleTasksheet,
}
const pageQueryTaskUrlMapper = {
'药品运送': config.urls.pageQueryMedicineTask,
'标本运送': config.urls.pageQuerySampleTasksheet,
}
const getTaskByIdUrlMapper = {
'药品运送': config.urls.getMedicineTaskById ,
'标本运送': config.urls.getSampleTaskById,
}
export default {
/**
* 获取指定名称的数据字典
* @param {Object} name
*/
getDataDictionary(name) {
return util.request({
url: config.dataDictionaryUrl + name,
method: 'GET'
});
},
// 获取微信用户的OpenID
queryOpenId(code){
return util.request({
url: config.urls.queryOpenId + code,
method: 'GET'
});
},
/**
* 查询全部科室网格列表
* @param {string} type
* @param {object} params
*/
getGrids() {
const staff = uni.getStorageSync("staff");
return util.request({
url: config.urls.getGridFromRedis,
data: {
abbre: null,
gridCode: null,
hospId: (staff==null || staff==undefined || staff == '')?config.defaultHospId:staff.hosp_id
}
});
},
/**
* 获取担架运送收费标准图片
*/
getStretcherTransportFeeImageUrl() {
return util.request({
url: config.urls.getStretcherTransportFeeImageUrl,
method: 'GET'
});
},
/**
* 获取新运单提示音
*/
getNewWaybillPromptWaveUrl() {
return util.request({
url: config.urls.getNewWaybillPromptWaveUrl,
method: 'GET'
});
},
/**
* 生成订单微信支付二维码
* @param {Object} params
*/
getStretcherTransportPayQRcode(params) {
return util.request({
url: config.urls.getStretcherTransportPayQRcode,
data: {
orderDec: params.orderDec,
orderNo: params.orderNo,
productId: params.productId,
totalFree: params.totalFree
},
method: 'POST'
});
},
/**
* 获取APP滑动广告图片
*/
getAppSlipPicUrl() {
return util.request({
url: config.urls.getAppSlipPicUrl,
method: 'GET'
});
},
// 根据id查询担架运单对象
getStretcherTaskById(id){
return util.request({
url: config.urls.getStretcherTransportTaskById + id,
method: 'GET'
});
},
/**
* 查询担架新运单列表
* @param {object} params
*/
queryStretcherNewTask(params) {
const staff = uni.getStorageSync("staff");
params.userId = (staff==null || staff==undefined || staff == '')?null:staff.id
return util.request({
url: config.urls.queryStretcherNewTask,
data: params
});
},
/**
* 列表查询刚接单的担架运送任务
* @param {Object} params
*/
queryStretcherReceiptTask(params){
return util.request({
url: config.urls.queryStretcherReceiptTask,
data: {
decDept: params.decDept || null,
decTel: params.decTel || null,
decBedNo: params.decBedNo || null,
index: 0,
page: 0,
size: 0,
sort: null,
hospId: null
}
});
},
/**
* 列表查询担架运单
* @param {Object} params
*/
queryStretcherTask(params){
const staff = uni.getStorageSync("staff");
return util.request({
url: config.urls.queryStretcherTask,
data: {
decDept: params.decDept || null,
decTel: params.decTel || null,
decBedNo: params.decBedNo || null,
needcontactTel: params.needcontactTel || null,
openid: params.openid || null,
decendDatetime: params.endTime || null,
decstartDatetime: params.startTime || null,
index: 0,
page: 0,
size: 0,
sort: null,
tasksheetStates: params.tasksheetStates || null,
undertakePerson: (staff==null || staff==undefined || staff == '')?null:staff.id,
hospId: null
}
});
},
/**
* 担架运送接单
* @param {Object} params
*/
receiptStretcherNewTask(params){
const staff = uni.getStorageSync("staff");
return util.request({
url: config.urls.receiptStretcherNewTask,
data: {
id: params.id,
acceptancePerson: (staff==null || staff==undefined || staff == '')?null:staff.id,
decBedNo: params.decBedNo,
decDept: null,
delTel: params.delTel,
exteamId: null,
undertakePersons: null
}
});
},
/**
* 启运担架运单
* @param {Object} params
*/
startStretcherTransport(params){
const staff = uni.getStorageSync("staff");
return util.request({
url: config.urls.startStretcherTransport,
data: {
acceptancePerson: (staff==null || staff==undefined || staff == '')?null:staff.id,
sheetId: params.sheetId,
smsStartCode: params.smsStartCode
}
});
},
/**
* 完工担架运单
* @param {Object} params
*/
finishStretcherTransport(params){
const staff = uni.getStorageSync("staff");
return util.request({
url: config.urls.finishStretcherTransport,
data: {
acceptancePerson: (staff==null || staff==undefined || staff == '')?null:staff.id,
finishWord: params.finishWord,
sheetId: params.sheetId,
smsFinishCode: params.smsFinishCode
}
});
},
/**
* 确认现金支付
* @param {Object} params
*/
confirmStretcherPay(params){
const staff = uni.getStorageSync("staff");
return util.request({
url: config.urls.confirmStretcherPay,
data: {
acceptancePerson: (staff==null || staff==undefined || staff == '')?null:staff.id,
paymentMethod: params.paymentMethod,
serviceFee: params.serviceFee,
sheetId: params.sheetId
}
});
},
/**
* 评价担架运单服务
* @param {Object} params
*/
evaluateStretcherTransport(params){
//const staff = uni.getStorageSync("staff");
return util.request({
url: config.urls.evaluateStretcherTransport,
data: {
id: params.id,
satisfactionContent: params.satisfactionContent,
satisfactionEvaluation: params.satisfactionEvaluation || "5",
tasksheetType: null
}
});
},
/**
* 查询全部订单
* @param {string} type
* @param {object} params
*/
queryNewTask(type, params) {
const staff = uni.getStorageSync("staff");
return util.request({
url: queryTaskListUrlMapper[type],
data: {
decendDatetime: params.endTime || null,
decstartDatetime: params.startTime || null,
index: 0,
page: null,
size: null,
sort: null,
tasksheetStates: params.tasksheetStates || null,
tasksheetType: taskType[type],
undertakePerson: (staff==null || staff==undefined || staff == '')?null:staff.id
}
});
},
/**
* 分页查询运单
* @param {string} type
* @param {object} params
*/
pageQueryTask(type, params){
return util.request({
url: pageQueryTaskUrlMapper[type],
data: {
decendDatetime: params.endTime || null,
decstartDatetime: params.startTime || null,
index: 0,
orderNo: params.orderNo || null,
page: params.pageNo || 1,
size: params.pageSize || 10,
sort: null,
tasksheetStates: params.tasksheetStates || null,
tasksheetType: params.tasksheetType || null,
undertakePerson: params.undertakePerson || null,
}
});
},
/**
* 分页查询未接单标本
* @param {Object} params
*/
pageQuerySpecimen(params){
return util.request({
url: config.urls.pageQuerySampleNewTask,
data: {
decendDatetime: params.endTime || null,
decstartDatetime: params.startTime || null,
index: 0,
page: params.pageNo || 1,
size: params.pageSize || 10,
sheetId: null,
sort: null,
tasksheetState: params.tasksheetState || null,
undertakePerson: params.undertakePerson || null,
urgentDegrees: params.urgentDegrees || null
}
});
},
/**
* 查询全部未接单标本
* @param {Object} params
*/
querySpecimen(params){
return util.request({
url: config.urls.querySampleNewTask,
data: {
decendDatetime: params.endTime || null,
decstartDatetime: params.startTime || null,
index: 0,
page: null,
size: null,
sheetId: null,
sort: null,
tasksheetState: params.tasksheetState || null,
undertakePerson: params.undertakePerson || null,
}
});
},
/**
* 药品运送接单
* @param {string} type
* @param {Object} data
*/
receiptMedicineNewTask(params){
const staff = uni.getStorageSync("staff");
return util.request({
url: config.urls.receiptMedicineNewTask,
data: {
acceptancePerson: params.acceptancePerson,
dsordertype: params.dsordertype,
tasksheetType: params.tasksheetType,
id: params.id,
}
});
},
/**
* 启动运单
* @param {Object} taskId
*/
startMedicineTransport(taskId){
return util.request({
url: config.urls.startMedicineTransport + taskId,
});
},
/**
* 完成药品运送
* @param {Object} type
* @param {Object} taskId
*/
completeMedicineTask(type, taskId){
const staff = uni.getStorageSync("staff");
return util.request({
url: config.urls.completeMedicineTask,
data: {
acceptancePerson: (staff==null || staff==undefined || staff == '')?null:staff.id,
finishWord: "完成送药",
id: taskId,
serviceItem: null,
serviceItemType: null,
materialconsumerecordSaveRequestList: null,
sheetId: taskId,
tasksheetType: taskType[type]
}
});
},
// 根据id查询
getTaskById(type, id){
console.log('getTaskByIdUrlMapper[type] + id',getTaskByIdUrlMapper[type] + id)
return util.request({
url: getTaskByIdUrlMapper[type] + id,
method: 'GET'
});
},
/**
* 根据sheedID查询运单下接收的标本
* @param {Object} params
*/
querySheetBySheetTd(sheetId){
return util.request({
url: config.urls.querySampleNewTask,
data: {
decendDatetime: null,
decstartDatetime: null,
index: 0,
page: null,
size: null,
sheetId: sheetId || null,
sort: null,
tasksheetState: null,
undertakePerson: null,
}
});
},
/**
* 扫码接收标本
* @param {Object} id
*/
receiptSampleNewTask(id){
return util.request({
url: config.urls.receiptSampleNewTask + id,
method: 'GET'
});
},
queryBarcodeFuzzySearch(params){
return util.request({
url: config.urls.querySampleNewTask,
data: {
barCode: params.barCode || null,
decendDatetime: params.endTime || null,
decstartDatetime: params.startTime || null,
index: 0,
page: null,
size: null,
sheetId: null,
sort: null,
tasksheetState: params.tasksheetState || null,
undertakePerson: params.undertakePerson || null,
}
});
},
/**
* 创建标本运单
* @param {Object} dtos
* @param {Object} source
*/
createSampleTaskSheet(dtos, source){
return util.request({
url: config.urls.createSampleTaskSheet,
data: {
dtos: dtos,
source: source,
}
})
},
/**
* 获得药品类型列表
*/
getMedicinetype(){
return util.request({
url: config.urls.getMedicineType,
method: 'GET'
})
}
}

20
src/service/unpload.js Normal file
View File

@ -0,0 +1,20 @@
import util from "../utils/util.js";
import config from "../config.js";
export default {
// 上传文件
upload(filePath) {
if (!filePath) {
return null;
}
return util.upload({
url: config.urls.unloadSign,
filePath: filePath,
})
.then((res) => {
res.data =
typeof res.data == "string" ? JSON.parse(res.data) : res.data;
return res;
});
},
};

6
src/shime-uni.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
export {}
declare module "vue" {
type Hooks = App.AppInstance & Page.PageInstance;
interface ComponentCustomOptions extends Hooks {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
src/static/icons/home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
src/static/icons/tasks.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
src/static/icons/图标.psd Normal file

Binary file not shown.

BIN
src/static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 619 KiB

17
src/types/externals.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
// 声明外部 JS 模块,避免 TS 提示缺少声明文件
declare module '@/utils/util.js' {
const v: any
export default v
}
declare module '@/service/queryService.js' {
const v: any
export default v
}
declare module '@/service/getService.js' {
const v: any
export default v
}
declare module '@/config.js' {
const v: any
export default v
}

76
src/uni.scss Normal file
View File

@ -0,0 +1,76 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color: #333; // 基本色
$uni-text-color-inverse: #fff; // 反色
$uni-text-color-grey: #999; // 辅助灰色如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable: #c0c0c0;
/* 背景颜色 */
$uni-bg-color: #fff;
$uni-bg-color-grey: #f8f8f8;
$uni-bg-color-hover: #f1f1f1; // 点击状态颜色
$uni-bg-color-mask: rgba(0, 0, 0, 0.4); // 遮罩颜色
/* 边框颜色 */
$uni-border-color: #c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm: 12px;
$uni-font-size-base: 14px;
$uni-font-size-lg: 16;
/* 图片尺寸 */
$uni-img-size-sm: 20px;
$uni-img-size-base: 26px;
$uni-img-size-lg: 40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2c405a; // 文章标题颜色
$uni-font-size-title: 20px;
$uni-color-subtitle: #555; // 二级标题颜色
$uni-font-size-subtitle: 18px;
$uni-color-paragraph: #3f536e; // 文章段落颜色
$uni-font-size-paragraph: 15px;

428
src/utils/md5.js Normal file
View File

@ -0,0 +1,428 @@
/*
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 1.1 Copyright (C) Paul Johnston 1999 - 2002.
* Code also contributed by Greg Holt
* See http://pajhome.org.uk/site/legal.html for details.
*/
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
function safe_add(x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF)
var msw = (x >> 16) + (y >> 16) + (lsw >> 16)
return (msw << 16) | (lsw & 0xFFFF)
}
/*
* Bitwise rotate a 32-bit number to the left.
*/
function rol(num, cnt) {
return (num << cnt) | (num >>> (32 - cnt))
}
/*
* These functions implement the four basic operations the algorithm uses.
*/
function cmn(q, a, b, x, s, t) {
return safe_add(rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b)
}
function ff(a, b, c, d, x, s, t) {
return cmn((b & c) | ((~b) & d), a, b, x, s, t)
}
function gg(a, b, c, d, x, s, t) {
return cmn((b & d) | (c & (~d)), a, b, x, s, t)
}
function hh(a, b, c, d, x, s, t) {
return cmn(b ^ c ^ d, a, b, x, s, t)
}
function ii(a, b, c, d, x, s, t) {
return cmn(c ^ (b | (~d)), a, b, x, s, t)
}
/*
* Calculate the MD5 of an array of little-endian words, producing an array
* of little-endian words.
*/
function coreMD5(x) {
var a = 1732584193
var b = -271733879
var c = -1732584194
var d = 271733878
for (var i = 0; i < x.length; i += 16) {
var olda = a
var oldb = b
var oldc = c
var oldd = d
a = ff(a, b, c, d, x[i + 0], 7, -680876936)
d = ff(d, a, b, c, x[i + 1], 12, -389564586)
c = ff(c, d, a, b, x[i + 2], 17, 606105819)
b = ff(b, c, d, a, x[i + 3], 22, -1044525330)
a = ff(a, b, c, d, x[i + 4], 7, -176418897)
d = ff(d, a, b, c, x[i + 5], 12, 1200080426)
c = ff(c, d, a, b, x[i + 6], 17, -1473231341)
b = ff(b, c, d, a, x[i + 7], 22, -45705983)
a = ff(a, b, c, d, x[i + 8], 7, 1770035416)
d = ff(d, a, b, c, x[i + 9], 12, -1958414417)
c = ff(c, d, a, b, x[i + 10], 17, -42063)
b = ff(b, c, d, a, x[i + 11], 22, -1990404162)
a = ff(a, b, c, d, x[i + 12], 7, 1804603682)
d = ff(d, a, b, c, x[i + 13], 12, -40341101)
c = ff(c, d, a, b, x[i + 14], 17, -1502002290)
b = ff(b, c, d, a, x[i + 15], 22, 1236535329)
a = gg(a, b, c, d, x[i + 1], 5, -165796510)
d = gg(d, a, b, c, x[i + 6], 9, -1069501632)
c = gg(c, d, a, b, x[i + 11], 14, 643717713)
b = gg(b, c, d, a, x[i + 0], 20, -373897302)
a = gg(a, b, c, d, x[i + 5], 5, -701558691)
d = gg(d, a, b, c, x[i + 10], 9, 38016083)
c = gg(c, d, a, b, x[i + 15], 14, -660478335)
b = gg(b, c, d, a, x[i + 4], 20, -405537848)
a = gg(a, b, c, d, x[i + 9], 5, 568446438)
d = gg(d, a, b, c, x[i + 14], 9, -1019803690)
c = gg(c, d, a, b, x[i + 3], 14, -187363961)
b = gg(b, c, d, a, x[i + 8], 20, 1163531501)
a = gg(a, b, c, d, x[i + 13], 5, -1444681467)
d = gg(d, a, b, c, x[i + 2], 9, -51403784)
c = gg(c, d, a, b, x[i + 7], 14, 1735328473)
b = gg(b, c, d, a, x[i + 12], 20, -1926607734)
a = hh(a, b, c, d, x[i + 5], 4, -378558)
d = hh(d, a, b, c, x[i + 8], 11, -2022574463)
c = hh(c, d, a, b, x[i + 11], 16, 1839030562)
b = hh(b, c, d, a, x[i + 14], 23, -35309556)
a = hh(a, b, c, d, x[i + 1], 4, -1530992060)
d = hh(d, a, b, c, x[i + 4], 11, 1272893353)
c = hh(c, d, a, b, x[i + 7], 16, -155497632)
b = hh(b, c, d, a, x[i + 10], 23, -1094730640)
a = hh(a, b, c, d, x[i + 13], 4, 681279174)
d = hh(d, a, b, c, x[i + 0], 11, -358537222)
c = hh(c, d, a, b, x[i + 3], 16, -722521979)
b = hh(b, c, d, a, x[i + 6], 23, 76029189)
a = hh(a, b, c, d, x[i + 9], 4, -640364487)
d = hh(d, a, b, c, x[i + 12], 11, -421815835)
c = hh(c, d, a, b, x[i + 15], 16, 530742520)
b = hh(b, c, d, a, x[i + 2], 23, -995338651)
a = ii(a, b, c, d, x[i + 0], 6, -198630844)
d = ii(d, a, b, c, x[i + 7], 10, 1126891415)
c = ii(c, d, a, b, x[i + 14], 15, -1416354905)
b = ii(b, c, d, a, x[i + 5], 21, -57434055)
a = ii(a, b, c, d, x[i + 12], 6, 1700485571)
d = ii(d, a, b, c, x[i + 3], 10, -1894986606)
c = ii(c, d, a, b, x[i + 10], 15, -1051523)
b = ii(b, c, d, a, x[i + 1], 21, -2054922799)
a = ii(a, b, c, d, x[i + 8], 6, 1873313359)
d = ii(d, a, b, c, x[i + 15], 10, -30611744)
c = ii(c, d, a, b, x[i + 6], 15, -1560198380)
b = ii(b, c, d, a, x[i + 13], 21, 1309151649)
a = ii(a, b, c, d, x[i + 4], 6, -145523070)
d = ii(d, a, b, c, x[i + 11], 10, -1120210379)
c = ii(c, d, a, b, x[i + 2], 15, 718787259)
b = ii(b, c, d, a, x[i + 9], 21, -343485551)
a = safe_add(a, olda)
b = safe_add(b, oldb)
c = safe_add(c, oldc)
d = safe_add(d, oldd)
}
return [a, b, c, d]
}
/*
* Convert an array of little-endian words to a hex string.
*/
function binl2hex(binarray) {
var hex_tab = "0123456789abcdef"
var str = ""
for (var i = 0; i < binarray.length * 4; i++) {
str += hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8 + 4)) & 0xF) +
hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8)) & 0xF)
}
return str
}
/*
* Convert an array of little-endian words to a base64 encoded string.
*/
function binl2b64(binarray) {
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
var str = ""
for (var i = 0; i < binarray.length * 32; i += 6) {
str += tab.charAt(((binarray[i >> 5] << (i % 32)) & 0x3F) |
((binarray[i >> 5 + 1] >> (32 - i % 32)) & 0x3F))
}
return str
}
/*
* Convert an 8-bit character string to a sequence of 16-word blocks, stored
* as an array, and append appropriate padding for MD4/5 calculation.
* If any of the characters are >255, the high byte is silently ignored.
*/
function str2binl(str) {
var nblk = ((str.length + 8) >> 6) + 1 // number of 16-word blocks  
var blks = new Array(nblk * 16)
for (var i = 0; i < nblk * 16; i++) blks[i] = 0
for (var i = 0; i < str.length; i++)
blks[i >> 2] |= (str.charCodeAt(i) & 0xFF) << ((i % 4) * 8)
blks[i >> 2] |= 0x80 << ((i % 4) * 8)
blks[nblk * 16 - 2] = str.length * 8
return blks
}
/*
* Convert a wide-character string to a sequence of 16-word blocks, stored as
* an array, and append appropriate padding for MD4/5 calculation.
*/
function strw2binl(str) {
var nblk = ((str.length + 4) >> 5) + 1 // number of 16-word blocks  
var blks = new Array(nblk * 16)
for (var i = 0; i < nblk * 16; i++) blks[i] = 0
for (var i = 0; i < str.length; i++)
blks[i >> 1] |= str.charCodeAt(i) << ((i % 2) * 16)
blks[i >> 1] |= 0x80 << ((i % 2) * 16)
blks[nblk * 16 - 2] = str.length * 16
return blks
}
/*
* External interface
*/
function hexMD5(str) {
return binl2hex(coreMD5(str2binl(str)))
}
function hexMD5w(str) {
return binl2hex(coreMD5(strw2binl(str)))
}
function b64MD5(str) {
return binl2b64(coreMD5(str2binl(str)))
}
function b64MD5w(str) {
return binl2b64(coreMD5(strw2binl(str)))
}
/* Backward compatibility */
function calcMD5(str) {
return binl2hex(coreMD5(str2binl(str)))
}
export default {
hexMD5: hexMD5
}

266
src/utils/util.js Normal file
View File

@ -0,0 +1,266 @@
import config from '../config.js';
const getStateCreateTime = (stateList, type) => {
if(stateList && stateList.length != 0){
const task = stateList.find(item => item.tasksheetState === type);
return task?.createTime;
}else{
return ''
}
}
const formatTime = date => {
date = new Date(date);
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return `${formatNumber(hour)}:${formatNumber(minute)}:${formatNumber(second)}`
}
const formatNumber = n => {
n = n.toString()
return n[1] ? n : '0' + n
}
const formatDate = date => {
date = new Date(date);
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
return `${year}-${formatNumber(month)}-${formatNumber(day)}`
}
const formatDateTime = date => {
return `${formatDate(date)} ${formatTime(date)}`
}
let loadingCount = 0;
let noError = true;
let timer = null;
const request = option => {
if (noError && loadingCount == 0) {
// uni.showLoading({
// title: '加载中',
// mask: true
// });
}
if (timer) {
loadingCount--;
clearTimeout(timer);
timer = null;
}
loadingCount++;
// console.log("request00:",config.baseUrl + option.url);
return new Promise((resolve, reject) => {
const commonSuccess = res => {
if (typeof res.data == 'object') {
let data = res.data.data || res.data.tableData;
res.data.tableData = data;
}
if (res.data.Token) {
uni.setStorageSync("token", res.data.Token);
}
if (res.statusCode != 200 || (res.data.code != undefined && res.data.code != 0 &&res.data.code != 1002)) {
res.statusCode = res.statusCode == 200 ? 500 : res.statusCode;
if (option.fail) {
option.fail(res);
}
return reject(res);
}
timer = setTimeout(() => {
loadingCount--;
// console.log("success loadingCount:", loadingCount);
if (loadingCount == 0) {
if (noError) {
// uni.hideLoading();
}
noError = true;
timer = null;
}
}, 300);
if (option.success) {
option.success(res);
}
resolve(res);
}
const commonFail = err => {
noError = false;
// uni.hideLoading();
if (--loadingCount == 0) {
noError = true;
}
if (option.fail) {
option.fail(err);
}
uni.showToast({
title: "网络错误: " + JSON.stringify(err),
icon: 'error', // 图标,有效值为 'success', 'loading', 'none'
duration: 3000 // 提示的延迟时间,单位毫秒,默认:1500
});
console.warn(`request: ${option.url}, ${JSON.stringify(err)}`);
reject(err);
}
//console.log("request0:",config.baseUrl + option.url);
let staff = uni.getStorageSync('staff');
let staffisnull = (staff == null || staff == undefined || staff == '');
uni.request({
url: config.baseUrl + option.url,
data: option.data,
header: option.header || {
'content-type': 'application/json',
'Authorization': staffisnull?null:`Bearer ${uni.getStorageSync('token')}`,
'hosp-ID': staffisnull?config.defaultHospId:staff.hosp_id,
'user-ID': staffisnull?null:staff.id,
'datetime': formatDateTime(new Date),
'createby': staffisnull?null:staff.id,
},
method: option.method || 'POST',
success: commonSuccess,
fail: commonFail,
complete: option.complete,
});
}).catch((err) => {
noError = false;
// uni.hideLoading();
if (--loadingCount == 0) {
noError = true;
}
//console.log("error loadingcount:", uni.getStorageSync('staff') == null?null:uni.getStorageSync('staff').hosp_id);
//console.log("err",err.data.message,"staff:",uni.getStorageSync('staff'));
if (err?.data?.message.includes("非法token")) {
//初始化
uni.setStorageSync('staff', null);
uni.setStorageSync('openid', null);
uni.setStorageSync('token', null);
uni.showToast({
title: "非法用户或登录过期,请重新登录。",
icon: 'error', // 图标,有效值为 'success', 'loading', 'none'
duration: 3000 // 提示的延迟时间,单位毫秒,默认:1500
});
} else {
uni.showToast({
title: "网络通信错误:" + JSON.stringify(err?.data?.msg || err),
icon: 'error', // 图标,有效值为 'success', 'loading', 'none'
duration: 3000 // 提示的延迟时间,单位毫秒,默认:1500
});
}
return err;
});
}
const upload = option => {
if (timer) {
loadingCount--;
clearTimeout(timer);
timer = null;
}
loadingCount++;
return new Promise((resolve, reject) => {
const ret = {
data: `{"mydata": {"filename": ""}}`,
};
const commonSuccess = (res) => {
timer = setTimeout(() => {
loadingCount--;
if (loadingCount == 0) {
uni.hideLoading();
timer = null;
}
}, 300);
if (option.success) {
option.success(res);
}
resolve(res);
};
var commonFail = (err) => {
noError = false;
uni.hideLoading();
loadingCount--;
uni.showToast({
title: "网络错误: " + JSON.stringify(err),
icon: 'error', // 图标,有效值为 'success', 'loading', 'none'
duration: 3000 // 提示的延迟时间,单位毫秒,默认:1500
});
reject(err);
};
if (!option.filePath) {
uni.hideLoading();
return resolve(ret);
}
let staff = uni.getStorageSync('staff');
let staffisnull = (staff == null || staff == undefined || staff == '');
uni.uploadFile({
url: config.baseUrl + option.url,
header: {
Authorization: staffisnull?null:`Bearer ${uni.getStorageSync('token')}`,
'hosp-ID': staffisnull?config.defaultHospId:staff.hosp_id,
'user-ID': staffisnull?null:staff.id
},
filePath: option.filePath,
name: "file" || option.name,
success: commonSuccess,
fail: commonFail,
complete: option.complete,
});
}).catch((err) => {
console.log('err',err)
noError = false;
uni.hideLoading();
if (--loadingCount == 0) {
noError = true;
}
uni.showToast({
title: "网络错误: " + JSON.stringify(err),
icon: 'error', // 图标,有效值为 'success', 'loading', 'none'
duration: 3000 // 提示的延迟时间,单位毫秒,默认:1500
});
return err;
});
}
function areArraysEqualById(arr1, arr2) {
// 如果数组长度不相等,直接返回 false
if (arr1.length !== arr2.length) return false;
// 创建两个 Map 用于存储每个数组的元素 id 及其出现的次数
const map1 = new Map();
const map2 = new Map();
// 遍历第一个数组,将元素 id 存储到 map1 中
for (let item of arr1) {
map1.set(item.id, (map1.get(item.id) || 0) + 1);
}
// 遍历第二个数组,将元素 id 存储到 map2 中
for (let item of arr2) {
map2.set(item.id, (map2.get(item.id) || 0) + 1);
}
// 比较两个 map 的键值对
if (map1.size !== map2.size) return false;
for (let [key, value] of map1) {
if (map2.get(key) !== value) return false;
}
return true;
}
//判断对象是否未空
function isNull(e) {
return e==null || e==undefined || e=='';
}
export default {
request: request,
formatTime: formatTime,
formatDate: formatDate,
formatDateTime: formatDateTime,
upload: upload,
areArraysEqualById: areArraysEqualById,
getStateCreateTime: getStateCreateTime,
isNull
}

13
tsconfig.json Normal file
View File

@ -0,0 +1,13 @@
{
"extends": "@vue/tsconfig/tsconfig.json",
"compilerOptions": {
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"lib": ["esnext", "dom"],
"types": ["@dcloudio/types"]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}

12
vite.config.ts Normal file
View File

@ -0,0 +1,12 @@
import { defineConfig } from "vite";
import uni from "@dcloudio/vite-plugin-uni";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [uni()],
// set publicDir: 'static',
publicDir: "static",
});