feat:综合服务适配
This commit is contained in:
1 parent
3f4e389b33
commit
536d066c9c
26 files changed
+824
-498
No files matched your search
@@ -17,13 +17,13 @@ VITE_PWD_ENC_KEY='pigxpigxpigxpigx'
|
||||
VITE_OAUTH2_APP_CLIENT='mini:mini'
|
||||
|
||||
# --------------------------- start 开发环境 start ----------------------------------
|
||||
# VITE_APP_DEV_SERVICE = https://stgl.pj.gov.cn/api
|
||||
VITE_APP_DEV_SERVICE = https://stgl.pj.gov.cn/api
|
||||
# VITE_APP_DEV_SERVICE = http://localhost:9999
|
||||
VITE_APP_DEV_SERVICE = https://rj4jv5rwqs.by.takin.cc
|
||||
# VITE_APP_DEV_SERVICE = https://dzc-test-1.fy.takin.cc
|
||||
|
||||
# VITE_IMAGE_DEV_PATH = http://localhost:9999
|
||||
# VITE_IMAGE_DEV_PATH = https://dzc-test-1.fy.takin.cc/api
|
||||
VITE_IMAGE_DEV_PATH = https://stgl.pj.gov.cn/api
|
||||
# VITE_IMAGE_DEV_PATH = https://rj4jv5rwqs.by.takin.cc
|
||||
# ----------------------------- end 开发环境 end ------------------------------------
|
||||
|
||||
# --------------------------- start 生产环境 start ----------------------------------
|
||||
|
||||
@@ -99,6 +99,13 @@
|
||||
"navigationBarTitleText": "webview页面",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/tabbar/integrated",
|
||||
"style": {
|
||||
"navigationBarTitleText": "综合服务",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"subPackages": [
|
||||
|
||||
@@ -357,17 +357,21 @@ const doFood = () => {
|
||||
<view v-for="(item, index) in state.bookingTimeList" :key="index" class="w-full flex items-center">
|
||||
<tm-radio class="w-full h-full px-16rpx" :value="item.id">
|
||||
<template v-slot:label="checked">
|
||||
<view class="text-30">{{ item.title }}</view>
|
||||
<view class="second-text-28 flex">
|
||||
<text class="w-1/2">可预订时间段:</text>
|
||||
<text class="w-1/2">{{ item.startTime.slice(0, -3) }}-{{ item.endTime.slice(0, -3) }}</text>
|
||||
</view>
|
||||
<view class="second-text-28 flex">
|
||||
<text class="w-1/2">用餐时间段:</text>
|
||||
<text class="w-1/2">
|
||||
{{ state.canteenDetail.jobTime?.split(",")[Number(index) + 1] ?
|
||||
state.canteenDetail.jobTime?.split(",")[Number(index) + 1] : "" }}
|
||||
</text>
|
||||
<view class="w-full">
|
||||
<view class="text-30rpx">{{ item.title }}</view>
|
||||
<view class="second-text-28 flex">
|
||||
<text class="w-1/2">可预订时间段:</text>
|
||||
<text class="w-1/2">
|
||||
{{ item.startTime.slice(0, -3) }}-{{ item.endTime.slice(0, -3) }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="second-text-28 flex">
|
||||
<text class="w-1/2">用餐时间段:</text>
|
||||
<text class="w-1/2">
|
||||
{{ state.canteenDetail.jobTime?.split(",")[Number(index) + 1] ?
|
||||
state.canteenDetail.jobTime?.split(",")[Number(index) + 1] : "" }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</tm-radio>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="feedback-detail">
|
||||
<crud-layout mainIsScroll title="意见反馈" :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<view v-if="!detail.title" class="loading-tip">加载中...</view>
|
||||
<view v-else>
|
||||
<view class="detail-header">
|
||||
@@ -11,29 +11,25 @@
|
||||
<view class="detail-content">
|
||||
<view class="content-text">{{ detail.description || '无描述' }}</view>
|
||||
<view v-if="imageList.length" class="image-list">
|
||||
<image
|
||||
v-for="(img, idx) in imageList"
|
||||
:key="idx"
|
||||
:src="img.startsWith('http') || img.startsWith('https') ? img : filePath(img)"
|
||||
class="feedback-img"
|
||||
mode="aspectFill"
|
||||
@error="handleImageError(img)"
|
||||
@load="handleImageLoad(img)"
|
||||
/>
|
||||
<image v-for="(img, idx) in imageList" :key="idx"
|
||||
:src="img.startsWith('http') || img.startsWith('https') ? img : filePath(img)" class="feedback-img"
|
||||
mode="aspectFill" @error="handleImageError(img)" @load="handleImageLoad(img)" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="detail-footer">
|
||||
<text class="create-time">提交时间:{{ formatTime(detail.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
import feedbackApi from '@/sheep/api/feedback'
|
||||
import { filePath } from '@/sheep/utils/file'
|
||||
import { SERVICE_IMAGE } from "@@/constant/index"
|
||||
|
||||
const detail = ref<any>({})
|
||||
const typeOptions = ref<{ value: string; label: string }[]>([])
|
||||
@@ -294,6 +290,7 @@ onLoad(async (options: any) => {
|
||||
gap: 16rpx;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.feedback-img {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="feedback-container">
|
||||
<crud-layout mainIsScroll title="意见反馈" :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<view class="feedback-tabs">
|
||||
<view :class="['tab', { active: activeTab === 0 }]" @click="activeTab = 0">提交反馈</view>
|
||||
<view :class="['tab', { active: activeTab === 1 }]" @click="activeTab = 1">我的反馈</view>
|
||||
@@ -56,8 +56,9 @@
|
||||
<view class="type-tag" :class="getTypeClass(item.type)">
|
||||
{{ typeOptionsLoaded ? getTypeLabel(item.type) : '加载中...' }}
|
||||
</view>
|
||||
<view class="status-tag" :class="getStatusClass(item.status)">{{ getStatusText(item.status)
|
||||
}}</view>
|
||||
<view class="status-tag" :class="getStatusClass(item.status)">
|
||||
{{ getStatusText(item.status) }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="item-title">{{ item.title }}</view>
|
||||
@@ -70,14 +71,17 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed, watch, nextTick } from 'vue'
|
||||
import feedbackApi from '@/sheep/api/feedback'
|
||||
import { onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app'
|
||||
|
||||
import feedbackApi from '@/sheep/api/feedback'
|
||||
import { filePath } from '@/sheep/utils/file'
|
||||
import { SERVICE_IMAGE } from "@@/constant/index"
|
||||
|
||||
import { useDefaultHeader } from '@/sheep/hooks/use-token'
|
||||
|
||||
const typeOptions = ref<{ value: string; label: string }[]>([])
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<view class="detail-wrapper">
|
||||
<crud-layout mainIsScroll title="失物招领" :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<view v-if="loading" class="loading">加载中...</view>
|
||||
|
||||
<view v-else-if="detail">
|
||||
<view class="detail-card">
|
||||
<view class="detail-title">{{ detail.title || '失物招领详情' }}</view>
|
||||
@@ -33,17 +32,20 @@
|
||||
<view class="section-content">{{ detail.remark }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="empty-state">暂无详情数据</view>
|
||||
</view>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import lostAndFoundApi from '@/sheep/api/lost-and-found'
|
||||
|
||||
import { filePath } from '@/sheep/utils/file'
|
||||
import { SERVICE_IMAGE } from "@@/constant/index"
|
||||
|
||||
interface LostAndFoundDetail {
|
||||
id: string
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view class="custom-nav">
|
||||
<view class="nav-back" @tap="goBack">
|
||||
<text class="back-arrow">←</text>
|
||||
</view>
|
||||
<view class="nav-title">失物招领</view>
|
||||
</view>
|
||||
<crud-layout mainIsScroll title="失物招领" :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<scroll-view scroll-y class="scroll-view" @scrolltolower="onLoadMore" refresher-enabled
|
||||
:refresher-triggered="refreshing" @refresherrefresh="onRefresh">
|
||||
<view v-if="list.length > 0">
|
||||
@@ -27,14 +21,18 @@
|
||||
<view v-if="loading" class="loading">加载中...</view>
|
||||
<view v-if="!hasMore && list.length > 0" class="no-more">没有更多了</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import lostAndFoundApi from '@/sheep/api/lost-and-found'
|
||||
|
||||
import { filePath } from "@@/utils/file"
|
||||
import { SERVICE_IMAGE } from "@@/constant/index"
|
||||
|
||||
interface LostAndFoundItem {
|
||||
id: string
|
||||
title: string
|
||||
@@ -161,13 +159,13 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.scroll-view {
|
||||
height: calc(100vh - 88rpx);
|
||||
margin-top: 88rpx;
|
||||
height: calc(100vh - 30rpx);
|
||||
margin-top: 30rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.item-card {
|
||||
margin: 12px;
|
||||
margin: 12px 0 0 0;
|
||||
padding: 16px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="booking-detail">
|
||||
<crud-layout mainIsScroll title="会议预定" :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<!-- 会议室信息卡片 -->
|
||||
<view class="room-card">
|
||||
<view class="room-header">
|
||||
@@ -71,12 +71,16 @@
|
||||
<text class="value remark">{{ detail.description }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
import { filePath } from "@/sheep/utils/file"
|
||||
import { SERVICE_IMAGE } from "@@/constant/index"
|
||||
|
||||
import meetingRoomApi from '@/sheep/api/meeting-room'
|
||||
|
||||
const detail = ref<any>({})
|
||||
@@ -96,7 +100,7 @@ function getStatusClass(status: string) {
|
||||
onLoad(async (options: any) => {
|
||||
const id = options.id
|
||||
if (!id) return
|
||||
const { code, data } = await meetingRoomApi.getBookingDetail(id)
|
||||
const { code, data }: any = await meetingRoomApi.getBookingDetail(id)
|
||||
if (code === 0 && data) {
|
||||
detail.value = data
|
||||
}
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 顶部自定义导航栏 -->
|
||||
<view class="custom-nav">
|
||||
<view class="nav-back" @tap="goBack">
|
||||
<text class="back-arrow">←</text>
|
||||
</view>
|
||||
<view class="nav-title">预约会议</view>
|
||||
</view>
|
||||
<crud-layout mainIsScroll title="预约会议" :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
|
||||
<!-- 会议室信息 -->
|
||||
<view class="room-info-box">
|
||||
@@ -95,13 +88,16 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
|
||||
import meetingRoomApi from '@/sheep/api/meeting-room'
|
||||
|
||||
import { filePath } from '@/sheep/utils/file'
|
||||
import { SERVICE_IMAGE } from "@@/constant/index"
|
||||
|
||||
// 定义类型
|
||||
interface TimeSlotItem {
|
||||
@@ -524,7 +520,7 @@ const goBack = () => {
|
||||
}
|
||||
|
||||
.room-info-box {
|
||||
margin-top: 100rpx;
|
||||
margin-top: 30rpx;
|
||||
display: flex;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
@@ -582,7 +578,7 @@ const goBack = () => {
|
||||
|
||||
.date-tabs {
|
||||
display: flex;
|
||||
margin: 24rpx 24rpx 0 24rpx;
|
||||
margin: 24rpx 0 0 0;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
@@ -604,7 +600,7 @@ const goBack = () => {
|
||||
}
|
||||
|
||||
.time-list-container {
|
||||
margin: 24rpx;
|
||||
margin: 24rpx 0 0 0;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
flex: 1;
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 顶部自定义导航栏 -->
|
||||
<view class="custom-nav">
|
||||
<view class="nav-back" @tap="goBack"> <text class="iconfont icon-fanhui"></text> 返回 </view>
|
||||
<view class="nav-title">预定会议室</view>
|
||||
</view>
|
||||
|
||||
<crud-layout mainIsScroll title="会议预定" :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<!-- 日期选择栏(简化版) -->
|
||||
<view class="date-bar">
|
||||
<text class="date-text">{{ todayText }}</text>
|
||||
@@ -55,13 +49,18 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<meeting-tabbar activeTab="home" />
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from "vue"
|
||||
|
||||
import meetingRoomApi from "@/sheep/api/meeting-room"
|
||||
import MeetingTabbar from "@/sheep/components/meeting-tabbar.vue"
|
||||
|
||||
import { filePath } from "@/sheep/utils/file"
|
||||
import { SERVICE_IMAGE } from "@@/constant/index"
|
||||
|
||||
const goBack = () => {
|
||||
uni.navigateBack()
|
||||
@@ -165,7 +164,7 @@ function getRoomImage(picUrl: string) {
|
||||
}
|
||||
|
||||
.date-bar {
|
||||
margin-top: 88rpx;
|
||||
margin-top: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
@@ -187,7 +186,7 @@ function getRoomImage(picUrl: string) {
|
||||
}
|
||||
|
||||
.room-card {
|
||||
margin: 24rpx 24rpx 0 24rpx;
|
||||
margin: 24rpx 0 0 0;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 顶部自定义导航栏 -->
|
||||
<view class="custom-nav">
|
||||
<view class="nav-title">我的预约</view>
|
||||
</view>
|
||||
|
||||
<crud-layout mainIsScroll title="会议预定" :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<!-- 顶部tab -->
|
||||
<view class="tabs">
|
||||
<view v-for="(tab, idx) in tabs" :key="tab" :class="['tab', idx === activeTab ? 'active' : '']"
|
||||
@tap="onTabChange(idx)">{{
|
||||
tab
|
||||
}}</view>
|
||||
}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 预约卡片列表 -->
|
||||
@@ -44,12 +40,18 @@
|
||||
<view class="empty-text">暂无预约</view>
|
||||
<view class="empty-tip">您还没有预约会议,快去预约吧!</view>
|
||||
</view>
|
||||
</view>
|
||||
<meeting-tabbar activeTab="mine" />
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue"
|
||||
|
||||
import meetingRoomApi from "@/sheep/api/meeting-room"
|
||||
import MeetingTabbar from "@/sheep/components/meeting-tabbar.vue"
|
||||
|
||||
import { filePath } from "@/sheep/utils/file"
|
||||
import { SERVICE_IMAGE } from "@@/constant/index"
|
||||
|
||||
const tabs = ["全部", "审核中", "审核成功", "已拒绝", "已取消"]
|
||||
const activeTab = ref(0)
|
||||
@@ -233,7 +235,7 @@ function viewDetail(item: any) {
|
||||
}
|
||||
|
||||
.card {
|
||||
margin: 24rpx;
|
||||
margin: 24rpx 0 0 0;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="repair-detail">
|
||||
<crud-layout mainIsScroll title="会议预定" :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<view class="detail-header">
|
||||
<view class="equipment-info">
|
||||
<text class="equipment-name">{{ detail.title || detail.equipmentName || '无标题' }}</text>
|
||||
@@ -7,7 +7,7 @@
|
||||
</view>
|
||||
<view class="status-tag" :class="getStatusClass(detail.status)">{{ getStatusText(detail.status) }}</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="detail-content">
|
||||
<view class="info-row">
|
||||
<text class="label">维修类型:</text>
|
||||
@@ -28,26 +28,20 @@
|
||||
<text class="description">{{ detail.description }}</text>
|
||||
</view>
|
||||
<view v-if="imageList.length" class="image-list">
|
||||
<image
|
||||
v-for="(img, idx) in imageList"
|
||||
:key="idx"
|
||||
:src="filePath(img)"
|
||||
class="repair-img"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<image v-for="(img, idx) in imageList" :key="idx" :src="filePath(img)" class="repair-img" mode="aspectFill" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view v-if="detail.repairResult" class="result-section">
|
||||
<view class="result-title">维修结果</view>
|
||||
<view class="result-content">{{ detail.repairResult }}</view>
|
||||
<view class="result-time">{{ formatTime(detail.repairTime) }}</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="detail-footer">
|
||||
<text class="create-time">申请时间:{{ formatTime(detail.createTime) }}</text>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 回复内容 -->
|
||||
<view v-if="hasHandleInfo" class="reply-section">
|
||||
<view class="reply-title">回复内容</view>
|
||||
@@ -55,28 +49,25 @@
|
||||
<text class="reply-text">{{ detail.handleRemark }}</text>
|
||||
</view>
|
||||
<view v-if="handleImageList.length" class="handle-image-list">
|
||||
<image
|
||||
v-for="(img, idx) in handleImageList"
|
||||
:key="idx"
|
||||
:src="filePath(img)"
|
||||
class="handle-img"
|
||||
mode="aspectFill"
|
||||
@click="previewHandleImage(idx)"
|
||||
/>
|
||||
<image v-for="(img, idx) in handleImageList" :key="idx" :src="filePath(img)" class="handle-img"
|
||||
mode="aspectFill" @click="previewHandleImage(idx)" />
|
||||
</view>
|
||||
<view class="reply-footer">
|
||||
<text class="handle-by" v-if="detail.handleBy">处理人:{{ detail.handleBy }}</text>
|
||||
<text class="handle-time" v-if="detail.handleTime">{{ formatTime(detail.handleTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
import maintenanceApi from '@/sheep/api/maintenance'
|
||||
|
||||
import { filePath } from "@/sheep/utils/file"
|
||||
import { SERVICE_IMAGE } from "@@/constant/index"
|
||||
|
||||
const detail = ref<any>({})
|
||||
const typeOptionsLoaded = ref(false)
|
||||
@@ -202,22 +193,22 @@ onLoad(async (options: any) => {
|
||||
uni.showToast({ title: '缺少维修ID', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
uni.showLoading({ title: '加载中...' })
|
||||
|
||||
|
||||
// 先加载维修类型选项
|
||||
await loadTypeOptions()
|
||||
|
||||
|
||||
// 再加载维修详情
|
||||
const response: any = await maintenanceApi.getRepairDetail(id)
|
||||
|
||||
|
||||
if (response.code === 0 && response.data) {
|
||||
detail.value = response.data
|
||||
} else {
|
||||
uni.showToast({ title: response.msg || '获取详情失败', icon: 'none' })
|
||||
}
|
||||
|
||||
|
||||
uni.hideLoading()
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
@@ -241,7 +232,7 @@ onLoad(async (options: any) => {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
|
||||
.equipment-info {
|
||||
.equipment-name {
|
||||
font-size: 32rpx;
|
||||
@@ -249,33 +240,33 @@ onLoad(async (options: any) => {
|
||||
color: #333;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
|
||||
.location {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.status-tag {
|
||||
padding: 12rpx 24rpx;
|
||||
border-radius: 24rpx;
|
||||
font-size: 26rpx;
|
||||
|
||||
|
||||
&.status-pending {
|
||||
background: #fff3e0;
|
||||
color: #f57c00;
|
||||
}
|
||||
|
||||
|
||||
&.status-processing {
|
||||
background: #e8f5e8;
|
||||
color: #388e3c;
|
||||
}
|
||||
|
||||
|
||||
&.status-completed {
|
||||
background: #e3f2fd;
|
||||
color: #1976d2;
|
||||
}
|
||||
|
||||
|
||||
&.status-cancelled {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
@@ -288,37 +279,37 @@ onLoad(async (options: any) => {
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
width: 160rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.description-section {
|
||||
margin-top: 24rpx;
|
||||
padding-top: 24rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
display: block;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
|
||||
.description {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
@@ -332,21 +323,21 @@ onLoad(async (options: any) => {
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
|
||||
.result-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
|
||||
.result-content {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
|
||||
.result-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
@@ -359,7 +350,7 @@ onLoad(async (options: any) => {
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
|
||||
.reply-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
@@ -368,24 +359,24 @@ onLoad(async (options: any) => {
|
||||
padding-bottom: 16rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
|
||||
.reply-content {
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
|
||||
.reply-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.handle-image-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
|
||||
.handle-img {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
@@ -393,19 +384,19 @@ onLoad(async (options: any) => {
|
||||
background: #f7f8fa;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
|
||||
.reply-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 16rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
|
||||
|
||||
.handle-by {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
.handle-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
@@ -417,7 +408,7 @@ onLoad(async (options: any) => {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
|
||||
|
||||
.create-time {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
@@ -430,6 +421,7 @@ onLoad(async (options: any) => {
|
||||
gap: 16rpx;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.repair-img {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
@@ -437,4 +429,4 @@ onLoad(async (options: any) => {
|
||||
background: #f7f8fa;
|
||||
object-fit: cover;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<view class="repair-container">
|
||||
<crud-layout mainIsScroll title="会议预定" :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<view class="repair-tabs">
|
||||
<view :class="['tab', {active: activeTab === 0} ]" @click="activeTab = 0">维修申请</view>
|
||||
<view :class="['tab', {active: activeTab === 1} ]" @click="activeTab = 1">维修记录</view>
|
||||
<view :class="['tab', { active: activeTab === 0 }]" @click="activeTab = 0">维修申请</view>
|
||||
<view :class="['tab', { active: activeTab === 1 }]" @click="activeTab = 1">维修记录</view>
|
||||
</view>
|
||||
<view v-if="activeTab === 0">
|
||||
<!-- 维修申请表单 -->
|
||||
@@ -32,23 +32,12 @@
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">图片</view>
|
||||
<tm-upload-photo
|
||||
v-model="uploadFileUrl"
|
||||
:url="'/api/admin/sys-file/upload'"
|
||||
:maxCount="5"
|
||||
:maxFileSize="10 * 1024 * 1024"
|
||||
:beforeComplete="beforeComplete"
|
||||
:header="header"
|
||||
/>
|
||||
<tm-upload-photo v-model="uploadFileUrl" :url="'/api/admin/sys-file/upload'" :maxCount="5"
|
||||
:maxFileSize="10 * 1024 * 1024" :beforeComplete="beforeComplete" :header="header" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">故障描述</view>
|
||||
<textarea
|
||||
v-model="description"
|
||||
placeholder="请详细描述设备故障情况..."
|
||||
class="description-textarea"
|
||||
maxlength="300"
|
||||
/>
|
||||
<textarea v-model="description" placeholder="请详细描述设备故障情况..." class="description-textarea" maxlength="300" />
|
||||
<view class="char-count">{{ description.length }}/300</view>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
@@ -70,12 +59,7 @@
|
||||
暂无维修记录
|
||||
</view>
|
||||
<view v-else>
|
||||
<view
|
||||
v-for="item in repairList"
|
||||
:key="item.id"
|
||||
class="repair-item"
|
||||
@click="viewDetail(item)"
|
||||
>
|
||||
<view v-for="item in repairList" :key="item.id" class="repair-item" @click="viewDetail(item)">
|
||||
<view class="item-header">
|
||||
<view class="type-tag" :class="getTypeClass(item.type)">
|
||||
{{ typeOptionsLoaded ? getTypeLabel(item.type) : '加载中...' }}
|
||||
@@ -96,15 +80,18 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch, nextTick } from 'vue'
|
||||
import { onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app'
|
||||
import maintenanceApi from '@/sheep/api/maintenance'
|
||||
|
||||
import { hooks } from '@@/index'
|
||||
|
||||
import maintenanceApi from '@/sheep/api/maintenance'
|
||||
import { filePath } from '@/sheep/utils/file'
|
||||
import { SERVICE_IMAGE } from "@@/constant/index"
|
||||
|
||||
const typeOptions = ref<any[]>([])
|
||||
const typeOptionsLoaded = ref(false)
|
||||
@@ -250,7 +237,7 @@ async function submitRepair() {
|
||||
if (res.code === 0 && res.data && res.data.url) {
|
||||
picUrl.push(res.data.url)
|
||||
}
|
||||
} catch {}
|
||||
} catch { }
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -388,6 +375,7 @@ onMounted(() => {
|
||||
border-radius: 16rpx 16rpx 0 0;
|
||||
overflow: hidden;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.tab {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
@@ -395,6 +383,7 @@ onMounted(() => {
|
||||
font-size: 30rpx;
|
||||
color: #888;
|
||||
background: #f7f8fa;
|
||||
|
||||
&.active {
|
||||
color: #1777FF;
|
||||
background: #fff;
|
||||
|
||||
+321
-314
@@ -1,357 +1,364 @@
|
||||
<template>
|
||||
<view class="todo-page">
|
||||
<view class="todo-tabs">
|
||||
<view :class="['tab-item', activeTab === 0 ? 'active' : '']" @click="switchTab(0)">我的待办</view>
|
||||
<view :class="['tab-item', activeTab === 1 ? 'active' : '']" @click="switchTab(1)">我的已办</view>
|
||||
</view>
|
||||
<view class="todo-list">
|
||||
<view v-if="todoList.length === 0 && !loading" class="todo-empty">
|
||||
<image src="/static/icon/empty-todo.png" class="empty-img" />
|
||||
<view class="empty-text">暂无数据</view>
|
||||
<crud-layout title="工作代办" :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<view class="todo-page">
|
||||
<view class="todo-tabs">
|
||||
<view :class="['tab-item', activeTab === 0 ? 'active' : '']" @click="switchTab(0)">我的待办</view>
|
||||
<view :class="['tab-item', activeTab === 1 ? 'active' : '']" @click="switchTab(1)">我的已办</view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view v-for="item in todoList" :key="item.taskId || item.id" class="todo-card">
|
||||
<view class="card-bar"></view>
|
||||
<view class="todo-card-content">
|
||||
<view class="todo-card-title">
|
||||
<text class="group">{{ item.groupName }}</text>
|
||||
<text class="process">{{ item.processName }}</text>
|
||||
</view>
|
||||
<view class="todo-card-info">
|
||||
<view class="info-row">
|
||||
<text class="label">发起人:</text>
|
||||
<text class="value">{{ item.rootUserName }}</text>
|
||||
<view class="todo-list">
|
||||
<view v-if="todoList.length === 0 && !loading" class="todo-empty">
|
||||
<image src="/static/icon/empty-todo.png" class="empty-img" />
|
||||
<view class="empty-text">暂无数据</view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view v-for="item in todoList" :key="item.taskId || item.id" class="todo-card">
|
||||
<view class="card-bar"></view>
|
||||
<view class="todo-card-content">
|
||||
<view class="todo-card-title">
|
||||
<text class="group">{{ item.groupName }}</text>
|
||||
<text class="process">{{ item.processName }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">发起时间:</text>
|
||||
<text class="value">{{ item.startTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="todo-card-actions">
|
||||
<button v-if="activeTab === 0" class="todo-btn process" @click="startProcess(item)">开始处理</button>
|
||||
<button v-if="activeTab === 1" class="todo-btn view" @click="viewProcess(item)">查看</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="loading" class="loading-tip">加载中...</view>
|
||||
<view v-else-if="finished" class="no-more-tip">没有更多了</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 审核意见弹窗 -->
|
||||
<view v-if="showCommentModal" class="modal-overlay comment-modal" @tap="hideCommentModal">
|
||||
<view class="modal-content" @tap.stop>
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">提交审核</text>
|
||||
<text class="modal-close" @tap="hideCommentModal">×</text>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<view class="comment-section">
|
||||
<text class="comment-label">审核意见</text>
|
||||
<textarea v-model="approvalComment" class="comment-textarea" placeholder="请输入审核意见..."
|
||||
maxlength="100"></textarea>
|
||||
<view class="char-count">{{ approvalComment.length }}/100</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-footer">
|
||||
<view class="modal-btn cancel-btn" @tap="hideCommentModal">取消</view>
|
||||
<view class="modal-btn confirm-btn" @tap="confirmApproval">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 转办人员选择弹窗 -->
|
||||
<view v-if="showDelegateModal" class="modal-overlay delegate-modal" @tap="hideDelegateModal">
|
||||
<view class="modal-content delegate-modal" @tap.stop>
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">选择转办人员</text>
|
||||
<text class="modal-close" @tap="hideDelegateModal">×</text>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-section">
|
||||
<input v-model="searchKeyword" class="search-input" placeholder="搜索" @input="filterUsers" />
|
||||
</view>
|
||||
|
||||
<!-- 面包屑导航 -->
|
||||
<view v-if="breadcrumbPath.length > 1" class="breadcrumb-section">
|
||||
<view class="breadcrumb">
|
||||
<view v-for="(item, index) in breadcrumbPath" :key="item.id" class="breadcrumb-item"
|
||||
@tap="navigateToBreadcrumb(index)">
|
||||
<text class="breadcrumb-text">{{ item.name }}</text>
|
||||
<text v-if="index < breadcrumbPath.length - 1" class="breadcrumb-separator">></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户列表 -->
|
||||
<view class="user-list">
|
||||
<!-- 部门列表 -->
|
||||
<view class="section-title">部门</view>
|
||||
<view v-for="dept in filteredDepartments" :key="dept.id" class="user-item" @tap="selectUser(dept)">
|
||||
<view class="user-checkbox" :class="{ checked: selectedUser?.id === dept.id }">
|
||||
<text v-if="selectedUser?.id === dept.id" class="check-icon">✓</text>
|
||||
</view>
|
||||
<view class="user-icon dept-icon">🏢</view>
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{ dept.name }}</text>
|
||||
<text class="user-type">部门</text>
|
||||
</view>
|
||||
<text class="subordinate-link">下级</text>
|
||||
</view>
|
||||
|
||||
<!-- 员工列表 -->
|
||||
<view class="section-title">员工</view>
|
||||
<view v-for="employee in filteredEmployees" :key="employee.id" class="user-item"
|
||||
@tap="selectUser(employee)">
|
||||
<view class="user-checkbox" :class="{ checked: selectedUser?.id === employee.id }">
|
||||
<text v-if="selectedUser?.id === employee.id" class="check-icon">✓</text>
|
||||
</view>
|
||||
<view class="user-icon employee-icon">
|
||||
<image v-if="employee.avatar" :src="employee.avatar" class="avatar-img" />
|
||||
<text v-else class="avatar-text">{{ employee.name.charAt(0) }}</text>
|
||||
</view>
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{ employee.name }}</text>
|
||||
<text class="user-type">员工</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-footer">
|
||||
<view class="modal-btn cancel-btn" @tap="hideDelegateModal">取消</view>
|
||||
<view class="modal-btn confirm-btn" @tap="confirmDelegateUser">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 转办确认弹窗 -->
|
||||
<view v-if="showDelegateConfirmModal" class="modal-overlay delegate-confirm-modal" @tap="hideDelegateConfirmModal">
|
||||
<view class="modal-content" @tap.stop>
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">转办确认</text>
|
||||
<text class="modal-close" @tap="hideDelegateConfirmModal">×</text>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<view class="delegate-info">
|
||||
<view class="info-item">
|
||||
<text class="info-label">转办给:</text>
|
||||
<text class="info-value">{{ selectedUser?.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="comment-section">
|
||||
<text class="comment-label">审核意见 *</text>
|
||||
<textarea v-model="delegateComment" class="comment-textarea" placeholder="请输入审核意见 (2-100字)"
|
||||
maxlength="100"></textarea>
|
||||
<view class="char-count">{{ delegateComment.length }}/100</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-footer">
|
||||
<view class="modal-btn cancel-btn" @tap="hideDelegateConfirmModal">取消</view>
|
||||
<view class="modal-btn confirm-btn" @tap="confirmDelegate">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 审批弹窗 -->
|
||||
<view v-if="showApprovalModal" class="modal-overlay" @tap="hideApprovalModal">
|
||||
<view class="modal-content" @tap.stop>
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">审批处理</text>
|
||||
<text class="modal-close" @tap="hideApprovalModal">×</text>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<!-- 动态字段渲染 - 根据 formItems 渲染 -->
|
||||
<view v-if="currentTask.formItems && currentTask.formItems.length > 0">
|
||||
<view v-for="field in currentTask.formItems" :key="field.id" class="form-item">
|
||||
<text class="form-label">{{ field.name }}{{ field.required ? ' *' : '' }}</text>
|
||||
<view v-if="field.type === 'Textarea'" class="form-textarea">
|
||||
{{ field.props?.value || '' }}
|
||||
</view>
|
||||
<view v-else-if="field.type === 'Sequence'" class="form-sequence">
|
||||
{{ field.props?.value || field.placeholder || '未知' }}
|
||||
</view>
|
||||
<view v-else class="form-input">{{ field.props?.value || '未知' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 如果没有动态字段,显示默认字段 -->
|
||||
<view v-else>
|
||||
<!-- 流水号 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">流水号</text>
|
||||
<view class="form-value">{{ currentTask.serialNumber || '未知流水号' }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 会议室 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">* 会议室</text>
|
||||
<view class="form-input">{{ currentTask.roomName || '未知会议室' }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 预约人 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">* 预约人</text>
|
||||
<view class="form-input">{{ currentTask.applicantName || '未知申请人' }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 预约时间 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">* 预约时间</text>
|
||||
<view class="form-input">{{ currentTask.meetingTime || '未知时间' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 动态审批流程 - 根据 approvalFlow 渲染 -->
|
||||
<view class="approval-flow">
|
||||
<view v-if="currentTask.approvalFlow && currentTask.approvalFlow.length > 0">
|
||||
<view v-for="(flow, index) in currentTask.approvalFlow" :key="flow.id" class="flow-item">
|
||||
<view class="flow-icon" :class="flow.status === 2 ? 'completed' : 'pending'">
|
||||
{{ flow.status === 2 ? '✓' : '⟲' }}
|
||||
<view class="todo-card-info">
|
||||
<view class="info-row">
|
||||
<text class="label">发起人:</text>
|
||||
<text class="value">{{ item.rootUserName }}</text>
|
||||
</view>
|
||||
<view class="flow-content">
|
||||
<text class="flow-label">{{ flow.name }}{{ flow.placeholder ? ` [${flow.placeholder}]` : '' }}</text>
|
||||
<view v-if="flow.userVoList && flow.userVoList.length > 0" class="flow-users">
|
||||
<view v-for="user in flow.userVoList" :key="user.id" class="flow-user">
|
||||
<view class="user-avatar">
|
||||
<image v-if="user.avatar" :src="user.avatar" class="avatar-img" />
|
||||
<text v-else class="avatar-text">{{ user.name?.charAt(0) || '?' }}</text>
|
||||
<view class="info-row">
|
||||
<text class="label">发起时间:</text>
|
||||
<text class="value">{{ item.startTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="todo-card-actions">
|
||||
<button v-if="activeTab === 0" class="todo-btn process" @click="startProcess(item)">开始处理</button>
|
||||
<button v-if="activeTab === 1" class="todo-btn view" @click="viewProcess(item)">查看</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="loading" class="loading-tip">加载中...</view>
|
||||
<view v-else-if="finished" class="no-more-tip">没有更多了</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 审核意见弹窗 -->
|
||||
<view v-if="showCommentModal" class="modal-overlay comment-modal" @tap="hideCommentModal">
|
||||
<view class="modal-content" @tap.stop>
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">提交审核</text>
|
||||
<text class="modal-close" @tap="hideCommentModal">×</text>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<view class="comment-section">
|
||||
<text class="comment-label">审核意见</text>
|
||||
<textarea v-model="approvalComment" class="comment-textarea" placeholder="请输入审核意见..."
|
||||
maxlength="100"></textarea>
|
||||
<view class="char-count">{{ approvalComment.length }}/100</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-footer">
|
||||
<view class="modal-btn cancel-btn" @tap="hideCommentModal">取消</view>
|
||||
<view class="modal-btn confirm-btn" @tap="confirmApproval">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 转办人员选择弹窗 -->
|
||||
<view v-if="showDelegateModal" class="modal-overlay delegate-modal" @tap="hideDelegateModal">
|
||||
<view class="modal-content delegate-modal" @tap.stop>
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">选择转办人员</text>
|
||||
<text class="modal-close" @tap="hideDelegateModal">×</text>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-section">
|
||||
<input v-model="searchKeyword" class="search-input" placeholder="搜索" @input="filterUsers" />
|
||||
</view>
|
||||
|
||||
<!-- 面包屑导航 -->
|
||||
<view v-if="breadcrumbPath.length > 1" class="breadcrumb-section">
|
||||
<view class="breadcrumb">
|
||||
<view v-for="(item, index) in breadcrumbPath" :key="item.id" class="breadcrumb-item"
|
||||
@tap="navigateToBreadcrumb(index)">
|
||||
<text class="breadcrumb-text">{{ item.name }}</text>
|
||||
<text v-if="index < breadcrumbPath.length - 1" class="breadcrumb-separator">></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户列表 -->
|
||||
<view class="user-list">
|
||||
<!-- 部门列表 -->
|
||||
<view class="section-title">部门</view>
|
||||
<view v-for="dept in filteredDepartments" :key="dept.id" class="user-item" @tap="selectUser(dept)">
|
||||
<view class="user-checkbox" :class="{ checked: selectedUser?.id === dept.id }">
|
||||
<text v-if="selectedUser?.id === dept.id" class="check-icon">✓</text>
|
||||
</view>
|
||||
<view class="user-icon dept-icon">🏢</view>
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{ dept.name }}</text>
|
||||
<text class="user-type">部门</text>
|
||||
</view>
|
||||
<text class="subordinate-link">下级</text>
|
||||
</view>
|
||||
|
||||
<!-- 员工列表 -->
|
||||
<view class="section-title">员工</view>
|
||||
<view v-for="employee in filteredEmployees" :key="employee.id" class="user-item"
|
||||
@tap="selectUser(employee)">
|
||||
<view class="user-checkbox" :class="{ checked: selectedUser?.id === employee.id }">
|
||||
<text v-if="selectedUser?.id === employee.id" class="check-icon">✓</text>
|
||||
</view>
|
||||
<view class="user-icon employee-icon">
|
||||
<image v-if="employee.avatar" :src="employee.avatar" class="avatar-img" />
|
||||
<text v-else class="avatar-text">{{ employee.name.charAt(0) }}</text>
|
||||
</view>
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{ employee.name }}</text>
|
||||
<text class="user-type">员工</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-footer">
|
||||
<view class="modal-btn cancel-btn" @tap="hideDelegateModal">取消</view>
|
||||
<view class="modal-btn confirm-btn" @tap="confirmDelegateUser">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 转办确认弹窗 -->
|
||||
<view v-if="showDelegateConfirmModal" class="modal-overlay delegate-confirm-modal"
|
||||
@tap="hideDelegateConfirmModal">
|
||||
<view class="modal-content" @tap.stop>
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">转办确认</text>
|
||||
<text class="modal-close" @tap="hideDelegateConfirmModal">×</text>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<view class="delegate-info">
|
||||
<view class="info-item">
|
||||
<text class="info-label">转办给:</text>
|
||||
<text class="info-value">{{ selectedUser?.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="comment-section">
|
||||
<text class="comment-label">审核意见 *</text>
|
||||
<textarea v-model="delegateComment" class="comment-textarea" placeholder="请输入审核意见 (2-100字)"
|
||||
maxlength="100"></textarea>
|
||||
<view class="char-count">{{ delegateComment.length }}/100</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-footer">
|
||||
<view class="modal-btn cancel-btn" @tap="hideDelegateConfirmModal">取消</view>
|
||||
<view class="modal-btn confirm-btn" @tap="confirmDelegate">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 审批弹窗 -->
|
||||
<view v-if="showApprovalModal" class="modal-overlay" @tap="hideApprovalModal">
|
||||
<view class="modal-content" @tap.stop>
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">审批处理</text>
|
||||
<text class="modal-close" @tap="hideApprovalModal">×</text>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<!-- 动态字段渲染 - 根据 formItems 渲染 -->
|
||||
<view v-if="currentTask.formItems && currentTask.formItems.length > 0">
|
||||
<view v-for="field in currentTask.formItems" :key="field.id" class="form-item">
|
||||
<text class="form-label">{{ field.name }}{{ field.required ? ' *' : '' }}</text>
|
||||
<view v-if="field.type === 'Textarea'" class="form-textarea">
|
||||
{{ field.props?.value || '' }}
|
||||
</view>
|
||||
<view v-else-if="field.type === 'Sequence'" class="form-sequence">
|
||||
{{ field.props?.value || field.placeholder || '未知' }}
|
||||
</view>
|
||||
<view v-else class="form-input">{{ field.props?.value || '未知' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 如果没有动态字段,显示默认字段 -->
|
||||
<view v-else>
|
||||
<!-- 流水号 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">流水号</text>
|
||||
<view class="form-value">{{ currentTask.serialNumber || '未知流水号' }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 会议室 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">* 会议室</text>
|
||||
<view class="form-input">{{ currentTask.roomName || '未知会议室' }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 预约人 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">* 预约人</text>
|
||||
<view class="form-input">{{ currentTask.applicantName || '未知申请人' }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 预约时间 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">* 预约时间</text>
|
||||
<view class="form-input">{{ currentTask.meetingTime || '未知时间' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 动态审批流程 - 根据 approvalFlow 渲染 -->
|
||||
<view class="approval-flow">
|
||||
<view v-if="currentTask.approvalFlow && currentTask.approvalFlow.length > 0">
|
||||
<view v-for="(flow, index) in currentTask.approvalFlow" :key="flow.id" class="flow-item">
|
||||
<view class="flow-icon" :class="flow.status === 2 ? 'completed' : 'pending'">
|
||||
{{ flow.status === 2 ? '✓' : '⟲' }}
|
||||
</view>
|
||||
<view class="flow-content">
|
||||
<text class="flow-label">{{ flow.name }}{{ flow.placeholder ? ` [${flow.placeholder}]` : ''
|
||||
}}</text>
|
||||
<view v-if="flow.userVoList && flow.userVoList.length > 0" class="flow-users">
|
||||
<view v-for="user in flow.userVoList" :key="user.id" class="flow-user">
|
||||
<view class="user-avatar">
|
||||
<image v-if="user.avatar" :src="user.avatar" class="avatar-img" />
|
||||
<text v-else class="avatar-text">{{ user.name?.charAt(0) || '?' }}</text>
|
||||
</view>
|
||||
<text class="user-name">{{ user.name || '未知用户' }}</text>
|
||||
</view>
|
||||
<text class="user-name">{{ user.name || '未知用户' }}</text>
|
||||
</view>
|
||||
<view v-else class="flow-user">
|
||||
<view class="user-avatar"></view>
|
||||
<text class="user-name">待分配</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="flow-user">
|
||||
<view class="user-avatar"></view>
|
||||
<text class="user-name">待分配</text>
|
||||
</view>
|
||||
<view v-if="index < currentTask.approvalFlow.length - 1" class="flow-line"></view>
|
||||
</view>
|
||||
<view v-if="index < currentTask.approvalFlow.length - 1" class="flow-line"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 如果没有动态审批流程,显示默认流程 -->
|
||||
<view v-else>
|
||||
<view class="flow-item">
|
||||
<view class="flow-icon completed">✓</view>
|
||||
<view class="flow-content">
|
||||
<text class="flow-label">发起人</text>
|
||||
<view class="flow-user">
|
||||
<view class="user-avatar"></view>
|
||||
<text class="user-name">{{ currentTask.applicantName || '未知申请人' }}</text>
|
||||
<!-- 如果没有动态审批流程,显示默认流程 -->
|
||||
<view v-else>
|
||||
<view class="flow-item">
|
||||
<view class="flow-icon completed">✓</view>
|
||||
<view class="flow-content">
|
||||
<text class="flow-label">发起人</text>
|
||||
<view class="flow-user">
|
||||
<view class="user-avatar"></view>
|
||||
<text class="user-name">{{ currentTask.applicantName || '未知申请人' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flow-line"></view>
|
||||
<view class="flow-item">
|
||||
<view class="flow-icon pending">⟲</view>
|
||||
<view class="flow-content">
|
||||
<text class="flow-label">审批人</text>
|
||||
<view class="flow-user">
|
||||
<view class="user-avatar"></view>
|
||||
<text class="user-name">当前用户</text>
|
||||
<view class="flow-line"></view>
|
||||
<view class="flow-item">
|
||||
<view class="flow-icon pending">⟲</view>
|
||||
<view class="flow-content">
|
||||
<text class="flow-label">审批人</text>
|
||||
<view class="flow-user">
|
||||
<view class="user-avatar"></view>
|
||||
<text class="user-name">当前用户</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-footer">
|
||||
<!-- 查看模式下只显示关闭按钮 -->
|
||||
<view v-if="currentTask.isViewMode" class="modal-btn close-btn" @tap="hideApprovalModal">
|
||||
<text class="btn-icon">×</text>
|
||||
<text class="btn-text">关闭</text>
|
||||
<view class="modal-footer">
|
||||
<!-- 查看模式下只显示关闭按钮 -->
|
||||
<view v-if="currentTask.isViewMode" class="modal-btn close-btn" @tap="hideApprovalModal">
|
||||
<text class="btn-icon">×</text>
|
||||
<text class="btn-text">关闭</text>
|
||||
</view>
|
||||
|
||||
<!-- 审批模式下显示操作按钮 -->
|
||||
<template v-else>
|
||||
<view class="modal-btn reject-btn" @tap="handleReject">
|
||||
<text class="btn-icon">✕</text>
|
||||
<text class="btn-text">拒绝</text>
|
||||
</view>
|
||||
<view class="modal-btn submit-btn" @tap="handleSubmit">
|
||||
<text class="btn-icon">✓</text>
|
||||
<text class="btn-text">同意</text>
|
||||
</view>
|
||||
<view class="modal-btn transfer-btn" @tap="handleTransfer">
|
||||
<text class="btn-icon">↗</text>
|
||||
<text class="btn-text">转办</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<!-- 审批模式下显示操作按钮 -->
|
||||
<template v-else>
|
||||
<view class="modal-btn reject-btn" @tap="handleReject">
|
||||
<text class="btn-icon">✕</text>
|
||||
<text class="btn-text">拒绝</text>
|
||||
</view>
|
||||
<view class="modal-btn submit-btn" @tap="handleSubmit">
|
||||
<text class="btn-icon">✓</text>
|
||||
<text class="btn-text">同意</text>
|
||||
</view>
|
||||
<view class="modal-btn transfer-btn" @tap="handleTransfer">
|
||||
<text class="btn-icon">↗</text>
|
||||
<text class="btn-text">转办</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 转办表单弹窗 -->
|
||||
<view v-if="showDelegateFormModal" class="modal-overlay delegate-form-modal" @tap="hideDelegateFormModal">
|
||||
<view class="modal-content delegate-form-content" @tap.stop>
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">转办</text>
|
||||
<view class="close-btn" @tap="hideDelegateFormModal">
|
||||
<text class="iconfont icon-guanbi"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="delegate-form">
|
||||
<!-- 选择转办人员 -->
|
||||
<view class="form-section">
|
||||
<view class="section-title">
|
||||
<text class="title-text">选择转办人员</text>
|
||||
<text class="required">*</text>
|
||||
<!-- 转办表单弹窗 -->
|
||||
<view v-if="showDelegateFormModal" class="modal-overlay delegate-form-modal" @tap="hideDelegateFormModal">
|
||||
<view class="modal-content delegate-form-content" @tap.stop>
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">转办</text>
|
||||
<view class="close-btn" @tap="hideDelegateFormModal">
|
||||
<text class="iconfont icon-guanbi"></text>
|
||||
</view>
|
||||
<view class="user-selector" @tap="showUserSelection">
|
||||
<view v-if="selectedUser" class="selected-user-tag">
|
||||
<text class="user-tag-name">{{ selectedUser.name }}</text>
|
||||
<text class="user-tag-remove" @tap.stop="removeSelectedUser">×</text>
|
||||
</view>
|
||||
|
||||
<view class="delegate-form">
|
||||
<!-- 选择转办人员 -->
|
||||
<view class="form-section">
|
||||
<view class="section-title">
|
||||
<text class="title-text">选择转办人员</text>
|
||||
<text class="required">*</text>
|
||||
</view>
|
||||
<view class="add-user-btn">
|
||||
<text class="add-icon">+</text>
|
||||
<view class="user-selector" @tap="showUserSelection">
|
||||
<view v-if="selectedUser" class="selected-user-tag">
|
||||
<text class="user-tag-name">{{ selectedUser.name }}</text>
|
||||
<text class="user-tag-remove" @tap.stop="removeSelectedUser">×</text>
|
||||
</view>
|
||||
<view class="add-user-btn">
|
||||
<text class="add-icon">+</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="userSelectError" class="error-message">{{ userSelectError }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 审核意见 -->
|
||||
<view class="form-section">
|
||||
<view class="section-title">
|
||||
<text class="title-text">审核意见</text>
|
||||
<text class="required">*</text>
|
||||
</view>
|
||||
<view class="comment-input-wrapper">
|
||||
<textarea v-model="delegateFormComment" class="comment-textarea" placeholder="请输入审核意见 (2-100字)"
|
||||
:maxlength="100" auto-height />
|
||||
<view class="char-count">{{ delegateFormComment.length }}/100</view>
|
||||
</view>
|
||||
<view v-if="commentError" class="error-message">{{ commentError }}</view>
|
||||
</view>
|
||||
<view v-if="userSelectError" class="error-message">{{ userSelectError }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 审核意见 -->
|
||||
<view class="form-section">
|
||||
<view class="section-title">
|
||||
<text class="title-text">审核意见</text>
|
||||
<text class="required">*</text>
|
||||
<view class="modal-footer">
|
||||
<view class="modal-btn cancel-btn" @tap="hideDelegateFormModal">
|
||||
<text class="btn-text">取消</text>
|
||||
</view>
|
||||
<view class="comment-input-wrapper">
|
||||
<textarea v-model="delegateFormComment" class="comment-textarea" placeholder="请输入审核意见 (2-100字)"
|
||||
:maxlength="100" auto-height />
|
||||
<view class="char-count">{{ delegateFormComment.length }}/100</view>
|
||||
<view class="modal-btn confirm-btn" @tap="confirmDelegateForm">
|
||||
<text class="btn-text">确定</text>
|
||||
</view>
|
||||
<view v-if="commentError" class="error-message">{{ commentError }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-footer">
|
||||
<view class="modal-btn cancel-btn" @tap="hideDelegateFormModal">
|
||||
<text class="btn-text">取消</text>
|
||||
</view>
|
||||
<view class="modal-btn confirm-btn" @tap="confirmDelegateForm">
|
||||
<text class="btn-text">确定</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
|
||||
|
||||
import { filePath } from "@@/utils/file"
|
||||
import { SERVICE_IMAGE } from "@@/constant/index"
|
||||
|
||||
import { taskApi, integratedApi } from "@/sheep/api/index"
|
||||
|
||||
const todoList = ref<any[]>([])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<crud-layout :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<crud-layout title="访客记录" :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<z-paging ref="pageRef" height="100%" :fixed="false" v-model="pageHook.list" :default-page-size="10"
|
||||
safe-area-inset-bottom :loading-more-enabled="true" :show-scrollbar="false" @query="query"
|
||||
:auto-clean-list-when-reload="false" :auto-scroll-to-top-when-reload="false" @scroll="pageHook.scroll">
|
||||
@@ -96,11 +96,11 @@
|
||||
<view class="h-100rpx flex justify-around items-center">
|
||||
<view class="flex-col justify-center items-center" @click="goInvitation">
|
||||
<image src="@/static/page/invitation.png" class="w-50rpx h-50rpx" />
|
||||
<view class="text-center text-24 c-#999999">访客邀约</view>
|
||||
<view class="text-center text-24rpx c-#999999">访客邀约</view>
|
||||
</view>
|
||||
<view class="flex-col justify-center items-center">
|
||||
<image src="@/static/page/order-select.png" class="w-50rpx h-50rpx" />
|
||||
<view class="text-center text-24 c-#1977FD">访客记录</view>
|
||||
<view class="text-center text-24rpx c-#1977FD">访客记录</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<crud-layout mainIsScroll :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<crud-layout title="访客邀约" mainIsScroll :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<view class="h-20rpx" />
|
||||
<tm-sheet :margin="[0]">
|
||||
<tm-alert :border="['2']" :borderColor="['#89c0ff']" :darkBorderColor="['#3b5066']">访客邀约请填写以下信息</tm-alert>
|
||||
@@ -7,7 +7,7 @@
|
||||
<tm-form-item name="mobile" label="访客手机号">
|
||||
<tm-input color="transparent" v-model.trim="form.mobile" placeholder="请输入访客手机号" />
|
||||
</tm-form-item>
|
||||
<tm-form-item name="vStartDate" label="拜访开始时间 >" @click="startDatePicker('vStartDate')">
|
||||
<tm-form-item name="vStartDate" label="拜访开始时间 >" @tap="startDatePicker('vStartDate')">
|
||||
<template #label>
|
||||
<span>拜访开始时间 </span>
|
||||
<tm-icon name="arrow-right-s-line" />
|
||||
@@ -15,7 +15,7 @@
|
||||
<tm-input :modelValue="form.vStartDate" placeholder="请选择拜访开始时间" style="pointer-events: none"
|
||||
disabled color="transparent" />
|
||||
</tm-form-item>
|
||||
<tm-form-item name="vEndDate" label="拜访离开时间 >" @click="endDatePicker('vEndDate')">
|
||||
<tm-form-item name="vEndDate" label="拜访离开时间 >" @tap="endDatePicker('vEndDate')">
|
||||
<template #label>
|
||||
<span>拜访离开时间 </span>
|
||||
<tm-icon name="arrow-right-s-line" />
|
||||
@@ -52,11 +52,11 @@
|
||||
<view class="h-100rpx flex justify-around items-center">
|
||||
<view class="flex-col justify-center items-center">
|
||||
<image src="@/static/page/invitation-select.png" class="w-50rpx h-50rpx" />
|
||||
<view class="text-center text-24 c-#1977FD">访客邀约</view>
|
||||
<view class="text-center text-24rpx c-#1977FD">访客邀约</view>
|
||||
</view>
|
||||
<view class="flex-col justify-center items-center" @click="goInvitationRecord">
|
||||
<image src="@/static/page/order.png" class="w-50rpx h-50rpx" />
|
||||
<view class="text-center text-24 c-#999999">访客记录</view>
|
||||
<view class="text-center text-24rpx c-#999999">访客记录</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -167,3 +167,10 @@ const goInvitationRecord = () => {
|
||||
uni.redirectTo({ url: "/pages/integrated/visitor/invitation-record" })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scope>
|
||||
:deep(.tmFormItemLabel) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@@ -58,18 +58,18 @@ const toDetail = async (v: any) => {
|
||||
|
||||
<template>
|
||||
<view class="h-full overflow-hidden">
|
||||
<swiper class="w-100vw h-30vh" circular :indicator-dots="true" indicator-active-color="#0779FD"
|
||||
indicator-color="#ffffff" :autoplay="true">
|
||||
<swiper-item v-for="(v, i) in state.detail?.swiperPic?.split(',')" :key="i">
|
||||
<image class="w-full h-full" :src="filePath(v)" mode="aspectFill" />
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<crud-layout>
|
||||
<z-paging ref="pageRef" height="70%" :fixed="false" v-model="pageHook.list" :default-page-size="10"
|
||||
safe-area-inset-bottom :loading-more-enabled="true" :show-scrollbar="false" @query="query"
|
||||
:auto-clean-list-when-reload="false" :auto-scroll-to-top-when-reload="false" @scroll="pageHook.scroll">
|
||||
<view class="h-20rpx" />
|
||||
<template #top>
|
||||
<swiper class="w-100vw h-30vh" circular :indicator-dots="true" indicator-active-color="#0779FD"
|
||||
indicator-color="#ffffff" :autoplay="true">
|
||||
<swiper-item v-for="(v, i) in state.detail?.swiperPic?.split(',')" :key="i">
|
||||
<image class="w-full h-full" :src="filePath(v)" mode="aspectFill" />
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</template>
|
||||
<view class="flex-row card p-20rpx m-b-20rpx" v-for="(v, i) in pageHook.list" :key="i"
|
||||
@tap="toDetail(v)">
|
||||
<image class="w-180rpx h-180rpx rounded-12rpx my-auto" :src="filePath(v.logoPic)"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<tm-sheet :margin="[0, 20]">
|
||||
<view class="prefix_title mb-15rpx">菜品分类购买数量占比图</view>
|
||||
<view class="w-full h-600rpx">
|
||||
<qiun-data-charts type="pie" :opts="pieChartOpts" :chartData="pieChartData"
|
||||
<qiun-data-charts type="pie" :canvas2d="true" :opts="pieChartOpts" :chartData="pieChartData"
|
||||
tooltipFormat="canteenPieTooltip" />
|
||||
</view>
|
||||
</tm-sheet>
|
||||
@@ -15,7 +15,7 @@
|
||||
<view class="prefix_title mb-15rpx">近一周各食堂购买数量趋势图</view>
|
||||
<view class="w-full h-600rpx">
|
||||
<!-- tooltipFormat="canteenAreaTooltip" -->
|
||||
<qiun-data-charts type="area" :opts="areaChartOpts" :chartData="areaChartData" />
|
||||
<qiun-data-charts type="area" :canvas2d="true" :opts="areaChartOpts" :chartData="areaChartData" />
|
||||
</view>
|
||||
</tm-sheet>
|
||||
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<crud-layout :bg="filePath(SERVICE_IMAGE.blueBg)">
|
||||
<div class="integrated-title-card">
|
||||
<div class="integrated-title">综合服务</div>
|
||||
</div>
|
||||
<div class="service-container-card">
|
||||
<div class="service-container">
|
||||
<div class="service-grid">
|
||||
<div v-for="item in services" :key="item.name" class="service-item" @click="goPage(item)">
|
||||
<div class="service-icon-wrapper">
|
||||
<image :src="item.icon" class="service-icon" />
|
||||
<view v-if="item.name === 'todo' && todoCount > 0" class="badge">
|
||||
{{ todoCount > 99 ? "99+" : todoCount }}
|
||||
</view>
|
||||
</div>
|
||||
<div class="service-label">{{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<view
|
||||
class="position-absolute bottom-16rpx left-0 right-0 second-text-26 text-center flex items-center justify-center">
|
||||
<view>联系热线:</view>
|
||||
<view @click="doPhone('18367908152')">18367908152</view>
|
||||
<view class="w-10rpx" />
|
||||
<view @click="doPhone('15657370065')">15657370065</view>
|
||||
</view>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onLoad } from "@dcloudio/uni-app"
|
||||
import { ref, onMounted } from "vue"
|
||||
|
||||
import { filePath } from "@@/utils/file"
|
||||
import { SERVICE_IMAGE } from "@@/constant/index"
|
||||
|
||||
import integratedApi from "@/sheep/api/integrated"
|
||||
import { hooks } from "@@/index"
|
||||
|
||||
const todoCount = ref(0)
|
||||
|
||||
const services = [
|
||||
{
|
||||
name: "todo",
|
||||
label: "工作待办",
|
||||
icon: "/static/icon/todo.png",
|
||||
url: "/pages/integrated/todo/index",
|
||||
},
|
||||
{
|
||||
name: "meeting-booking",
|
||||
label: "会议预定",
|
||||
icon: "/static/icon/meeting-booking.png",
|
||||
url: "/pages/integrated/meeting/index",
|
||||
},
|
||||
{
|
||||
name: "lost-and-found",
|
||||
label: "失物招领",
|
||||
icon: "/static/icon/service_1.png",
|
||||
url: "/pages/integrated/lostAndFound/index",
|
||||
},
|
||||
{
|
||||
name: "feedback",
|
||||
label: "意见反馈",
|
||||
icon: "/static/icon/feedback.png",
|
||||
url: "/pages/integrated/feedback/index",
|
||||
},
|
||||
{
|
||||
name: "repair-report",
|
||||
label: "物业维修",
|
||||
icon: "/static/icon/service_2.png",
|
||||
url: "/pages/integrated/repair/index",
|
||||
},
|
||||
{
|
||||
name: "notice",
|
||||
label: "通知公告",
|
||||
icon: "/static/icon/service_3.png",
|
||||
url: "/pages/integrated/notice/index",
|
||||
},
|
||||
{
|
||||
name: "visitor",
|
||||
label: "访客邀约",
|
||||
icon: "/static/icon/visitor.png",
|
||||
url: "/pages/integrated/visitor/invitation",
|
||||
},
|
||||
]
|
||||
|
||||
// 获取待办数量
|
||||
async function fetchTodoCount() {
|
||||
try {
|
||||
const res = await integratedApi.getTodoList({ current: 1, size: 1 })
|
||||
const responseData = res as any
|
||||
if (responseData.code === 0 && responseData.data) {
|
||||
todoCount.value = responseData.data.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取待办数量失败:", error)
|
||||
todoCount.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
fetchTodoCount()
|
||||
})
|
||||
|
||||
function goPage(item) {
|
||||
// 访客邀约功能正在开发中
|
||||
// if (item.name === "visitor") {
|
||||
// uni.showToast({
|
||||
// title: "访客邀约功能正在开发中",
|
||||
// icon: "none",
|
||||
// duration: 2000,
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
|
||||
if (item.url) {
|
||||
uni.navigateTo({ url: item.url })
|
||||
}
|
||||
}
|
||||
|
||||
// 联系客服
|
||||
const doPhone = (phone: string) => {
|
||||
hooks.useMakePhoneCall(phone)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.integrated-title-card {
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
margin: 32rpx 24rpx 0 24rpx;
|
||||
padding: 0 0 8rpx 0;
|
||||
}
|
||||
|
||||
.integrated-title {
|
||||
font-size: 38rpx;
|
||||
font-weight: bold;
|
||||
color: #222;
|
||||
text-align: center;
|
||||
padding: 36rpx 0 24rpx 0;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
.service-container-card {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
margin: 24rpx;
|
||||
padding-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.service-container {
|
||||
padding: 32rpx 0 0 0;
|
||||
background: transparent;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.service-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.service-item {
|
||||
width: 25%;
|
||||
margin-bottom: 40rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.service-item:hover {
|
||||
transform: translateY(-6rpx) scale(1.06);
|
||||
box-shadow: 0 2rpx 12rpx rgba(25, 118, 210, 0.08);
|
||||
}
|
||||
|
||||
.service-icon-wrapper {
|
||||
position: relative;
|
||||
margin-bottom: 14rpx;
|
||||
}
|
||||
|
||||
.service-icon {
|
||||
width: 92rpx;
|
||||
height: 92rpx;
|
||||
border-radius: 16rpx;
|
||||
background: #f5f7fa;
|
||||
box-shadow: 0 2rpx 8rpx rgba(25, 118, 210, 0.04);
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
top: -8rpx;
|
||||
right: -8rpx;
|
||||
background: #ff6b35;
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
font-weight: bold;
|
||||
min-width: 32rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 8rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(255, 107, 53, 0.4);
|
||||
border: 2rpx solid #fff;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.service-label {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #f5f6fa;
|
||||
}
|
||||
</style>
|
||||
@@ -51,6 +51,13 @@
|
||||
切换角色
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="second-text-26 text-center mb-2 mt-10 flex items-center justify-center">
|
||||
<view>联系热线:</view>
|
||||
<view @click="doPhone('18367908152')">18367908152</view>
|
||||
<view class="w-10rpx" />
|
||||
<view @click="doPhone('15657370065')">15657370065</view>
|
||||
</view>
|
||||
</crud-layout>
|
||||
|
||||
<modify-user ref="modifyUserRef" warnText="填写出生日期即可领取生日兑换券" @close="storage.getUserInfo" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { watch, reactive, onMounted } from "vue"
|
||||
import { watch, reactive, onMounted, computed } from "vue"
|
||||
|
||||
import { router } from "@@/index"
|
||||
import { useTabbarStore } from "@@/stores/tabbar"
|
||||
@@ -111,10 +111,11 @@ function tabbarChange(index: number) {
|
||||
<view class="layout_main_bottom">
|
||||
<slot name="main-bottom"></slot>
|
||||
</view>
|
||||
<tm-tabbar v-if="tabbarStore.isTabbar" :activeIndex="tabbarStore.activeIndex"
|
||||
:list="tabbarStore.pageTabbarList" :outIndex="-1" fontSize="12px" iconSize="24px" bgColor="#F6F5FA"
|
||||
darkBgColor="#F6F5FA" borderColor="#DBDDDE" color="#8E8E8E" selected-color="#0094ff" position="relative"
|
||||
@change="tabbarChange">
|
||||
</view>
|
||||
<view v-if="tabbarStore.isTabbar" class="layout_foot">
|
||||
<tm-tabbar class="layout_foot" :activeIndex="tabbarStore.activeIndex" :list="tabbarStore.pageTabbarList"
|
||||
:outIndex="-1" fontSize="12px" iconSize="24px" bgColor="#F6F5FA" :zIndex="99999" darkBgColor="#F6F5FA"
|
||||
borderColor="#DBDDDE" color="#8E8E8E" selected-color="#0094ff" position="fixed" @change="tabbarChange">
|
||||
</tm-tabbar>
|
||||
</view>
|
||||
</view>
|
||||
@@ -175,6 +176,9 @@ page {
|
||||
}
|
||||
}
|
||||
|
||||
&_foot {}
|
||||
&_foot {
|
||||
display: inline-block;
|
||||
z-index: 99999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<view class="tabbar">
|
||||
<view class="tabbar-item" :class="{ active: activeTab === 'home' }" @tap="goHome">
|
||||
<image v-if="activeTab === 'home'" src="/static/tabbar/home-select.png" class="tabbar-icon" />
|
||||
<image v-else src="/static/tabbar/home.png" class="tabbar-icon" />
|
||||
<text>首页</text>
|
||||
</view>
|
||||
<view class="tabbar-item" :class="{ active: activeTab === 'mine' }" @tap="goMine">
|
||||
<image v-if="activeTab === 'mine'" src="/static/tabbar/my-select.png" class="tabbar-icon" />
|
||||
<image v-else src="/static/tabbar/my.png" class="tabbar-icon" />
|
||||
<text>我的</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{ activeTab: 'home' | 'mine' }>()
|
||||
function goHome() {
|
||||
uni.reLaunch({
|
||||
url: '/pages/integrated/meeting/index'
|
||||
})
|
||||
}
|
||||
function goMine() {
|
||||
uni.reLaunch({
|
||||
url: '/pages/integrated/meeting/my-reservations'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tabbar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 100rpx;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
border-top: 1px solid #eee;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.tabbar-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #888;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.tabbar-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.tabbar-item.active {
|
||||
color: #1777FF;
|
||||
}
|
||||
</style>
|
||||
@@ -13,7 +13,7 @@
|
||||
{{ item.time.slice(0, 5) }}
|
||||
</tm-button>
|
||||
</view>
|
||||
<view class="px-20rpx py-2 flex justify-between">
|
||||
<view class="px-20rpx py-16rpx flex justify-between">
|
||||
<tm-button skin="thin" block @click="emit('cancel')">取消</tm-button>
|
||||
|
||||
<tm-button block @click="onConfirm">确定</tm-button>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 599 B |
Binary file not shown.
|
After Width: | Height: | Size: 588 B |
@@ -393,9 +393,11 @@ onMounted(() => {
|
||||
name="item">
|
||||
<tm-badge :dot="item.dotType == 'dot'" :label="item.dotLabel" :offset="[0, 5]">
|
||||
<view style="height: 30px;padding: 0px 4px;">
|
||||
<tm-icon v-if="_outIndex != index" :color="nowIndex == index ? item.selectedColor : item.color"
|
||||
<tm-icon v-if="_outIndex != index"
|
||||
:color="nowIndex == index ? item.selectedColor : item.color"
|
||||
:dark-color="nowIndex == index ? item.selectedColor : item.color"
|
||||
:name="nowIndex == index ? item.selectedIcon : item.icon" :size="iconSize"></tm-icon>
|
||||
:name="nowIndex == index ? item.selectedIcon : item.icon"
|
||||
:size="iconSize"></tm-icon>
|
||||
</view>
|
||||
<text
|
||||
:style="{ fontSize: _fontSize, color: nowIndex == index ? item.selectedColor : item.color, paddingTop: '4px' }">
|
||||
@@ -406,6 +408,7 @@ onMounted(() => {
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<view :style="{ height: safeHeight + 'px' }"></view>
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user