This commit is contained in:
zhouyanli
2026-05-12 17:41:07 +08:00
30 changed files with 4970 additions and 901 deletions

View File

@@ -62,6 +62,18 @@ export default {
.flex-1 { .flex-1 {
flex: 1; flex: 1;
} }
.h-full {
height: 100%;
}
.w-full {
width: 100%;
}
.overflow-hidden {
overflow: hidden;
}
.overflow-auto {
overflow: auto;
}
/* 自定义导航栏专用 */ /* 自定义导航栏专用 */
.custom-header { .custom-header {
height: calc(var(--status-bar-height) + 88rpx); height: calc(var(--status-bar-height) + 88rpx);
@@ -80,9 +92,11 @@ export default {
.page_container { .page_container {
min-height: 100vh; min-height: 100vh;
max-height: 100vh;
height: 100%; height: 100%;
background: repeating-linear-gradient(180deg, #e2f0ff, #f6faff); background: repeating-linear-gradient(180deg, #e2f0ff, #f6faff);
overflow: hidden;
box-sizing: border-box;
padding: 0 40rpx; padding: 0 40rpx;
/* 先写 constant() 兼容 iOS < 11.2,再写 env() */ /* 先写 constant() 兼容 iOS < 11.2,再写 env() */
padding-top: calc(constant(safe-area-inset-top) + 20rpx); padding-top: calc(constant(safe-area-inset-top) + 20rpx);
@@ -90,5 +104,18 @@ export default {
padding-bottom: constant(safe-area-inset-bottom); padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom);
} }
.page_padding {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
display: flex;
flex-direction: column;
width: 100%;
min-height: 100vh;
max-height: 100vh;
overflow: hidden;
box-sizing: border-box;
}
/* 1 电表 2水表 */ /* 1 电表 2水表 */
</style> </style>

View File

@@ -0,0 +1,261 @@
<template>
<view class="simple-uploader">
<!-- 图片列表 -->
<view class="image-list">
<!-- 已选图片 -->
<view v-for="(img, index) in innerList" :key="index" class="image-item">
<image :src="img.url || img" class="img" mode="aspectFill" @tap="preview(index)"></image>
<!-- 删除按钮 -->
<view class="delete-btn" @tap.stop="remove(index)">
<uni-icons type="closeempty" size="16" color="#fff"></uni-icons>
</view>
<!-- 上传状态 -->
<view v-if="img.uploading" class="uploading-mask">
<text>上传中...</text>
</view>
</view>
<!-- 添加按钮 -->
<view class="add-btn" @tap="choose">
<uni-icons color="#a6a6a6" type="plusempty" size="25"></uni-icons>
</view>
</view>
</view>
</template>
<script setup>
import { ref, watch } from 'vue'
// ======================
// Props(可配置)
// ======================
const props = defineProps({
// 双向绑定的图片列表
modelValue: {
type: Array,
default: () => []
},
/** 多选 最多上传数量 默认9*/
limit: {
type: Number,
default: 9
},
// 选图来源:album(相册)/ camera(拍照)
sourceType: {
type: Array,
default: () => ['album', 'camera']
},
// 图片大小:original(原图)/ compressed(压缩图)
sizeType: {
type: Array,
default: () => ['compressed']
},
// 后端上传接口地址(必填)
uploadUrl: {
type: String,
default: ''
},
// 后端接收的文件字段名
fileFieldName: {
type: String,
default: 'file'
},
// ✅ 新增:上传时携带的额外参数
extraData: {
type: Object,
default: () => ({})
}
})
// ======================
// Emits
// ======================
const emit = defineEmits(['update:modelValue', 'success', 'fail', 'remove'])
// ======================
// 内部数据
// ======================
const innerList = ref([])
// 监听外部数据变化
watch(
() => props.modelValue,
newVal => {
innerList.value = newVal.map(item => {
if (typeof item === 'string') {
return { url: item }
}
return item
})
},
{ immediate: true, deep: true }
)
// ======================
// 核心方法
// ======================
// 1. 选择图片
function choose() {
uni.chooseImage({
count: props.limit,
sourceType: props.sourceType,
sizeType: props.sizeType,
success: async res => {
const tempPaths = res.tempFilePaths
// 如果没传上传接口,只返回临时路径
if (!props.uploadUrl) {
const newItems = tempPaths.map(path => ({ url: path }))
innerList.value = [...innerList.value, ...newItems]
emit(
'update:modelValue',
innerList.value.map(i => i.url)
)
emit('success', { tempFilePaths: tempPaths })
return
}
// 有上传接口,自动上传
for (const path of tempPaths) {
await uploadSingle(path)
}
},
fail: err => {
console.log('选图失败', err)
emit('fail', err)
}
})
}
// 2. 上传单张图片
async function uploadSingle(filePath) {
// 先添加到列表,标记为上传中
const tempItem = { url: filePath, uploading: true }
innerList.value = [...innerList.value, tempItem]
return new Promise((resolve, reject) => {
uni.uploadFile({
url: props.uploadUrl,
filePath: filePath,
name: props.fileFieldName,
// ✅ 新增:携带额外参数
formData: props.extraData,
success: res => {
try {
const data = JSON.parse(res.data)
// 假设后端返回格式:{ code: 200, url: "https://xxx.com/xxx.png" }
// 如果你的后端返回格式不同,请在这里修改
if (data.code === 200 || data.url) {
const finalUrl = data.url || data.data?.url
// 更新列表,替换为网络 URL
const index = innerList.value.findIndex(item => item.url === filePath)
if (index !== -1) {
innerList.value[index] = { url: finalUrl, uploading: false }
}
emit(
'update:modelValue',
innerList.value.map(i => i.url)
)
emit('success', { url: finalUrl, tempPath: filePath })
resolve(finalUrl)
} else {
throw new Error(data.msg || '上传失败')
}
} catch (e) {
console.error('解析上传结果失败', e)
remove(innerList.value.findIndex(item => item.url === filePath))
emit('fail', e)
reject(e)
}
},
fail: err => {
console.error('上传失败', err)
remove(innerList.value.findIndex(item => item.url === filePath))
emit('fail', err)
reject(err)
}
})
})
}
// 3. 删除图片
function remove(index) {
const removed = innerList.value.splice(index, 1)[0]
emit(
'update:modelValue',
innerList.value.map(i => i.url)
)
emit('remove', { index, url: removed.url })
}
// 4. 预览图片
function preview(index) {
const urls = innerList.value.map(i => i.url)
uni.previewImage({
urls: urls,
current: index
})
}
</script>
<style scoped>
.simple-uploader {
width: 100%;
}
.image-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20rpx;
}
.image-item {
position: relative;
width: 160rpx;
height: 160rpx;
}
.img {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
.delete-btn {
position: absolute;
top: 0rpx;
right: 0rpx;
width: 32rpx;
height: 32rpx;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.uploading-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 24rpx;
}
.add-btn {
width: 160rpx;
height: 160rpx;
background-color: #f3f3f3;
display: flex;
align-items: center;
justify-content: center;
}
</style>

View File

@@ -0,0 +1,205 @@
<template>
<!--
外层容器:默认为空壳,仅用于包裹
如果开启 placeholder,则通过 style 撑开高度
-->
<view
class="nav-wrapper"
:style="{
background: props.bgColor
}"
>
<!-- 【占位层】:只在开启 placeholder 时渲染,纯粹为了撑高度 -->
<view
v-if="placeholder"
class="nav-placeholder"
:style="{ height: totalNavHeight + 'px' }"
></view>
<!-- 【功能层】:Fixed 定位,脱离文档流,永远显示在最上层 -->
<view class="nav-fixed-layer" :style="fixedLayerStyle">
<!-- 状态栏区域(用于沉浸式背景填充) -->
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
<!-- 导航栏主体 -->
<view class="nav-main" :style="{ height: navBarHeight + 'px' }">
<!-- 左侧 -->
<view class="nav-left" :style="{ width: leftWidth + 'px' }">
<slot name="left">
<view class="back-btn" @click="goBack" v-if="showBack">
<uni-icons type="left" size="22" color="inherit"></uni-icons>
<text v-if="backText" class="back-text">{{ backText }}</text>
</view>
</slot>
</view>
<!-- 中间 -->
<view class="nav-center">
<slot name="center">
<text class="nav-title" :style="{ color: titleColor }">{{ title }}</text>
</slot>
</view>
<!-- 右侧 -->
<view class="nav-right" :style="{ width: rightWidth + 'px' }">
<slot name="right"></slot>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
// --- Props ---
interface Props {
title?: string
bgColor?: string
titleColor?: string
showBack?: boolean
backText?: string
placeholder?: boolean // 是否开启“占位层”
}
const props = withDefaults(defineProps<Props>(), {
title: '标题',
bgColor: 'transparent',
titleColor: '#333333',
showBack: true,
backText: '',
placeholder: true // 默认开启占位,防止遮挡
})
const emits = defineEmits(['clickLeft'])
// --- 状态 ---
const statusBarHeight = ref(0)
const navBarHeight = ref(44)
const capsuleHeight = ref(0)
const gap = ref(0)
// --- 计算属性 ---
const totalNavHeight = computed(() => statusBarHeight.value + navBarHeight.value)
// 2. 功能层样式:Fixed 定位,负责视觉展示
const fixedLayerStyle = computed(() => ({
backgroundColor: props.bgColor,
zIndex: 9999
}))
const leftWidth = computed(() => {
// #ifdef MP-WEIXIN
return capsuleHeight.value || 40
// #endif
// #ifndef MP-WEIXIN
return 40
// #endif
})
const rightWidth = computed(() => leftWidth.value)
// --- 生命周期 ---
onMounted(() => {
initNavBar()
})
// --- 方法 ---
const initNavBar = () => {
const windowInfo = uni.getWindowInfo()
statusBarHeight.value = windowInfo.statusBarHeight || 0
// #ifdef MP-WEIXIN
const menuInfo = uni.getMenuButtonBoundingClientRect()
if (menuInfo.top && menuInfo.height) {
gap.value = menuInfo.top - statusBarHeight.value
navBarHeight.value = (gap.value > 0 ? gap.value : 4) * 2 + menuInfo.height
capsuleHeight.value = menuInfo.height
} else {
navBarHeight.value = 44
capsuleHeight.value = 0
}
// #endif
// #ifndef MP-WEIXIN
navBarHeight.value = 44
// #endif
}
const goBack = () => {
emits('clickLeft')
}
</script>
<style scoped>
/* 【外层容器】:文档流中的普通盒子 */
.nav-wrapper {
width: 100%;
/* 这里不设置 position,让它老老实实在文档流里呆着 */
}
/* 【占位层】:看不见摸不着,只有高度 */
.nav-placeholder {
width: 100%;
/* 仅通过 height 撑开父容器 */
}
/* 【功能层】:脱离文档流,悬浮在页面上方 */
.nav-fixed-layer {
position: fixed;
top: 0;
left: 0;
width: 100vw;
box-sizing: border-box;
/* 视觉上的背景色在这里 */
}
.status-bar {
width: 100%;
pointer-events: none; /* 防止拦截点击 */
}
.nav-main {
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
padding: 0 20rpx;
padding-top: 10rpx;
}
.nav-left,
.nav-right {
display: flex;
height: 100%;
}
.nav-center {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
overflow: hidden;
}
.nav-title {
font-size: 25rpx;
font-size: 28rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #000000;
line-height: 40rpx;
text-align: center;
}
.back-btn {
display: flex;
align-items: center;
font-weight: 600;
}
.back-text {
font-size: 28rpx;
margin-left: 4rpx;
color: inherit;
}
</style>

View File

@@ -0,0 +1,176 @@
<template>
<view class="steps-vertical">
<view class="step-item" v-for="(item, index) in steps" :key="index">
<!-- 左侧:圆点 + 竖线 -->
<view class="step-left">
<!-- 圆点 -->
<view class="step-circle" :class="getClass(index)">
<!-- 插槽:自定义圆点 -->
<slot v-if="$slots.circle" name="circle" :index="index" :status="getStatus(index)">
</slot>
<view v-else-if="getStatus(index) === 'finish'">
<uni-icons type="checkmarkempty" size="15" :color="props.color"></uni-icons>
</view>
<text v-else-if="getStatus(index) === 'process'">{{ index + 1 }}</text>
<text v-else-if="getStatus(index) === 'wait'">{{ index + 1 }}</text>
</view>
<!-- 竖线(最后一条不显示) -->
<view
class="step-line"
:class="{ finish: getStatus(index) === 'finish' }"
v-if="index < steps.length - 1"
></view>
</view>
<!-- 右侧:内容区域(支持插槽!) -->
<view class="step-right">
<!-- 默认标题 -->
<view
class="step-title"
:class="{ active: getStatus(index) !== 'wait' }"
v-if="!$slots.item"
>
{{ item.title }}
</view>
<!-- 默认描述 -->
<view class="step-desc" v-if="item.desc && !$slots.item">
{{ item.desc }}
</view>
<!-- 🔥 核心:自定义内容插槽 -->
<slot
name="item"
v-if="$slots.item"
:index="index"
:status="getStatus(index)"
:data="item"
></slot>
</view>
</view>
</view>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
// 步骤列表
steps: {
type: Array,
default: () => []
},
// 当前步骤(从 0 开始)
current: {
type: Number,
default: 0
},
// 主题色
color: {
type: String,
default: '#007aff'
}
})
// 获取状态:finish / process / wait
const getStatus = index => {
if (index < props.current) return 'finish'
if (index === props.current) return 'process'
return 'wait'
}
// 样式类
const getClass = index => {
return 'step-circle-' + getStatus(index)
}
</script>
<style scoped lang="scss">
.step-item {
display: flex;
position: relative;
}
/* 左侧 */
.step-left {
display: flex;
flex-direction: column;
align-items: center;
margin-right: 14rpx;
}
/* 圆点 */
.step-circle {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
font-size: 32rpx;
line-height: 48rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
z-index: 10;
border: 3rpx solid transparent;
color: #999999;
border-color: #999999;
}
/* 已完成 */
.step-circle-finish {
background: transparent;
color: v-bind('props.color');
border-color: v-bind('props.color');
}
/* 进行中 */
.step-circle-process {
border-color: v-bind('props.color');
color: #fff;
background: v-bind('props.color');
}
/* 待完成 */
.step-circle-wait {
color: #999999;
border-color: #999999;
}
/* 竖线 */
.step-line {
width: 4rpx;
height: 100%;
background: #e5e5e5;
margin-top: 8rpx;
margin-bottom: 8rpx;
}
.step-line.finish {
background: v-bind('props.color');
}
/* 右侧内容 */
.step-right {
flex: 1;
}
.step-title {
font-size: 28rpx;
color: #666;
line-height: 1.4;
}
.step-title.active {
color: #333;
font-weight: 500;
}
.step-desc {
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
line-height: 1.5;
}
</style>

View File

@@ -1,39 +1,80 @@
<template> <template>
<view class="tabs_box"> <view class="tabs_box">
<view <view
v-for="(item, index) in tabslist" v-for="(item, index) in tabs"
:key="index" :key="index"
@tap="handleTabs(item)" @tap="handleTabs(item)"
class="tabs_items" class="tabs_items"
:class="{ :class="{
active: item.value === activeValue active: item.value === currentActiveValue,
line: showBottomLine && item.value === currentActiveValue
}" }"
> >
{{ item.name }} {{ item.label }}
</view> </view>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref, computed } from 'vue'
type tabslistType = { name: string; value: number } // 标签项类型
const props = defineProps<{ interface TabItem {
tabslist: tabslistType[] label: string
}>() value: string | number
}
// Props 类型定义
interface Props {
tabslist?: any[]
showBottomLine?: boolean
// 配置:传入字段名
options?: {
label: string
value: string
}
activeValue?: string | number
}
// 定义 Props
const props = withDefaults(defineProps<Props>(), {
tabslist: () => [],
showBottomLine: true,
options: () => ({ label: 'name', value: 'value' }),
activeValue: null
})
// ======================
// 格式化 tabs 数据(核心)
// ======================
const tabs = computed<TabItem[]>(() => {
const { tabslist, options } = props
if (!tabslist || tabslist.length === 0) return []
return tabslist.map(item => ({
label: item[options.label],
value: item[options.value]
}))
})
// ======================
// 当前激活值(响应式)
// ======================
const currentActiveValue = ref<string | number>(props.activeValue ?? tabs.value[0]?.value ?? '')
// 事件
const emit = defineEmits<{ const emit = defineEmits<{
update: [value: number] update: [value: string | number]
}>() }>()
const activeValue = ref(0) // 点击切换
const handleTabs = (row: tabslistType) => { const handleTabs = (item: TabItem) => {
activeValue.value = row.value currentActiveValue.value = item.value
emit('update', row.value) emit('update', item.value)
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// tabs
.tabs_box { .tabs_box {
height: 60rpx; height: 60rpx;
line-height: 60rpx; line-height: 60rpx;
@@ -44,25 +85,25 @@ const handleTabs = (row: tabslistType) => {
overflow-x: auto; overflow-x: auto;
.tabs_items { .tabs_items {
/* 关键:不压缩宽度 */
flex-shrink: 0; flex-shrink: 0;
margin-right: 60rpx; margin-right: 60rpx;
color: #4d5dfa; font-size: 28rpx;
color: #222222;
&.active {
position: relative; position: relative;
&.active {
color: #4d5dfa;
&.line {
&::after { &::after {
content: ''; content: '';
display: block; display: block;
width: 100%; width: 100%;
height: 8rpx; height: 8rpx;
background-color: #6371fa;
background: linear-gradient(180deg, #4d5dfa 0%, #bac0fa 100%); background: linear-gradient(180deg, #4d5dfa 0%, #bac0fa 100%);
border-radius: 4rpx; border-radius: 4rpx;
position: absolute; position: absolute;
left: 0; left: 0;
bottom: 0rpx; bottom: 0;
}
} }
} }
} }

View File

@@ -50,9 +50,9 @@
"quickapp": {}, "quickapp": {},
/* 小程序特有相关 */ /* 小程序特有相关 */
"mp-weixin": { "mp-weixin": {
"appid": "wx4a49ad94e2b25927", "appid": "wx40625124315d2de1",
"usingComponents": true, "usingComponents": true,
"lazyCodeLoading":"requiredComponents" "lazyCodeLoading": "requiredComponents"
}, },
"mp-alipay": { "mp-alipay": {
"usingComponents": true "usingComponents": true

View File

@@ -0,0 +1,192 @@
<template>
<view class="page_padding">
<view class="detailsbox flex flex-col overflow-hidden">
<!-- 状态筛选标签 -->
<TabsComponents
:showBottomLine="false"
:tabslist="statusTabs"
@update="switchStatus"
></TabsComponents>
<view class="detail_list flex flex-1 overflow-auto">
<scroll-view scroll-y="true">
<view class="item" v-for="(item, index) in list" :key="index" @tap="handleComplaint">
<view class="title flex items-center">
<text>{{ item.title }}</text>
<view class="status padding" v-if="item.status === 0"> <text>待处理</text> </view>
<view class="status compluted" v-else>
<text>已完成</text>
</view>
</view>
<view class="descrtion">
<text>{{ item.desction }}</text>
</view>
<view class="detailsMan">
<text>投诉人:</text>
<text>{{ item.user }}</text>
</view>
<view class="detailsMan">
<text>手机号:</text>
<text>{{ item.phone }}</text>
</view>
<view class="detailsMan flex justify-between">
<view class="">
<text>投诉时间:</text>
<text>{{ item.time }}</text>
</view>
<view class="actions" v-if="item.status === 0">
<text>处理</text>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import TabsComponents from '@/components/tabs/tabs.vue'
const activeStatus = ref(0)
const statusTabs = [
{
name: '全部',
value: 0
},
{
name: '待处理',
value: 1
},
{
name: '已完成',
value: 2
}
]
// 切换状态(全部/待分配/待解决/已完成)筛选
const switchStatus = value => {
activeStatus.value = value
}
//
const list = [
{
title: '小区环境',
status: 0, // 0 待处理 1 已完成
desction: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
user: '胡小小',
phone: '19113212388',
time: '2023-08-01 10:05:46'
},
{
title: '小区环境',
status: 1, // 0 待处理 1 已完成
desction: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
user: '胡小小',
phone: '19113212388',
time: '2023-08-01 10:05:46'
},
{
title: '小区环境',
status: 1, // 0 待处理 1 已完成
desction: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
user: '胡小小',
phone: '19113212388',
time: '2023-08-01 10:05:46'
},
{
title: '小区环境',
status: 0, // 0 待处理 1 已完成
desction: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
user: '胡小小',
phone: '19113212388',
time: '2023-08-01 10:05:46'
},
{
title: '小区环境',
status: 1, // 0 待处理 1 已完成
desction: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
user: '胡小小',
phone: '19113212388',
time: '2023-08-01 10:05:46'
}
]
function handleComplaint() {
uni.navigateTo({
url: '/pageSubPack/complaint/Details_handle'
})
}
</script>
<style scoped lang="scss">
.page_padding {
background: #f9f9f9;
}
.detailsbox {
flex: 1;
background: #f9f9f9;
padding: 20rpx 40rpx;
.detail_list {
.item {
height: 390rpx;
background: #ffffff;
border-radius: 16rpx;
margin-bottom: 20rpx;
box-sizing: border-box;
padding: 30rpx 36rpx 55rpx 54rpx;
.title {
font-weight: 400;
font-size: 32rpx;
color: #222222;
line-height: 40rpx;
margin-bottom: 10rpx;
.status {
width: 82rpx;
height: 30rpx;
border-radius: 198rpx 198rpx 198rpx 198rpx;
font-weight: 400;
font-size: 20rpx;
color: #ffffff;
line-height: 30rpx;
text-align: center;
margin-left: 20rpx;
&.padding {
background: #e34d59;
}
&.compluted {
background: #4d5dfa;
}
}
}
.descrtion {
font-weight: 450;
font-size: 28rpx;
color: #666666;
line-height: 40rpx;
margin-bottom: 20rpx;
letter-spacing: 0rpx;
}
.detailsMan {
font-weight: 400;
font-size: 28rpx;
color: #999999;
line-height: 40rpx;
margin-bottom: 20rpx;
.actions {
width: 127rpx;
height: 44rpx;
background: #4d5dfa;
border-radius: 198rpx 198rpx 198rpx 198rpx;
text-align: center;
font-weight: 400;
font-size: 28rpx;
color: #ffffff;
line-height: 44rpx;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,203 @@
<template>
<view class="page_padding">
<view class="Details_handelbox h-full flex-1">
<view class="items top flex items-center justify-between">
<view class="title flex items-center">
<view class="line"></view>
<text>投诉状态</text>
</view>
<view class="status" v-if="info.status === 0"> <text>待处理</text> </view>
<view class="status alwary" v-if="info.status === 1">
<text>已完成</text>
</view>
</view>
<view class="subDescrition" v-if="info.status === 1">
<text>已完成处理,已联系工作人员粘贴标语提醒,并派人定时巡视。</text>
</view>
<view class="items flex items-center justify-between">
<view class="title flex items-center">
<view class="line"></view>
<text>投诉详情</text>
</view>
</view>
<view class="list">
<view class="item">
<view class="label"> 投诉类型 </view>
<view class="value flex items-center justify-between">
<view class=""> 社区服务 </view>
<uni-icons type="right" size="14" color="#000000"></uni-icons>
</view>
</view>
<view class="item">
<view class="label"> 问题描述 </view>
<view class="value">
<text
>投诉内容投诉内容投诉内容投诉内容投诉内容投诉内容投诉内容投诉内容投诉内容投诉内容
</text>
</view>
</view>
<view class="item">
<view class="label"> 投诉人姓名 </view>
<view class="value">
<text>匿名 </text>
</view>
</view>
<view class="item">
<view class="label"> 手机号码 </view>
<view class="value">
<text>无 </text>
</view>
</view>
<view class="photobox">
<view class="lable">
<text>问题照片</text>
</view>
<view class="imgbox">
<image
v-for="(item, index) in info.imgs"
:key="index"
:src="item"
class="img"
mode="scaleToFill"
></image>
</view>
</view>
</view>
<!-- submit -->
<view v-if="info.status === 0" class="action_btn" @tap="handleSelecthouse">
<text>处理</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
const info = ref({
status: 0, //0待解决 1已完成
imgs: [
'https://picsum.photos/61',
'https://picsum.photos/61',
'https://picsum.photos/61',
'https://picsum.photos/61',
'https://picsum.photos/61'
]
})
function handleSelecthouse() {
uni.navigateTo({
url: '/pageSubPack/complaint/handle'
})
}
</script>
<style scoped lang="scss">
.Details_handelbox {
padding: 30rpx 40rpx;
padding-bottom: 0;
position: relative;
.items {
height: 100rpx;
&.top {
border-bottom: 1rpx solid #ccc;
}
.title {
font-weight: 550;
font-size: 32rpx;
color: #333333;
line-height: 40rpx;
.line {
width: 6rpx;
height: 28rpx;
background: #4d5dfa;
margin-right: 10rpx;
}
}
.status {
font-weight: 400;
font-size: 28rpx;
color: #4d5dfa;
line-height: 36rpx;
&.alwary {
color: #999999;
}
}
}
.subDescrition {
margin: 30rpx 0;
font-weight: 500;
font-size: 28rpx;
letter-spacing: 1rpx;
color: #4d5dfa;
line-height: 36rpx;
}
.list {
.item {
border-bottom: 1rpx solid rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
padding: 20rpx;
box-sizing: border-box;
min-height: 100rpx;
.label {
width: 226rpx;
font-weight: 400;
font-size: 28rpx;
color: #333333;
line-height: 36rpx;
}
.value {
font-weight: 400;
font-size: 28rpx;
color: #000000;
line-height: 36rpx;
flex: 1;
}
}
.photobox {
margin-top: 30rpx;
font-weight: 400;
font-size: 28rpx;
color: #333333;
line-height: 36rpx;
padding: 0 20rpx;
.lable {
margin-bottom: 30rpx;
}
.imgbox {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20rpx;
.img {
width: 132rpx;
height: 132rpx;
}
}
}
}
// 确定派单
.action_btn {
width: 670rpx;
height: 80rpx;
font-weight: 400;
font-size: 28rpx;
color: #ffffff;
line-height: 80rpx;
text-align: center;
margin-top: 20rpx;
background: #4d5dfa;
border-radius: 8rpx 8rpx 8rpx 8rpx;
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
}
</style>

View File

@@ -0,0 +1,98 @@
<template>
<view class="page_padding">
<view class="handlebox flex-1">
<view class="items checkout flex items-center" @tap="handleClick">
<view class="label">处理人员</view>
<view class="value"> {{ checkoutinfo.username ? checkoutinfo.username : '请选择' }} </view>
</view>
<view class="items">
<view class="label">处理描述</view>
<view class="areabox">
<textarea value="" placeholder="请输入" placeholder-style="font-size:28rpx;" />
</view>
</view>
<view class="action_btn" @tap="handleSelecthouse">
<text>确 认</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
const checkoutinfo = ref({
username: '',
userid: ''
})
function handleClick() {
uni.navigateTo({
url: '/pageSubPack/complaint/handleUser',
events: {
backData: data => {
checkoutinfo.value.username = data.username
checkoutinfo.value.userid = data.userid
}
}
})
}
function handleSelecthouse() {
uni.navigateBack()
}
</script>
<style lang="scss" scoped>
.handlebox {
padding: 30rpx 40rpx;
position: relative;
.items {
margin-bottom: 30rpx;
&.checkout {
height: 100rpx;
line-height: 100rpx;
border-bottom: 1rpx solid rgba(0, 0, 0, 0.1);
}
.label {
width: 128rpx;
height: 40rpx;
font-weight: 550;
font-size: 32rpx;
color: #333333;
line-height: 40rpx;
margin-right: 86rpx;
}
.value {
height: 36rpx;
font-weight: 400;
font-size: 28rpx;
color: #666666;
line-height: 36rpx;
text-align: left;
}
.areabox {
margin-top: 20rpx;
border-radius: 8rpx;
border: 2rpx solid #e7e7e7;
padding: 10rpx;
box-sizing: border-box;
}
}
.action_btn {
width: 670rpx;
height: 80rpx;
font-weight: 400;
font-size: 28rpx;
color: #ffffff;
line-height: 80rpx;
text-align: center;
background: #4d5dfa;
border-radius: 8rpx 8rpx 8rpx 8rpx;
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
}
</style>

View File

@@ -0,0 +1,245 @@
<template>
<view class="page_padding shareUser">
<view class="shareBody flex flex-col flex-1 overflow-hidden">
<!-- 搜索 -->
<view class="search_box">
<view
@click="handleClick"
class="search_view flex-center"
v-if="!queryParmas.value.trim() && Searching"
>
<uni-icons type="search" size="34rpx" color="rgba(0,0,0,0.26)"></uni-icons>
<text>请输入关键字进行搜索</text>
</view>
<uni-easyinput
@blur="handleInput"
@change="handleSearch"
:focus="true"
ref="inputRef"
v-model="queryParmas.value"
v-else
/>
</view>
<!-- srcoll -->
<scroll-view
scroll-y="true"
class="flex-1 overflow-auto"
lower-threshold="60"
@scrolltolower="scrolltolower"
>
<radio-group @change="radioChange">
<label v-for="(item, index) in searchlist" :key="index">
<view
class="item flex items-center justify-between"
:class="{
active: checkoutUserid === item.id
}"
>
<view class="">
<view class="info">
<text>姓名:</text>
<text>{{ item.name }}</text>
</view>
<view class="info">
<text>角色:</text>
<text>{{ item.roleName }}</text>
</view>
<view class="info">
<text>手机号码:</text>
<text>{{ item.phone }}</text>
</view>
</view>
<radio color="#4d5dfa" :value="item.id" />
</view>
</label>
</radio-group>
</scroll-view>
<!-- submit -->
<view class="action_btn" @tap="handleSelecthouse">
<text>确定派单</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, getCurrentInstance } from 'vue'
import navBar from '@/components/navbar/navbar.vue'
const Searching = ref(true)
/** 点击时切换到input,并聚焦 */
function handleClick() {
Searching.value = false
}
/** 判断 失去焦点时 切换回 view */
function handleInput() {
if (queryParmas.value.value.trim()) {
Searching.value = true
}
}
function handleSearch() {}
// -----------------------------------------
/* 选择列表 */
const searchlist = ref([])
const queryParmas = ref({
pageNum: 1,
pageSize: 10,
value: ''
})
const isLoading = ref(false) // 加载锁
const hasMore = ref(true) // 是否还有更多数据
const total = ref(21)
function scrolltolower() {
// 必须加这两个判断!
if (isLoading.value || !hasMore.value) {
return
}
queryParmas.value.pageNum += 1
getlist()
}
function getlist() {
// 1. 如果正在加载或没有更多数据,直接返回
if (isLoading.value || !hasMore.value) return
// 2. 开启锁
isLoading.value = true
uni.showLoading({
title: '加载中',
mask: true
})
const { pageNum, pageSize } = queryParmas.value
const startId = (pageNum - 1) * pageSize + 1
const arr = Array.from({ length: pageSize }).map((_, index) => {
const realId = startId + index
const randomStatus = Math.floor(Math.random() * 3)
return {
name: '张三' + realId,
roleName: '维修人员',
phone: '19578012123',
id: realId
}
})
// 追加数据(正确方式)
searchlist.value = [...searchlist.value, ...arr]
// 判断是否还有更多
hasMore.value = searchlist.value.length < total.value
isLoading.value = false
uni.hideLoading()
}
getlist()
function resetList() {
reportRecords.value = []
queryParmas.value.pageNum = 1
hasMore.value = true
getlist()
}
//=======================
const checkoutUserid = ref(null)
const checkoutUserInfo = ref(null)
function radioChange(val) {
checkoutUserid.value = Number(val.detail.value)
const find = searchlist.value.find(item => item.id === Number(val.detail.value))
if (find) {
checkoutUserInfo.value = find
}
}
let eventChannel = null
// 必须在 onMounted 里获取
onMounted(() => {
const instance = getCurrentInstance().proxy
if (instance && instance.getOpenerEventChannel) {
eventChannel = instance.getOpenerEventChannel()
}
})
function handleSelecthouse() {
if (eventChannel) {
eventChannel.emit('backData', {
userid: checkoutUserid.value,
username: checkoutUserInfo.value.name
})
// 3. 返回上一页
uni.navigateBack()
}
}
</script>
<style scoped lang="scss">
.shareUser {
height: 100vh;
background-color: #f9f9f9;
.shareBody {
padding: 0 40rpx;
.search_box {
height: 80rpx;
margin-bottom: 20rpx;
.search_view {
height: 80rpx;
line-height: 80rpx;
background: #f3f3f3;
border-radius: 8rpx;
font-weight: 400;
font-size: 32rpx;
color: rgba(0, 0, 0, 0.26);
line-height: 48rpx;
text-align: center;
}
}
// 选择人数
.checkoutedUser_box {
margin-top: 20rpx;
margin-bottom: 40rpx;
font-weight: 400;
font-size: 28rpx;
color: #333333;
line-height: 36rpx;
height: 36rpx;
}
// 角色列表
.item {
height: 170rpx;
background: #ffffff;
border: 1rpx solid transparent;
margin-bottom: 20rpx;
padding-left: 26rpx;
padding-right: 32rpx;
.info {
font-weight: 400;
font-size: 28rpx;
color: #333333;
line-height: 36rpx;
height: 36rpx;
margin-bottom: 10rpx;
}
&:deep(radio) {
transform: scale(0.68);
}
&.active {
background: #f5faff;
border: 1rpx solid #4d5dfa;
}
}
// 确定派单
.action_btn {
width: 670rpx;
height: 80rpx;
font-weight: 400;
font-size: 28rpx;
color: #ffffff;
line-height: 80rpx;
text-align: center;
margin-top: 20rpx;
background: #4d5dfa;
border-radius: 8rpx 8rpx 8rpx 8rpx;
}
}
}
</style>

View File

@@ -16,20 +16,33 @@
<view class="form-item"> <view class="form-item">
<text class="label">标题</text> <text class="label">标题</text>
<!-- <input type="text" placeholder="请输入问题标题" v-model="form.title" /> --> <!-- <input type="text" placeholder="请输入问题标题" v-model="form.title" /> -->
<uni-easyinput v-model="form.title" placeholder="请输入内容" :inputBorder="false"></uni-easyinput> <uni-easyinput
v-model="form.title"
placeholder="请输入内容"
:inputBorder="false"
></uni-easyinput>
</view> </view>
<!-- 问题描述 --> <!-- 问题描述 -->
<view class="form-item-wenti"> <view class="form-item-wenti">
<text class="label">问题描述</text> <text class="label">问题描述</text>
<!-- <textarea placeholder="请输入问题内容" v-model="form.desc" rows="5" ></textarea> --> <!-- <textarea placeholder="请输入问题内容" v-model="form.desc" rows="5" ></textarea> -->
<uni-easyinput v-model="form.desc" placeholder="请描述你的问题" type="textarea"></uni-easyinput> <uni-easyinput
v-model="form.desc"
placeholder="请描述你的问题"
type="textarea"
></uni-easyinput>
</view> </view>
<!-- 手机号 --> <!-- 手机号 -->
<view class="form-item"> <view class="form-item">
<text class="label">手机号</text> <text class="label">手机号</text>
<uni-easyinput type="number" v-model="form.phone" placeholder="请输入手机号" :inputBorder="false"></uni-easyinput> <uni-easyinput
type="number"
v-model="form.phone"
placeholder="请输入手机号"
:inputBorder="false"
></uni-easyinput>
</view> </view>
<!-- 图片上传 --> <!-- 图片上传 -->
@@ -39,7 +52,11 @@
<!-- 已上传图片 --> <!-- 已上传图片 -->
<view class="upload-item" v-for="(img, index) in form.images" :key="index"> <view class="upload-item" v-for="(img, index) in form.images" :key="index">
<image :src="img" mode="aspectFill" class="upload-img"></image> <image :src="img" mode="aspectFill" class="upload-img"></image>
<image src="/static/imgs/delete.png" class="upload-delete" @click="deleteImage(index)"></image> <image
src="/static/imgs/delete.png"
class="upload-delete"
@click="deleteImage(index)"
></image>
<!-- <uni-icons type="close" size="16" @click="deleteImage(index)"></uni-icons> --> <!-- <uni-icons type="close" size="16" @click="deleteImage(index)"></uni-icons> -->
</view> </view>
<!-- 上传按钮 --> <!-- 上传按钮 -->
@@ -57,7 +74,7 @@
<script setup> <script setup>
import { ref } from 'vue' import { ref } from 'vue'
import{onLoad} from "@dcloudio/uni-app" import { onLoad } from '@dcloudio/uni-app'
// import { onLoad, uniShowToast, uniNavigateBack, uniChooseImage, uniUploadFile } from '@dcloudio/uni-app' // import { onLoad, uniShowToast, uniNavigateBack, uniChooseImage, uniUploadFile } from '@dcloudio/uni-app'
// 接收上个页面传的维修项目 // 接收上个页面传的维修项目
@@ -72,7 +89,7 @@ const form = ref({
}) })
// 页面加载时获取传参 // 页面加载时获取传参
onLoad((options) => { onLoad(options => {
if (options.category && options.project) { if (options.category && options.project) {
repairProject.value = `${options.category}-${options.project}` repairProject.value = `${options.category}-${options.project}`
} }
@@ -81,27 +98,27 @@ onLoad((options) => {
// 选择项目页 // 选择项目页
const goBackSelect = () => { const goBackSelect = () => {
uni.navigateTo({ uni.navigateTo({
url:"/pageSubPack/fault-reporting/repairSelect" url: '/pageSubPack/fault-reporting/repairSelect'
}) })
} }
// 选择图片 // 选择图片
const chooseImage = () => { const chooseImage = () => {
// 计算还能选几张 // 计算还能选几张
const remainCount = 3 - form.value.images.length; const remainCount = 3 - form.value.images.length
if (remainCount <= 0) { if (remainCount <= 0) {
uni.showToast({ uni.showToast({
title: '最多只能上传3张图片', title: '最多只能上传3张图片',
icon: 'none' icon: 'none'
}) })
return; return
} }
uni.chooseImage({ uni.chooseImage({
count: remainCount, // 动态限制可选数量 count: remainCount, // 动态限制可选数量
sizeType: ['original', 'compressed'], sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'], sourceType: ['album', 'camera'],
success: (res) => { success: res => {
const tempFilePaths = res.tempFilePaths const tempFilePaths = res.tempFilePaths
form.value.images = [...form.value.images, ...tempFilePaths] form.value.images = [...form.value.images, ...tempFilePaths]
} }
@@ -109,7 +126,7 @@ const chooseImage = () => {
} }
// 删除图片 // 删除图片
const deleteImage = (index) => { const deleteImage = index => {
form.value.images.splice(index, 1) form.value.images.splice(index, 1)
} }
@@ -182,7 +199,8 @@ const submitForm = () => {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
.input, .textarea { .input,
.textarea {
font-size: 14px; font-size: 14px;
width: 100%; width: 100%;
padding: 8px 0; padding: 8px 0;
@@ -202,12 +220,12 @@ const submitForm = () => {
width: 80px; width: 80px;
height: 80px; height: 80px;
} }
.upload-item .upload-img{ .upload-item .upload-img {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 4px; border-radius: 4px;
} }
.upload-item .upload-delete{ .upload-item .upload-delete {
position: absolute; position: absolute;
top: -5px; top: -5px;
right: -5px; right: -5px;
@@ -232,7 +250,7 @@ const submitForm = () => {
} }
.submit-btn { .submit-btn {
width: 100%; width: 100%;
background: #4D5DFA; background: #4d5dfa;
color: #fff; color: #fff;
border-radius: 6px; border-radius: 6px;
height: 44px; height: 44px;

View File

@@ -1,5 +1,6 @@
<template> <template>
<view class="page_container inspection_main_box"> <view class="page_padding">
<view class="flex-1 flex-col flex inspection_main_box">
<view class="info_box"> <view class="info_box">
<view class="title_box flex items-center justify-between"> <view class="title_box flex items-center justify-between">
<view class="title flex items-center"> <view class="title flex items-center">
@@ -62,9 +63,10 @@
<view class="inspection_items"> <view class="inspection_items">
<view class="label">巡检照片:</view> <view class="label">巡检照片:</view>
<image <image
lazy-load
class="inspection_photo" class="inspection_photo"
v-for="(ite, ind) in item.photos" v-for="(ite, ind) in item.photos"
:key="index" :key="ind"
:src="ite" :src="ite"
mode="scaleToFill" mode="scaleToFill"
></image> ></image>
@@ -73,6 +75,7 @@
</view> </view>
</view> </view>
</view> </view>
</view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -131,8 +134,16 @@ const result = ref([
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.inspection_main_box { .page_padding {
background: #f9f9f9; background: #f9f9f9;
}
.inspection_main_box {
padding: 20rpx 40rpx;
background: #f9f9f9;
flex: 1;
overflow: hidden;
box-sizing: border-box;
.info_box { .info_box {
padding: 40rpx 30rpx; padding: 40rpx 30rpx;
border-radius: 16rpx; border-radius: 16rpx;
@@ -181,6 +192,10 @@ const result = ref([
background-color: #fff; background-color: #fff;
border-radius: 16rpx; border-radius: 16rpx;
margin-top: 20rpx; margin-top: 20rpx;
flex: 1;
box-sizing: border-box;
overflow: hidden;
overflow-y: auto;
.inspecton_result_title { .inspecton_result_title {
font-size: 32rpx; font-size: 32rpx;
color: #222222; color: #222222;

View File

@@ -1,7 +1,16 @@
<template> <template>
<view class="page_container inspection_record_box"> <!-- 顶部导航栏 -->
<view class="page_padding">
<view class="inspection_record_box flex-1 flex flex-col">
<navBar @clickLeft="navBack" title="巡检记录"></navBar>
<TabsComponents :tabslist="tabslist" @update="handleChangeTabs"></TabsComponents> <TabsComponents :tabslist="tabslist" @update="handleChangeTabs"></TabsComponents>
<view class="record_body"> <scroll-view
class="record_body record-list"
scroll-y
lower-threshold="60"
@scrolltolower="scrolltolower"
>
<!-- v-for --> <!-- v-for -->
<view class="record_item" v-for="(item, index) in list" :key="index"> <view class="record_item" v-for="(item, index) in list" :key="index">
<view class="time"> <view class="time">
@@ -32,12 +41,15 @@
</view> </view>
</view> </view>
</view> </view>
</scroll-view>
</view> </view>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import TabsComponents from '@/components/tabs/tabs.vue' import TabsComponents from '@/components/tabs/tabs.vue'
import navBar from '@/components/navbar/navbar.vue'
import { ref } from 'vue'
const tabslist = [ const tabslist = [
{ {
name: '全部', name: '全部',
@@ -52,13 +64,11 @@ const tabslist = [
value: 2 value: 2
} }
] ]
function handleChangeTabs(val: number) { function handleChangeTabs(val: number) {}
console.log('选择了:', val)
}
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
const list = [ const list = ref([
{ {
day: '10月11日', day: '10月11日',
list: [ list: [
@@ -77,87 +87,99 @@ const list = [
user: '湖色' user: '湖色'
} }
] ]
},
{
day: '10月12日',
list: [
{
title: '标题2',
status: 1,
items: ['防火设施检查'],
time: '09:00:00-12:00:00',
user: '郧西'
} }
] ])
},
{ const queryParmas = ref({
day: '10月12日', pageNum: 1,
list: [ pageSize: 5
{ })
title: '标题2', const isLoading = ref(false) // 加载锁
status: 1, const hasMore = ref(true) // 是否还有更多数据
items: ['防火设施检查'], const total = ref(21)
time: '09:00:00-12:00:00', function scrolltolower() {
user: '郧西' // 必须加这两个判断!
if (isLoading.value || !hasMore.value) {
return
} }
] queryParmas.value.pageNum += 1
}, getlist()
{ }
day: '10月12日', function getlist() {
// 1. 如果正在加载或没有更多数据,直接返回
if (isLoading.value || !hasMore.value) return
// 2. 开启锁
isLoading.value = true
uni.showLoading({
title: '加载中',
mask: true
})
const { pageNum, pageSize } = queryParmas.value
const startId = (pageNum - 1) * pageSize + 1
const arr = Array.from({ length: pageSize }).map((_, index) => {
const realId = startId + index // ✅ 修复 ID 错误
// ✅ 随机生成 0、1、2
const randomStatus = Math.floor(Math.random() * 2)
return {
day: '10月' + realId + '日',
list: [ list: [
{ {
title: '标题2', title: '标题1',
status: 1, status: randomStatus,
items: ['防火设施检查'], items: ['设备巡检'],
time: '09:00:00-12:00:00', time: '07:00:00-09:00:00',
user: '郧西' user: '李兴'
}
]
}, },
{ {
day: '10月12日', title: '标题3',
list: [ status: randomStatus,
{ items: ['设备巡检'],
title: '标题2', time: '07:00:00-09:00:00',
status: 1, user: '湖色'
items: ['防火设施检查'],
time: '09:00:00-12:00:00',
user: '郧西'
}
]
},
{
day: '10月12日',
list: [
{
title: '标题2',
status: 1,
items: ['防火设施检查'],
time: '09:00:00-12:00:00',
user: '郧西'
}
]
},
{
day: '10月12日',
list: [
{
title: '标题2',
status: 1,
items: ['防火设施检查'],
time: '09:00:00-12:00:00',
user: '郧西'
} }
] ]
} }
] })
// 追加数据(正确方式)
list.value = [...list.value, ...arr]
// 判断是否还有更多
hasMore.value = list.value.length < total.value
isLoading.value = false
uni.hideLoading()
}
getlist()
function resetList() {
list.value = []
queryParmas.value.pageNum = 1
hasMore.value = true
getlist()
}
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// 返回上一页
const navBack = () => {
uni.navigateBack()
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.inspection_record_box { .inspection_record_box {
background: linear-gradient(225deg, #e2f0ff 0%, #f9f9f9 50.71%);
padding: 20rpx 40rpx;
position: relative;
flex: 1;
height: 100vh;
overflow: hidden;
box-sizing: border-box;
.record_body { .record_body {
margin-top: 20rpx; margin-top: 20rpx;
flex: 1;
overflow: hidden;
overflow-y: auto;
box-sizing: border-box;
.record_item { .record_item {
& > .time { & > .time {
width: 168rpx; width: 168rpx;

View File

@@ -1,5 +1,6 @@
<template> <template>
<view class="inspection_upload_box page_container"> <view class="page_padding">
<view class="inspection_upload_box flex flex-col">
<view class="inspection_info_box"> <view class="inspection_info_box">
<view class="inspection_title flex flex-between"> <view class="inspection_title flex flex-between">
<view class="title flex"> <view class="title flex">
@@ -40,13 +41,18 @@
<view class="header"> <text>巡检结果</text> </view> <view class="header"> <text>巡检结果</text> </view>
<view class="info_body" :key="index" v-for="(item, index) in baseFormData"> <view class="info_body" :key="index" v-for="(item, index) in baseFormData">
<view class="info_title" style="margin-bottom: 20rpx"> {{ item.name }} </view> <view class="info_title" style="margin-bottom: 20rpx"> {{ item.name }} </view>
<uni-forms :modelValue="item" :rules="rules" label-position="left" label-width="150rpx"> <uni-forms :modelValue="item" :rules="rules" label-position="left" label-width="150">
<uni-forms-item label="巡检状态" required> <uni-forms-item label="巡检状态" required>
<uni-data-checkbox v-model="item.status" :localdata="statuslist" /> <uni-data-checkbox
:disabled="item.complated"
v-model="item.status"
:localdata="statuslist"
/>
</uni-forms-item> </uni-forms-item>
<uni-forms-item label="巡检描述"> <uni-forms-item label="巡检描述">
<uni-easyinput <uni-easyinput
type="textarea" type="textarea"
:disabled="item.complated"
:input-border="true" :input-border="true"
v-model="item.desition" v-model="item.desition"
placeholder="请输入" placeholder="请输入"
@@ -55,22 +61,59 @@
<uni-forms-item label="巡检时间"> <uni-forms-item label="巡检时间">
<uni-datetime-picker <uni-datetime-picker
type="datetime" type="datetime"
:disabled="item.complated"
return-type="timestamp" return-type="timestamp"
v-model="item.datetimesingle" v-model="item.datetimesingle"
/> />
</uni-forms-item> </uni-forms-item>
<uni-forms-item label="巡检照片"> <!-- <uni-forms-item label="巡检照片">
<uni-file-picker v-model="item.files" file-mediatype="image"> <uni-file-picker
@delete="deleteFile"
@select="select"
mode="grid"
v-model="item.files"
:auto-upload="false"
:sourceType="['camera']"
file-mediatype="image"
>
<view class="filePicker flex-center"> <view class="filePicker flex-center">
<uni-icons color="#a6a6a6" type="plusempty" size="25"> </uni-icons> <uni-icons color="#a6a6a6" type="plusempty" size="25"> </uni-icons>
</view> </view>
</uni-file-picker> </uni-file-picker>
</uni-forms-item> </uni-forms-item> -->
<view class="items_picker_box">
<view class="label"> 巡检照片 </view>
<view class="filePickerbox" v-if="!item.complated">
<filePicker
v-model="item.files"
:sourceType="['camera']"
file-field-name="file"
:extra-data="uploadParams"
@success="onUploadSuccess"
@fail="onUploadFail"
></filePicker>
</view>
<view class="filePickerbox showimagegrid" v-else>
<image
class="img"
mode="scaleToFill"
v-for="(ite, ind) in item.files"
lazy-load
:key="ind"
:src="ite"
@tap="previewImages(item.files, index)"
></image>
</view>
</view>
</uni-forms> </uni-forms>
<view class="submitbox" @tap="handleSubmit(item.id)">
<text>提交</text>
</view>
<view class="line" v-if="index + 1 !== baseFormData.length"> </view> <view class="line" v-if="index + 1 !== baseFormData.length"> </view>
</view> </view>
<view class="submitbox"> <!-- <view class="submitbox">
<text>提交</text> <text>提交</text>
</view> -->
</view> </view>
</view> </view>
</view> </view>
@@ -78,6 +121,7 @@
<script setup> <script setup>
import { ref } from 'vue' import { ref } from 'vue'
import filePicker from '@/components/filePicker/filePicker.vue'
const statuslist = [ const statuslist = [
{ {
@@ -97,14 +141,16 @@ const info = ref({
id: index + '' id: index + ''
} }
let obj = { let obj = {
id: obj1.id, id: index,
name: obj1.name, name: obj1.name,
desition: '', // 描述 desition: '', // 描述
datetimesingle: '', // 时间 datetimesingle: '', // 时间
status: 0, // 状态 status: 0, // 状态 0正常1异常
files: [], // 图片 files: [], // 图片
complated: false, // 巡检点完成检查
fileUrls: '' // 图片 fileUrls: '' // 图片
} }
baseFormData.value.push(obj) baseFormData.value.push(obj)
return obj1 return obj1
}) })
@@ -119,15 +165,68 @@ const rules = {
] ]
} }
} }
// ----------------------------------------------
const uploadParams = ref({
type: 'repair', // 上传类型:报修
businessId: '123456', // 关联业务ID
userId: '789' // 用户ID
})
// 选择文件
function onUploadSuccess(e) {
console.log('上传成功', e)
}
function onUploadFail(e) {
console.log('上传失败', e)
}
function submit() {
console.log('最终要提交的图片URL', imageList.value)
// 这里调用你的提交接口
}
/** 提交巡检点 检查情况 */
function handleSubmit(id) {
console.log(typeof id, id, baseFormData.value)
const find = baseFormData.value.find(item => item.id === id)
if (find) {
find.complated = true
}
}
/**
* 预览图片
* @param {Array} images - 所有图片的URL数组
* @param {Number} currentIndex - 当前点击的图片索引
*/
function previewImages(images, currentIndex) {
uni.previewImage({
urls: [
'https://picsum.photos/80',
'https://picsum.photos/80',
'https://picsum.photos/80',
'https://picsum.photos/80'
],
current: currentIndex
})
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.inspection_upload_box { .inspection_upload_box {
background: #f9f9f9;
padding: 20rpx 40rpx;
position: relative;
flex: 1;
overflow: hidden;
box-sizing: border-box;
.inspection_info_box { .inspection_info_box {
background-color: #ffffff; background-color: #ffffff;
padding: 20rpx 40rpx; padding: 20rpx 40rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
border-radius: 16rpx 16rpx 16rpx 16rpx; border-radius: 16rpx;
.inspection_title { .inspection_title {
margin-bottom: 20rpx; margin-bottom: 20rpx;
.title { .title {
@@ -153,6 +252,7 @@ const rules = {
} }
.inspection_item_info_box { .inspection_item_info_box {
width: 100%;
.inspection_item { .inspection_item {
margin-bottom: 20rpx; margin-bottom: 20rpx;
font-weight: 400; font-weight: 400;
@@ -163,10 +263,13 @@ const rules = {
} }
} }
.inspection_info_upload_box { .inspection_info_upload_box {
border-radius: 16rpx 16rpx 16rpx 16rpx; border-radius: 16rpx;
background-color: #fff; background-color: #fff;
padding: 30rpx 40rpx; padding: 30rpx 40rpx;
min-height: 500rpx; flex: 1;
box-sizing: border-box;
overflow: hidden;
overflow-y: auto;
.header { .header {
font-weight: 500; font-weight: 500;
font-size: 32rpx; font-size: 32rpx;
@@ -184,10 +287,27 @@ const rules = {
width: 100%; width: 100%;
border: 1rpx dashed #ccc; border: 1rpx dashed #ccc;
} }
.filePicker { .items_picker_box {
.label {
text-align: left;
font-size: 14px;
color: #606266;
height: 36px;
padding: 0 12px 0 0;
vertical-align: middle;
box-sizing: border-box;
}
.showimagegrid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20rpx;
width: 100%;
.img {
width: 160rpx; width: 160rpx;
height: 160rpx; height: 160rpx;
background: #f3f3f3; }
}
} }
} }
.submitbox { .submitbox {

View File

@@ -1,24 +1,17 @@
<template> <template>
<view class="today_inspection_page"> <view class="page_padding">
<!-- tabs --> <view class="today_inspection_page flex flex-col">
<view class="tabs_box"> <!-- 状态筛选标签 -->
<view <TabsComponents :tabslist="tabslist" @update="handleTabs"></TabsComponents>
v-for="(item, index) in tabslist"
:key="index"
@tap="handleTabs(item)"
class="tabs_items"
:class="{
active: item.value === activeValue
}"
>
{{ item.name }}
</view>
</view>
<!-- tabs -->
<!-- body --> <!-- body -->
<view class="inspection_body"> <view class="inspection_body flex-1">
<view class="inspection_items" v-for="(item, index) in list" :key="index"> <view
class="inspection_items"
v-for="(item, index) in list"
:key="index"
@tap="gotoInspection(item)"
>
<view class="title"> <view class="title">
<text class="subtitle"> <text class="subtitle">
<i></i> <i></i>
@@ -27,7 +20,7 @@
<text class="noactions" v-if="item.status === 0">未巡检</text> <text class="noactions" v-if="item.status === 0">未巡检</text>
<text class="noactions alaway_actions" v-else>已巡检</text> <text class="noactions alaway_actions" v-else>已巡检</text>
</view> </view>
<view class="inspection_item_box"> <view class="inspection_item_box flex">
<text class="inspection_item" v-for="(ite, index) in item.item" :key="index"> <text class="inspection_item" v-for="(ite, index) in item.item" :key="index">
{{ ite }} {{ ite }}
</text> </text>
@@ -39,18 +32,20 @@
<view class="inspection_time"> <view class="inspection_time">
<text>巡检人:</text> <text>巡检人:</text>
<text>{{ item.user }}</text> <text>{{ item.user }}</text>
<view class="actions" v-if="item.status === 0" @tap="gotoInspection"> <view class="actions" v-if="item.status === 0">
<text>去巡检</text> <text>去巡检</text>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</view>
<!-- body --> <!-- body -->
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import TabsComponents from '@/components/tabs/tabs.vue'
// tabs ========================================================= // tabs =========================================================
const activeValue = ref(0) const activeValue = ref(0)
const tabslist = ref([ const tabslist = ref([
@@ -59,36 +54,69 @@ const tabslist = ref([
value: 0 value: 0
}, },
{ {
name: '已完成', name: '未巡检',
value: 1 value: 1
}, },
{ {
name: '已派单', name: '已巡检',
value: 2 value: 2
} }
]) ])
// 点击事件 // 点击事件
const handleTabs = (val: { name: string; value: number }) => { const handleTabs = (val: number) => {
activeValue.value = val.value activeValue.value = val
} }
// tabs ========================================================= // tabs =========================================================
// body ========================================================= // body =========================================================
/** 跳转到巡检上传页面 */ /** 跳转到巡检上传页面 */
const gotoInspection = () => { // 0未巡检 1已巡检
console.log('upload') const gotoInspection = row => {
if (row.status === 0) {
uni.navigateTo({ uni.navigateTo({
url: '/pageSubPack/inspection/inspection_upload' url: '/pageSubPack/inspection/inspection_upload'
}) })
} else {
uni.navigateTo({
url: '/pageSubPack/inspection/inspection_main'
})
}
} }
// body ========================================================= // body =========================================================
const list = ref([ const list = ref([
{ {
address: '幸福苑小区- B1层地下车库', address: '幸福苑小区- B1层地下车库',
time: '07:00:00 -09:00:00', time: '07:00:00 -09:00:00',
item: ['防火设施安全巡检'], item: [
'防火设施安全巡检',
'防火设施安全巡检',
'防火设施安全巡检',
'防火设施安全巡检',
'防火设施安全巡检'
],
user: '李相龙', user: '李相龙',
status: 0 // 0未巡检 1已巡检 status: 0 // 0未巡检 1已巡检
}, },
{
address: '幸福苑小区- B2层地下车库',
time: '13:00:00 -15:00:00',
item: ['设备巡检'],
user: '李相龙',
status: 1 // 0未巡检 1已巡检
},
{
address: '幸福苑小区- B2层地下车库',
time: '13:00:00 -15:00:00',
item: ['设备巡检'],
user: '李相龙',
status: 1 // 0未巡检 1已巡检
},
{
address: '幸福苑小区- B2层地下车库',
time: '13:00:00 -15:00:00',
item: ['设备巡检'],
user: '李相龙',
status: 1 // 0未巡检 1已巡检
},
{ {
address: '幸福苑小区- B2层地下车库', address: '幸福苑小区- B2层地下车库',
time: '13:00:00 -15:00:00', time: '13:00:00 -15:00:00',
@@ -101,11 +129,16 @@ const list = ref([
<style scoped lang="scss"> <style scoped lang="scss">
/* 页面容器 */ /* 页面容器 */
.page_padding {
background: #f9f9f9;
}
.today_inspection_page { .today_inspection_page {
padding: 20rpx 40rpx 40rpx; background: #f9f9f9;
min-height: 100vh; padding: 20rpx 40rpx;
max-height: 100vh;
background: repeating-linear-gradient(180deg, #e2f0ff, #f6faff); flex: 1;
box-sizing: border-box;
overflow: hidden;
// tabs // tabs
.tabs_box { .tabs_box {
@@ -151,9 +184,8 @@ const list = ref([
.inspection_items { .inspection_items {
margin-top: 20rpx; margin-top: 20rpx;
padding: 40rpx; padding: 40rpx;
background-color: #fff;
border-radius: 16rpx; border-radius: 16rpx;
background-color: #ffffff;
.title { .title {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -195,17 +227,20 @@ const list = ref([
.inspection_item_box { .inspection_item_box {
margin-top: 20rpx; margin-top: 20rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
flex-wrap: wrap;
.inspection_item { .inspection_item {
border: 1px solid #4d5dfa; border: 1px solid #4d5dfa;
margin-bottom: 10rpx;
margin-right: 10rpx;
padding: 0 14rpx; padding: 0 14rpx;
height: 48rpx; height: 48rpx;
line-height: 48rpx;
border-radius: 198rpx 198rpx 198rpx 198rpx; border-radius: 198rpx 198rpx 198rpx 198rpx;
border: 2rpx solid #4d5dfa; border: 2rpx solid #4d5dfa;
font-size: 28rpx; font-size: 28rpx;
color: #4d5dfa; color: #4d5dfa;
line-height: 36rpx;
} }
} }

View File

@@ -5,21 +5,24 @@
<uni-data-checkbox v-model="formData.type" :localdata="items"></uni-data-checkbox> <uni-data-checkbox v-model="formData.type" :localdata="items"></uni-data-checkbox>
</uni-forms-item> </uni-forms-item>
<uni-forms-item label="设备编号"> <uni-forms-item label="设备编号">
<view class="flex items-center justify-between">
<uni-easyinput <uni-easyinput
class="flex-1"
:input-border="false" :input-border="false"
v-model="formData.deviceCode" v-model="formData.deviceCode"
placeholder="请输入设备号" placeholder="请输入设备号"
:clearable="true"
> >
<template #right>
<uni-icons type="scan" color="#737373"></uni-icons>
</template>
</uni-easyinput> </uni-easyinput>
<uni-icons @tap="handleScan" size="48rpx" type="scan" color="#737373"></uni-icons>
</view>
</uni-forms-item> </uni-forms-item>
<uni-forms-item label="房屋号"> <uni-forms-item label="房屋号">
<uni-easyinput <uni-easyinput
:inputBorder="false" :inputBorder="false"
@tap="handleClick" @tap="handleClick"
disabled disabled
v-model="houseName"
placeholderStyle="color: #999999" placeholderStyle="color: #999999"
placeholder="请选择安装房屋" placeholder="请选择安装房屋"
> >
@@ -29,11 +32,16 @@
</uni-easyinput> </uni-easyinput>
</uni-forms-item> </uni-forms-item>
</uni-forms> </uni-forms>
<view class="action_btn" @tap="handleSubmit">
<text>提 交</text>
</view>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup>
import { ref } from 'vue' import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const formData = ref({ const formData = ref({
type: 1, type: 1,
deviceCode: '', deviceCode: '',
@@ -50,9 +58,30 @@ const items = [
} }
] ]
const houseName = ref('')
/** 处理 安装房屋 */
function handleClick() { function handleClick() {
console.log('click') uni.navigateTo({
url: '/pageSubPack/instrument/checkouthouse',
events: {
backData: data => {
houseName.value = data.HouseName
formData.value.houseId = data.Houseid
}
}
})
} }
/** 处理 扫码 */
function handleScan() {
// 允许从相机和相册扫码
uni.scanCode({
success: function (res) {
formData.value.deviceCode = res.result
}
})
}
function handleSubmit() {}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -70,5 +99,21 @@ function handleClick() {
&:deep(.uni-easyinput__content) { &:deep(.uni-easyinput__content) {
background: none !important; background: none !important;
} }
.action_btn {
width: 670rpx;
height: 80rpx;
background: #4d5dfa;
border-radius: 8rpx;
text-align: center;
margin-top: 300rpx;
font-weight: 400;
font-size: 28rpx;
color: #ffffff;
line-height: 80rpx;
}
} }
</style> </style>

View File

@@ -1,5 +1,5 @@
<template> <template>
<view class="page_container checkouthouse_box"> <view class="page_container checkouthouse_box flex flex-col">
<view class="search_box"> <view class="search_box">
<view <view
@click="handleClick" @click="handleClick"
@@ -18,13 +18,8 @@
v-else v-else
/> />
</view> </view>
<view class="search_list"> <view class="search_list flex-1 flex">
<scroll-view <scroll-view lower-threshold="160" scroll-y="true" @scrolltolower="scrolltolower">
style="height: 100%"
lower-threshold="160"
scroll-y="true"
@scrolltolower="scrolltolower"
>
<radio-group @change="radioChange"> <radio-group @change="radioChange">
<label v-for="(item, index) in searchlist" :key="index"> <label v-for="(item, index) in searchlist" :key="index">
<view <view
@@ -46,17 +41,20 @@
</radio-group> </radio-group>
</scroll-view> </scroll-view>
</view> </view>
<view class="action_btn" @tap="handleSelecthouse">
<text>确 认</text>
</view>
</view> </view>
<!-- 滚动 高度如何计算? --> <!-- 滚动 高度如何计算? -->
</template> </template>
<script setup lang="ts"> <script setup>
import { ref } from 'vue' import { ref, getCurrentInstance, onMounted } from 'vue'
const Searching = ref(true) const Searching = ref(true)
const queryParmas = ref({ const queryParmas = ref({
value: '', value: '',
pageNum: 1, pageNum: 1,
pageSize: 12 pageSize: 10
}) })
/** 点击时切换到input,并聚焦 */ /** 点击时切换到input,并聚焦 */
function handleClick() { function handleClick() {
@@ -68,49 +66,101 @@ function handleInput() {
Searching.value = true Searching.value = true
} }
} }
function handleSearch() { function handleSearch() {}
console.log('search')
}
const checkoutHouseid = ref(null) const checkoutHouseid = ref(null)
const checkoutHouseInfo = ref(null)
/** 选择房屋,返回id*/ /** 选择房屋,返回id*/
function radioChange(res) { function radioChange(res) {
checkoutHouseid.value = Number(res.detail.value) checkoutHouseid.value = Number(res.detail.value)
} const find = searchlist.value.find(item => item.id === Number(res.detail.value))
// if (find) {
const searchlist = ref() checkoutHouseInfo.value = find
function getlist() {
const allnum = queryParmas.value.pageNum * queryParmas.value.pageSize
searchlist.value = Array.from({ length: allnum }).map((_, index) => {
return {
address: 'A栋3单元301',
username: '里番',
id: index + 1
} }
})
} }
getlist()
// -0---------------------------------------
const searchlist = ref([])
const isLoading = ref(false) // 加载锁
const hasMore = ref(true) // 是否还有更多数据
const total = ref(21)
function scrolltolower() { function scrolltolower() {
// 必须加这两个判断!
if (isLoading.value || !hasMore.value) {
return
}
queryParmas.value.pageNum += 1 queryParmas.value.pageNum += 1
getlist() getlist()
} }
function getlist() {
// 1. 如果正在加载或没有更多数据,直接返回
if (isLoading.value || !hasMore.value) return
// 2. 开启锁
isLoading.value = true
uni.showLoading({
title: '加载中',
mask: true
})
const { pageNum, pageSize } = queryParmas.value
const startId = (pageNum - 1) * pageSize + 1
const arr = Array.from({ length: pageSize }).map((_, index) => {
const realId = startId + index // ✅ 修复 ID 错误
return {
id: realId,
username: '李赫',
address: 'A栋3单元3' + realId
}
})
// 追加数据(正确方式)
searchlist.value = [...searchlist.value, ...arr]
// 判断是否还有更多
hasMore.value = searchlist.value.length < total.value
isLoading.value = false
uni.hideLoading()
}
getlist()
function resetList() {
searchlist.value = []
queryParmas.value.pageNum = 1
hasMore.value = true
getlist()
}
// -0---------------------------------------
let eventChannel = null
// 必须在 onMounted 里获取
onMounted(() => {
const instance = getCurrentInstance().proxy
if (instance && instance.getOpenerEventChannel) {
eventChannel = instance.getOpenerEventChannel()
}
})
function handleSelecthouse() {
console.log(checkoutHouseInfo.value)
if (!eventChannel) return
eventChannel.emit('backData', {
Houseid: checkoutHouseid.value,
HouseName: checkoutHouseInfo.value.address
})
// 3. 返回上一页
uni.navigateBack()
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.checkouthouse_box { .checkouthouse_box {
background: #f9f9f9; background: #f9f9f9;
height: 100%;
overflow: hidden;
box-sizing: border-box;
.search_box { .search_box {
height: 80rpx; height: 80rpx;
.search_view { .search_view {
height: 80rpx; height: 80rpx;
line-height: 80rpx; line-height: 80rpx;
background: #f3f3f3; background: #f3f3f3;
border-radius: 8rpx 8rpx 8rpx 8rpx; border-radius: 8rpx;
font-weight: 400; font-weight: 400;
font-size: 32rpx; font-size: 32rpx;
@@ -121,9 +171,10 @@ function scrolltolower() {
} }
.search_list { .search_list {
height: calc(100% - 140rpx); height: 100%;
background-color: #4d5dfa;
margin-top: 20rpx; margin-top: 20rpx;
box-sizing: border-box;
overflow: hidden;
.search_item { .search_item {
height: 116rpx; height: 116rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
@@ -157,5 +208,18 @@ function scrolltolower() {
} }
} }
} }
.action_btn {
width: 670rpx;
height: 80rpx;
font-weight: 400;
font-size: 28rpx;
color: #ffffff;
line-height: 80rpx;
text-align: center;
margin-top: 20rpx;
background: #4d5dfa;
border-radius: 8rpx 8rpx 8rpx 8rpx;
}
} }
</style> </style>

View File

@@ -1,18 +1,20 @@
<template> <template>
<view class="page_container replacement_record_box"> <view class="page_padding">
<view class="replacement_record_box flex-1 flex flex-col">
<view class="nav flex items-center"> <view class="nav flex items-center">
<view class="line"></view> <view class="line"></view>
<text class="title">仪表换新记录</text> <text class="title">仪表换新记录</text>
</view> </view>
<TabsComponents :tabslist="tabslist" @update="handleChangeTabs"></TabsComponents> <TabsComponents :tabslist="tabslist" @update="handleChangeTabs"></TabsComponents>
<view class="instrument_body"> <view class="instrument_body flex-1 flex">
<scroll-view scroll-y lower-threshold="160" @scrolltolower="scrolltolower">
<view <view
class="instrument_items flex items-center flex-between" class="instrument_items flex items-center flex-between"
v-for="(item, index) in infolist" v-for="(item, index) in infolist"
:key="index" :key="index"
> >
<view class="flex items-center"> <view class="flex items-center">
<image src="/static/imgs/home.png" class="icon" mode=""></image> <image lazy-load src="/static/imgs/home.png" class="icon" mode=""></image>
<view class="instrument_info"> <view class="instrument_info">
<view class="instrument_title"> <view class="instrument_title">
<text>{{ item.title }}</text> <text>{{ item.title }}</text>
@@ -27,13 +29,35 @@
<text v-else>水表</text> <text v-else>水表</text>
</view> </view>
</view> </view>
<!-- 底部加载状态提示 -->
<uni-load-more :status="statusmore"></uni-load-more>
</scroll-view>
</view>
<view class="action_btn" @tap="handleClick">
<text>新增</text>
</view>
</view> </view>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { computed, nextTick, ref } from 'vue'
import TabsComponents from '@/components/tabs/tabs.vue' import TabsComponents from '@/components/tabs/tabs.vue'
/** 加载状态 */
const statusmore = computed(() => {
if (isLoading.value === true) {
return 'loading'
} else if (isLoading.value === false) {
if (hasMore.value) {
return 'more'
} else {
return 'noMore'
}
}
})
const isLoading = ref(false) // 加载锁
const hasMore = ref(true) // 是否还有更多数据
const tabslist = [ const tabslist = [
{ {
name: '全部', name: '全部',
@@ -49,37 +73,79 @@ const tabslist = [
} }
] ]
function handleChangeTabs(val: number) { function handleChangeTabs(val: number) {
console.log('选择了:', val) queryParmas.value.pageNum = 1
infolist.value = []
getlist()
} }
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
const infolist = ref([])
const infolist = ref([ const queryParmas = ref({
{ pageNum: 1,
title: 'M2栋3单元1801室(M2-3-1801)', pageSize: 10
time: '2023-08-01 10:05:46', })
type: 1 //1水表 2电表 const total = ref(21)
}, function scrolltolower() {
{ // 必须加这两个判断!
title: 'M2栋3单元1801室(M2-3-1801)', if (isLoading.value || !hasMore.value) {
time: '2023-08-01 10:05:46', return
type: 2 //1水表 2电表
},
{
title: 'M2栋3单元1801室(M2-3-1801)',
time: '2023-08-01 10:05:46',
type: 1 //1水表 2电表
},
{
title: 'M2栋3单元1801室(M2-3-1801)',
time: '2023-08-01 10:05:46',
type: 2 //1水表 2电表
} }
]) queryParmas.value.pageNum += 1
getlist()
}
function getlist() {
// 1. 如果正在加载或没有更多数据,直接返回
if (isLoading.value || !hasMore.value) return
// 2. 开启锁
isLoading.value = true
uni.showLoading({
title: '加载中',
mask: true
})
const { pageNum, pageSize } = queryParmas.value
const startId = (pageNum - 1) * pageSize + 1
const arr = Array.from({ length: pageSize }).map((_, index) => {
const realId = startId + index // ✅ 修复 ID 错误
// ✅ 随机生成 0、1、2
const randomStatus = Math.floor(Math.random() * 2)
return {
title: 'M2栋3单元18' + realId + '室(M2-3-1801)',
time: '2023-08-01 10:05:46',
type: randomStatus //1水表 2电表
}
})
// 追加数据(正确方式)
infolist.value = [...infolist.value, ...arr]
// 判断是否还有更多
hasMore.value = infolist.value.length < total.value
isLoading.value = false
uni.hideLoading()
}
getlist()
function resetList() {
infolist.value = []
queryParmas.value.pageNum = 1
hasMore.value = true
getlist()
}
function handleClick() {
uni.navigateTo({
url: '/pageSubPack/instrument/addreplacement'
})
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.replacement_record_box { .replacement_record_box {
background: #f9f9f9; background: #f9f9f9;
padding: 20rpx 40rpx;
position: relative;
flex: 1;
overflow: hidden;
box-sizing: border-box;
.nav { .nav {
margin-bottom: 40rpx; margin-bottom: 40rpx;
font-weight: 550; font-weight: 550;
@@ -95,11 +161,15 @@ const infolist = ref([
} }
.instrument_body { .instrument_body {
overflow: hidden;
margin-top: 20rpx; margin-top: 20rpx;
box-sizing: border-box;
.instrument_items { .instrument_items {
box-sizing: border-box;
background: #ffffff; background: #ffffff;
padding: 40rpx 20rpx; padding: 40rpx 20rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
.icon { .icon {
width: 40rpx; width: 40rpx;
height: 32rpx; height: 32rpx;
@@ -128,5 +198,18 @@ const infolist = ref([
} }
} }
} }
.action_btn {
width: 100%;
height: 80rpx;
background: #4d5dfa;
border-radius: 8rpx;
text-align: center;
font-weight: 400;
font-size: 28rpx;
color: #ffffff;
line-height: 80rpx;
margin-top: 20rpx;
}
} }
</style> </style>

View File

@@ -1,26 +1,15 @@
<template> <template>
<view class="report-record-page"> <view class="report-record-page page_padding">
<view class="status-bar"></view>
<!-- 顶部导航栏 --> <!-- 顶部导航栏 -->
<uni-nav-bar <navBar @clickLeft="navBack" title="上报记录"></navBar>
left-icon="back" <view class="hall_body flex-1 flex flex-col">
title="上报记录" <view class="bgaction"> </view>
backgroundColor="transparent"
:border= "false"
@clickLeft="navBack"/>
<!-- 状态筛选标签 --> <!-- 状态筛选标签 -->
<view class="status-tabs"> <TabsComponents
<view :tabslist="statusTabs"
v-for="item in statusTabs" :options="{ label: 'label', value: 'value' }"
:key="item.value" @update="switchStatus"
class="status-tab-item" ></TabsComponents>
:class="{ active: activeStatus === item.value }"
@tap="switchStatus(item.value)"
>
{{ item.label }}
</view>
</view>
<!-- 类型筛选标签 --> <!-- 类型筛选标签 -->
<view class="type-tabs"> <view class="type-tabs">
@@ -37,27 +26,35 @@
<!-- 上报记录列表 --> <!-- 上报记录列表 -->
<scroll-view <scroll-view
class="record-list" class="record-list flex-1"
scroll-y scroll-y
:style="{ height: `calc(100vh - ${navHeight + statusTabHeight + typeTabHeight}px)` }" lower-threshold="60"
@scrolltolower="scrolltolower"
> >
<!-- 空状态 --> <!-- 空状态 -->
<view v-if="filteredRecords.length === 0" class="empty-state"> <view v-if="reportRecords.length === 0" class="empty-state">
<uni-icons type="empty" size="60" color="#ccc"></uni-icons> <uni-icons type="empty" size="60" color="#ccc"></uni-icons>
<text class="empty-text">暂无上报记录</text> <text class="empty-text">暂无上报记录</text>
</view> </view>
<!-- 记录卡片 --> <!-- 记录卡片 -->
<view <view
v-for="record in filteredRecords" v-for="(record, index) in reportRecords"
:key="record.id" :key="index"
class="record-card" class="record-card"
@tap="handleMain(record.id)"
> >
<!-- 标题 + 状态标签 --> <!-- 标题 + 状态标签 -->
<view class="card-header"> <view class="card-header">
<text class="card-title">{{ record.title }}</text> <text class="card-title">{{ record.title }}</text>
<view class="status-tag" :class="record.status"> <view
{{ getStatusText(record.status) }} class="status-tag"
:class="{
pending: record.repairStatus === 0,
processing: record.repairStatus === 1,
completed: record.repairStatus === 2
}"
>
{{ getStatusText(record.repairStatus) }}
</view> </view>
</view> </view>
@@ -71,25 +68,40 @@
<view class="card-content"> <view class="card-content">
{{ record.content }} {{ record.content }}
</view> </view>
<!-- 工单图片 -->
<!-- <view class="card-image">
<image
v-for="(item, index) in record.images"
:key="index"
lazy-load
:src="item"
class="images"
mode="scaleToFill"
@tap="handlePrivew(record.images)"
></image>
</view> -->
<!-- 底部信息 --> <!-- 底部信息 -->
<view class="card-footer"> <view class="card-footer">
<text class="create-time">{{ record.createTime }}</text> <text class="create-time">{{ record.createTime }}</text>
<text v-if="record.repairman" class="repairman">维修师傅:{{ record.repairman }}</text> <text v-if="record.repairStatus !== 0" class="repairman"
>维修师傅:{{ record.repairman }}</text
>
<!-- <view class="share" v-if="record.repairStatus === 0">
<text>分配</text>
</view> -->
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
</view>
</template> </template>
<script setup> <script setup>
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import {onReady} from "@dcloudio/uni-app" import { onReady } from '@dcloudio/uni-app'
import TabsComponents from '@/components/tabs/tabs.vue'
// 导航栏/标签高度(适配不同设备) import NavBar from '@/components/navbar/navbar.vue'
const navHeight = ref(44)
const statusTabHeight = ref(88)
const typeTabHeight = ref(88)
// 筛选状态 // 筛选状态
const activeStatus = ref('all') // 全部/待分配/待解决/已完成 const activeStatus = ref('all') // 全部/待分配/待解决/已完成
@@ -111,101 +123,144 @@ const typeTabs = ref([
]) ])
// 模拟上报记录数据(实际项目替换为接口请求) // 模拟上报记录数据(实际项目替换为接口请求)
const reportRecords = ref([ const reportRecords = ref([])
{
id: 1, const queryParmas = ref({
pageNum: 1,
pageSize: 5,
value: ''
})
const isLoading = ref(false) // 加载锁
const hasMore = ref(true) // 是否还有更多数据
const total = ref(21)
function scrolltolower() {
// 必须加这两个判断!
if (isLoading.value || !hasMore.value) {
return
}
queryParmas.value.pageNum += 1
getlist()
}
function getlist() {
// 1. 如果正在加载或没有更多数据,直接返回
if (isLoading.value || !hasMore.value) return
// 2. 开启锁
isLoading.value = true
uni.showLoading({
title: '加载中',
mask: true
})
const { pageNum, pageSize } = queryParmas.value
const startId = (pageNum - 1) * pageSize + 1
const arr = Array.from({ length: pageSize }).map((_, index) => {
const realId = startId + index
const randomStatus = Math.floor(Math.random() * 3)
return {
id: realId,
title: '标题巴拉巴拉巴拉', title: '标题巴拉巴拉巴拉',
status: 'pending', // pending/processing/completed status: 'pending', // pending/processing/completed
reportType: '公共报修', reportType: '公共报修',
category: '路灯', category: '路灯',
content: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。', content: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
createTime: '2026-08-01 10:05:46', createTime: '2026-08-01 10:05:46',
repairman: '' repairman: '张三' + realId,
}, images: [
{ 'https://picsum.photos/93',
id: 2, 'https://picsum.photos/93',
title: '标题巴拉巴拉巴拉', 'https://picsum.photos/93',
status: 'processing', 'https://picsum.photos/93',
reportType: '公共报修', 'https://picsum.photos/93'
category: '路灯', ],
content: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。', repairStatus: randomStatus // 0 待分配,1待解决 2已完成
createTime: '2026-08-01 10:05:46',
repairman: '张三'
},
{
id: 3,
title: '标题巴拉巴拉巴拉',
status: 'completed',
reportType: '公共报修',
category: '路灯',
content: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
createTime: '2026-08-01 10:05:46',
repairman: '张三'
} }
])
// 筛选后的记录
const filteredRecords = computed(() => {
return reportRecords.value.filter(record => {
// 状态筛选
const statusMatch = activeStatus.value === 'all' || record.status === activeStatus.value
// 类型筛选
const typeMatch = activeType.value === 'all' || record.reportType === typeTabs.value.find(item => item.value === activeType.value)?.label
return statusMatch && typeMatch
}) })
}) // 追加数据(正确方式)
reportRecords.value = [...reportRecords.value, ...arr]
// 判断是否还有更多
hasMore.value = reportRecords.value.length < total.value
isLoading.value = false
uni.hideLoading()
}
getlist()
function resetList() {
reportRecords.value = []
queryParmas.value.pageNum = 1
hasMore.value = true
getlist()
}
// 0-------------------------------
// 切换状态(全部/待分配/待解决/已完成)筛选 // 切换状态(全部/待分配/待解决/已完成)筛选
const switchStatus = (value) => { const switchStatus = value => {
activeStatus.value = value activeStatus.value = value
} }
// 切换类型(公共/个人)筛选 // 切换类型(公共/个人)筛选
const switchType = (value) => { const switchType = value => {
activeType.value = value activeType.value = value
} }
const statusMap = ['待分配', '待解决', '已完成']
// 获取状态文本
const getStatusText = status => {
return statusMap[status] || ''
}
// 返回上一页 // 返回上一页
const navBack = () => { const navBack = () => {
uni.switchTab({ uni.switchTab({
url:"/pages/my/my" url: '/pages/my/my'
}) })
} }
/* 0 待分配,1待解决 2已完成*/
// 获取状态文本 function handleMain(id) {
const getStatusText = (status) => { const find = reportRecords.value.find(item => item.id === id)
const statusMap = { if (find) {
pending: '待分配', const status = find.repairStatus
processing: '待解决', if (status === 2) {
completed: '已完成' uni.navigateTo({
} url: `/pageSubPack/ticket/repairMain?id=${id}&showbtn=${false}&path=hall`
return statusMap[status] || '' })
}
// 页面渲染完成后适配高度
onReady(() => {
// 可根据实际设备调整高度
uni.getSystemInfo({
success: (res) => {
if (res.platform === 'ios') {
navHeight.value = 44
} else { } else {
navHeight.value = 48 uni.navigateTo({
} url: `/pageSubPack/ticket/repairMain?id=${id}&showbtn=${false}&path=hall`
}
}) })
}) }
}
}
/* 预览图片 */
function handlePrivew(imgs) {
// if (imgs.length > 0) {
// uni.previewImage({
// urls: imgs,
// success(e) {
// console.log(JSON.stringify(e))
// }
// })
// }
}
</script> </script>
<style scoped> <style scoped>
.report-record-page { .bgaction {
position: absolute;
right: 0;
top: 20%;
width: 100%; width: 100%;
height: 100vh; height: 100%;
background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%) z-index: 0;
background: linear-gradient(225deg, #e2f0ff 0%, #f9f9f9 25%);
} }
.status-bar{ .report-record-page {
width: 100%; height: 100vh;
height: 46px; background: linear-gradient(225deg, #e2f0ff 0%, #f9f9f9 25.71%);
}
.hall_body {
padding: 0 40rpx;
overflow: hidden;
position: relative;
} }
/* 状态筛选标签 */ /* 状态筛选标签 */
.status-tabs { .status-tabs {
@@ -216,66 +271,40 @@ onReady(() => {
align-items: center; align-items: center;
} }
.status-tab-item {
/* flex: 1; */
display: flex;
justify-content: flex-start;
margin-right: 60rpx;
text-align: left;
font-size: 32rpx;
color: #666;
height: 100%;
line-height: 88rpx;
position: relative;
}
.status-tab-item.active {
color: #4D5DFA;
font-weight: 500;
}
.status-tab-item.active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 80rpx;
height: 8rpx;
background: linear-gradient(to bottom, #4D5DFA, #BAC0FA);
border-radius: 6rpx;
}
/* 类型筛选标签 */ /* 类型筛选标签 */
.type-tabs { .type-tabs {
display: flex; display: flex;
height: 48rpx;
/* background-color: #fff; */ /* background-color: #fff; */
padding: 20rpx 0rpx; margin: 20rpx 0;
margin-top: 10rpx;
justify-content: flex-start; justify-content: flex-start;
gap: 20rpx; gap: 20rpx;
margin: 0rpx 40rpx;
} }
.type-tab-item { .type-tab-item {
padding: 12rpx 30rpx; width: 142rpx;
height: 48rpx;
border-radius: 24rpx;
border: 2rpx solid #999999;
font-weight: 400;
font-size: 28rpx; font-size: 28rpx;
color: #999; color: #999999;
border: 2rpx solid #999; line-height: 48rpx;
border-radius: 198rpx; text-align: center;
} }
.type-tab-item.active { .type-tab-item.active {
color: #4D5DFA; color: #4d5dfa;
border: 2rpx solid #4D5DFA; border: 2rpx solid #4d5dfa;
border-radius: 198rpx; border-radius: 24rpx;
/* background-color: #007AFF; */
} }
/* 记录列表 */ /* 记录列表 */
.record-list { .record-list {
padding: 20rpx 40rpx;
box-sizing: border-box; box-sizing: border-box;
padding-bottom: 40rpx;
overflow: auto;
} }
/* 空状态 */ /* 空状态 */
@@ -298,7 +327,7 @@ onReady(() => {
.record-card { .record-card {
background-color: #fff; background-color: #fff;
border-radius: 16rpx; border-radius: 16rpx;
padding: 30rpx; padding: 34rpx 32rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
} }
@@ -311,54 +340,80 @@ onReady(() => {
} }
.card-title { .card-title {
font-size: 32rpx;
font-weight: 500;
color: #333;
margin-right: 20rpx; margin-right: 20rpx;
font-weight: 450;
font-size: 32rpx;
color: #222222;
line-height: 40rpx;
} }
/* 状态标签 */ /* 状态标签 */
.status-tag { .status-tag {
padding: 6rpx 16rpx; width: 82rpx;
height: 30rpx;
border-radius: 15rpx;
font-weight: 400;
font-size: 20rpx; font-size: 20rpx;
border-radius: 12rpx; color: #ffffff;
color: #fff; line-height: 30rpx;
border-radius: 198rpx; text-align: center;
line-height: 30rpx;
} }
.status-tag.pending { .status-tag.pending {
background-color: #E34D59; /* 待分配-红色 */ background-color: #e34d59; /* 待分配-红色 */
} }
.status-tag.processing { .status-tag.processing {
background-color: #09C2BD; /* 待解决-绿色 */ background-color: #09c2bd; /* 待解决-绿色 */
} }
.status-tag.completed { .status-tag.completed {
background-color: #4D5DFA; /* 已完成-蓝色 */ background-color: #4d5dfa; /* 已完成-蓝色 */
} }
/* 类型标签组 */ /* 类型标签组 */
.tag-group { .tag-group {
display: flex; display: flex;
gap: 15rpx; gap: 20rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
.tag-item { .tag-item {
padding: 6rpx 20rpx; width: 112rpx;
height: 42rpx;
border-radius: 8rpx 8rpx 8rpx 8rpx;
border: 1rpx solid #999999;
font-weight: 400;
font-size: 24rpx; font-size: 24rpx;
color: #666; color: #999999;
border: 1rpx solid #999; line-height: 42rpx;
border-radius: 8rpx; text-align: center;
} }
/* 内容 */ /* 内容 */
.card-content { .card-content {
font-size: 28rpx;
color: #666;
line-height: 1.6;
margin-bottom: 20rpx; margin-bottom: 20rpx;
font-weight: 450;
font-size: 28rpx;
color: #666666;
line-height: 40rpx;
letter-spacing: 2rpx;
}
/* 图片 */
.card-image {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20rpx;
min-width: 186rpx;
min-height: 104rpx;
margin-bottom: 20rpx;
}
.images {
width: 186rpx;
height: 104rpx;
} }
/* 底部信息 */ /* 底部信息 */
@@ -368,9 +423,21 @@ onReady(() => {
align-items: center; align-items: center;
font-size: 28rpx; font-size: 28rpx;
color: #999; color: #999;
}
.repairman { font-weight: 400;
color: #999; font-size: 28rpx;
line-height: 40rpx;
}
.share {
width: 127rpx;
height: 44rpx;
background: #4d5dfa;
border-radius: 22rpx;
font-weight: 400;
font-size: 28rpx;
color: #ffffff;
line-height: 44rpx;
text-align: center;
} }
</style> </style>

View File

@@ -22,14 +22,16 @@
<!-- 修改密码设置项 --> <!-- 修改密码设置项 -->
<view class="setting-item" @tap="goToModifyPassword"> <view class="setting-item" @tap="goToModifyPassword">
<view class="item-label">修改密码</view> <view class="item-label">修改密码</view>
<view class="item-value">{{passwordNumber}}</view> <view class="item-value">{{ passwordNumber }}</view>
<uni-icons type="arrowright" size="20" color="#ccc"></uni-icons> <uni-icons type="arrowright" size="20" color="#ccc"></uni-icons>
</view> </view>
</scroll-view> </scroll-view>
<!-- 退出登录按钮 --> <!-- 退出登录按钮 -->
<view class="logout-btn-wrap"> <view class="logout-btn-wrap">
<button class="logout-btn" :disabled="!phoneNumber || !passwordNumber" @tap="handleLogout">退出登录</button> <button class="logout-btn" :disabled="!phoneNumber || !passwordNumber" @tap="handleLogout">
退出登录
</button>
</view> </view>
</view> </view>
</template> </template>
@@ -43,11 +45,11 @@ const phoneNumber = ref('15699991212') // 绑定手机号
const passwordNumber = ref('') const passwordNumber = ref('')
// 页面加载时可从接口获取用户信息 // 页面加载时可从接口获取用户信息
onLoad((option) => { onLoad(option => {
// console.log('option',option) // console.log('option',option)
passwordNumber.value = option.newPassword passwordNumber.value = option.newPassword
const userInfo = uni.getStorageSync('tempChangePwdParams') const userInfo = uni.getStorageSync('tempChangePwdParams')
if(userInfo.phoneNumber){ if (userInfo.phoneNumber) {
phoneNumber.value = userInfo.phoneNumber phoneNumber.value = userInfo.phoneNumber
} }
}) })
@@ -55,7 +57,7 @@ onLoad((option) => {
// 返回上一页 // 返回上一页
const goBack = () => { const goBack = () => {
uni.switchTab({ uni.switchTab({
url:"/pages/my/my" url: '/pages/my/my'
}) })
} }
@@ -80,7 +82,7 @@ const handleLogout = () => {
content: '确定要退出当前账号吗?', content: '确定要退出当前账号吗?',
confirmText: '确定', confirmText: '确定',
cancelText: '取消', cancelText: '取消',
success: (res) => { success: res => {
if (res.confirm) { if (res.confirm) {
confirmLogout() // 确认退出 confirmLogout() // 确认退出
} }
@@ -123,7 +125,7 @@ const confirmLogout = () => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.status_bar{ .status_bar {
width: 100%; width: 100%;
height: 46px; height: 46px;
} }
@@ -201,7 +203,7 @@ const confirmLogout = () => {
width: 100%; width: 100%;
height: 44px; height: 44px;
line-height: 44px; line-height: 44px;
background-color: #4D5DFA; background-color: #4d5dfa;
color: #fff; color: #fff;
border: none; border: none;
border-radius: 6px; border-radius: 6px;

View File

@@ -0,0 +1,436 @@
<template>
<view class="report-record-page page_padding">
<!-- 顶部导航栏 -->
<navBar @clickLeft="navBack" title="工单大厅"></navBar>
<view class="hall_body flex flex-col">
<view class="bgaction"> </view>
<!-- 状态筛选标签 -->
<TabsComponents
:tabslist="statusTabs"
:options="{ label: 'label', value: 'value' }"
@update="switchStatus"
></TabsComponents>
<!-- 类型筛选标签 -->
<view class="type-tabs">
<view
v-for="item in typeTabs"
:key="item.value"
class="type-tab-item"
:class="{ active: activeType === item.value }"
@tap="switchType(item.value)"
>
{{ item.label }}
</view>
</view>
<!-- 上报记录列表 -->
<scroll-view class="record-list" scroll-y lower-threshold="60" @scrolltolower="scrolltolower">
<!-- 空状态 -->
<view v-if="reportRecords.length === 0" class="empty-state">
<uni-icons type="empty" size="60" color="#ccc"></uni-icons>
<text class="empty-text">暂无上报记录</text>
</view>
<!-- 记录卡片 -->
<view
v-for="(record, index) in reportRecords"
:key="index"
class="record-card"
@tap="handleMain(record.id)"
>
<!-- 标题 + 状态标签 -->
<view class="card-header">
<text class="card-title">{{ record.title }}</text>
<view
class="status-tag"
:class="{
pending: record.repairStatus === 0,
processing: record.repairStatus === 1,
completed: record.repairStatus === 2
}"
>
{{ getStatusText(record.repairStatus) }}
</view>
</view>
<!-- 类型标签 -->
<view class="tag-group">
<view class="tag-item">{{ record.reportType }}</view>
<view class="tag-item">{{ record.category }}</view>
</view>
<!-- 内容 -->
<view class="card-content">
{{ record.content }}
</view>
<!-- 工单图片 -->
<view class="card-image" v-if="record.images.length > 0">
<image
lazy-load
v-for="(item, index) in record.images"
:key="index"
:src="item"
class="images"
mode="scaleToFill"
@tap="handlePrivew(record.images)"
></image>
</view>
<!-- 底部信息 -->
<view class="card-footer">
<text class="create-time">{{ record.createTime }}</text>
<text v-if="record.repairStatus === 1" class="repairman"
>维修师傅:{{ record.repairman }}</text
>
<view class="share" v-if="record.repairStatus === 0">
<text>分配</text>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script setup>
import { ref, computed, nextTick } from 'vue'
import { onReady } from '@dcloudio/uni-app'
import TabsComponents from '@/components/tabs/tabs.vue'
import NavBar from '@/components/navbar/navbar.vue'
// 导航栏/标签高度(适配不同设备)
// 筛选状态
const activeStatus = ref('0') // 全部/待分配/待解决/已完成
const activeType = ref('all') // 全部/公共报修/个人报修
// 状态筛选选项
const statusTabs = ref([
{ label: '全部', value: 0 },
{ label: '待分配', value: 1 },
{ label: '待解决', value: 2 },
{ label: '已完成', value: 3 }
])
// 类型筛选选项
const typeTabs = ref([
{ label: '全部', value: 'all' },
{ label: '公共报修', value: 'public' },
{ label: '个人报修', value: 'private' }
])
// 模拟上报记录数据(实际项目替换为接口请求)
const reportRecords = ref([])
const queryParmas = ref({
pageNum: 1,
pageSize: 5
})
const isLoading = ref(false) // 加载锁
const hasMore = ref(true) // 是否还有更多数据
const total = ref(21)
function scrolltolower() {
// 必须加这两个判断!
if (isLoading.value || !hasMore.value) {
return
}
queryParmas.value.pageNum += 1
getlist()
}
function getlist() {
// 1. 如果正在加载或没有更多数据,直接返回
if (isLoading.value || !hasMore.value) return
// 2. 开启锁
isLoading.value = true
uni.showLoading({
title: '加载中',
mask: true
})
const { pageNum, pageSize } = queryParmas.value
const startId = (pageNum - 1) * pageSize + 1
const arr = Array.from({ length: pageSize }).map((_, index) => {
const realId = startId + index // ✅ 修复 ID 错误
// ✅ 随机生成 0、1、2
const randomStatus = Math.floor(Math.random() * 3)
return {
id: realId,
title: '标题巴拉巴拉巴拉' + startId + 1,
status: 'completed',
reportType: '公共报修',
category: '路灯',
content: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
createTime: '2026-08-01 10:05:46',
images: ['https://picsum.photos/93', 'https://picsum.photos/93', 'https://picsum.photos/93'],
repairman: '张三',
repairStatus: randomStatus // 0 待分配,1待解决 2已完成
}
})
// 追加数据(正确方式)
reportRecords.value = [...reportRecords.value, ...arr]
// 判断是否还有更多
hasMore.value = reportRecords.value.length < total.value
isLoading.value = false
uni.hideLoading()
}
getlist()
function resetList() {
reportRecords.value = []
queryParmas.value.pageNum = 1
hasMore.value = true
getlist()
}
// 切换状态(全部/待分配/待解决/已完成)筛选
const switchStatus = value => {
activeStatus.value = value
resetList()
}
// 切换类型(公共/个人)筛选
const switchType = value => {
activeType.value = value
resetList()
}
const statusMap = ['待分配', '待解决', '已完成']
// 获取状态文本
const getStatusText = status => {
return statusMap[status] || ''
}
// 返回上一页
const navBack = () => {
uni.navigateBack()
}
/* 0 待分配,1待解决 2已完成*/
function handleMain(id) {
const find = reportRecords.value.find(item => item.id === id)
if (find) {
const status = find.repairStatus
if (status === 2) {
uni.navigateTo({
url: `/pageSubPack/ticket/repairMain?id=${id}&showbtn=${false}&path=hall`
})
} else {
uni.navigateTo({
url: `/pageSubPack/ticket/repairMain?id=${id}&showbtn=${false}&path=hall`
})
}
}
}
/* 预览图片 */
function handlePrivew(imgs) {
// if (imgs.length > 0) {
// uni.previewImage({
// urls: imgs,
// success(e) {
// console.log(JSON.stringify(e))
// }
// })
// }
}
</script>
<style scoped>
.bgaction {
position: absolute;
right: 0;
top: 20%;
width: 100%;
height: 100%;
z-index: 0;
background: linear-gradient(225deg, #e2f0ff 0%, #f9f9f9 25%);
}
.report-record-page {
height: 100vh;
background: linear-gradient(225deg, #e2f0ff 0%, #f9f9f9 25.71%);
}
.hall_body {
flex: 1;
padding: 0 40rpx;
overflow: hidden;
position: relative;
}
/* 状态筛选标签 */
.status-tabs {
display: flex;
/* background-color: #fff; */
padding: 0 40rpx;
height: 88rpx;
align-items: center;
}
/* 类型筛选标签 */
.type-tabs {
display: flex;
height: 48rpx;
/* background-color: #fff; */
margin: 20rpx 0;
justify-content: flex-start;
gap: 20rpx;
}
.type-tab-item {
width: 142rpx;
height: 48rpx;
border-radius: 24rpx;
border: 2rpx solid #999999;
font-weight: 400;
font-size: 28rpx;
color: #999999;
line-height: 48rpx;
text-align: center;
}
.type-tab-item.active {
color: #4d5dfa;
border: 2rpx solid #4d5dfa;
border-radius: 24rpx;
}
/* 记录列表 */
.record-list {
box-sizing: border-box;
padding-bottom: 40rpx;
overflow-y: auto;
flex: 1;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
color: #ccc;
}
.empty-text {
font-size: 32rpx;
color: #999;
margin-top: 20rpx;
}
/* 记录卡片 */
.record-card {
background-color: #fff;
border-radius: 16rpx;
padding: 34rpx 32rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.card-header {
display: flex;
justify-content: flex-start;
align-items: center;
margin-bottom: 20rpx;
}
.card-title {
margin-right: 20rpx;
font-weight: 450;
font-size: 32rpx;
color: #222222;
line-height: 40rpx;
}
/* 状态标签 */
.status-tag {
width: 82rpx;
height: 30rpx;
border-radius: 15rpx;
font-weight: 400;
font-size: 20rpx;
color: #ffffff;
line-height: 30rpx;
text-align: center;
line-height: 30rpx;
}
.status-tag.pending {
background-color: #e34d59; /* 待分配-红色 */
}
.status-tag.processing {
background-color: #09c2bd; /* 待解决-绿色 */
}
.status-tag.completed {
background-color: #4d5dfa; /* 已完成-蓝色 */
}
/* 类型标签组 */
.tag-group {
display: flex;
gap: 20rpx;
margin-bottom: 20rpx;
}
.tag-item {
width: 112rpx;
height: 42rpx;
border-radius: 8rpx 8rpx 8rpx 8rpx;
border: 1rpx solid #999999;
font-weight: 400;
font-size: 24rpx;
color: #999999;
line-height: 42rpx;
text-align: center;
}
/* 内容 */
.card-content {
margin-bottom: 20rpx;
font-weight: 450;
font-size: 28rpx;
color: #666666;
line-height: 40rpx;
letter-spacing: 2rpx;
}
/* 图片 */
.card-image {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20rpx;
min-width: 186rpx;
min-height: 104rpx;
margin-bottom: 20rpx;
}
.images {
width: 186rpx;
height: 104rpx;
}
/* 底部信息 */
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28rpx;
color: #999;
}
.share {
width: 127rpx;
height: 44rpx;
background: #4d5dfa;
border-radius: 22rpx;
font-weight: 400;
font-size: 28rpx;
color: #ffffff;
line-height: 44rpx;
text-align: center;
}
.repairman {
color: #999;
}
</style>

View File

@@ -0,0 +1,480 @@
<template>
<view class="report-record-page page_padding">
<!-- 顶部导航栏 -->
<navBar @clickLeft="navBack" title="我的工单"></navBar>
<view class="hall_body flex-1 flex flex-col">
<!-- 状态筛选标签 -->
<TabsComponents
:tabslist="statusTabs"
:options="{ label: 'label', value: 'value' }"
@update="switchStatus"
></TabsComponents>
<!-- 类型筛选标签 -->
<view class="type-tabs">
<view
v-for="item in typeTabs"
:key="item.value"
class="type-tab-item"
:class="{ active: activeType === item.value }"
@tap="switchType(item.value)"
>
{{ item.label }}
</view>
</view>
<!-- 上报记录列表 -->
<scroll-view
class="record-list flex-1"
scroll-y
lower-threshold="60"
@scrolltolower="scrolltolower"
>
<!-- 空状态 -->
<view v-if="reportRecords.length === 0" class="empty-state">
<uni-icons type="empty" size="60" color="#ccc"></uni-icons>
<text class="empty-text">暂无上报记录</text>
</view>
<!-- 记录卡片 -->
<view
v-for="(record, index) in reportRecords"
@tap="handleComplted(record.id)"
:key="index"
class="record-card"
>
<!-- 标题 + 状态标签 -->
<view class="card-header">
<text class="card-title">{{ record.title }}</text>
<view class="status-tag" :class="record.status">
{{ getStatusText(record.status) }}
</view>
</view>
<!-- 类型标签 -->
<view class="tag-group">
<view class="tag-item">{{ record.reportType }}</view>
<view class="tag-item">{{ record.category }}</view>
</view>
<!-- 内容 -->
<view class="card-content">
{{ record.content }}
</view>
<!-- 工单图片 -->
<!-- <view class="card-image">
<image
lazy-load
v-for="(item, index) in record.images"
:key="index"
:src="item"
class="images"
mode="scaleToFill"
></image>
</view -->
<!-- 底部信息 -->
<view class="card-footer">
<text class="create-time">{{ record.createTime }}</text>
<text v-if="record.repairStatus === 2" class="repairman"
>维修师傅:{{ record.repairman }}</text
>
<view class="share" v-if="record.repairStatus === 1">
<text>维修完成</text>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import { onReady } from '@dcloudio/uni-app'
import TabsComponents from '@/components/tabs/tabs.vue'
import NavBar from '@/components/navbar/navbar.vue'
// 导航栏/标签高度(适配不同设备)
const navHeight = ref(44)
const statusTabHeight = ref(88)
const typeTabHeight = ref(88)
// 筛选状态
const activeStatus = ref('all') // 全部/待分配/待解决/已完成
const activeType = ref('all') // 全部/公共报修/个人报修
// 状态筛选选项
const statusTabs = ref([
{ label: '全部', value: 'all' },
{ label: '待解决', value: 'processing' },
{ label: '已完成', value: 'completed' }
])
// 类型筛选选项
const typeTabs = ref([
{ label: '全部', value: 'all' },
{ label: '公共报修', value: 'public' },
{ label: '个人报修', value: 'private' }
])
// 模拟上报记录数据(实际项目替换为接口请求)
const reportRecords = ref([
{
id: 1,
title: '标题巴拉巴拉巴拉',
status: 'processing', // pending/processing/completed
reportType: '公共报修',
category: '路灯',
content: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
createTime: '2026-08-01 10:05:46',
repairman: '张胜',
images: [
'https://picsum.photos/93',
'https://picsum.photos/93',
'https://picsum.photos/93',
'https://picsum.photos/93',
'https://picsum.photos/93'
],
repairStatus: 1 // 1待解决 2已完成
}
])
const queryParmas = ref({
pageNum: 1,
pageSize: 10,
value: ''
})
const isLoading = ref(false) // 加载锁
const hasMore = ref(true) // 是否还有更多数据
const total = ref(21)
function scrolltolower() {
// 必须加这两个判断!
if (isLoading.value || !hasMore.value) {
return
}
queryParmas.value.pageNum += 1
getlist()
}
function getlist() {
// 1. 如果正在加载或没有更多数据,直接返回
if (isLoading.value || !hasMore.value) return
// 2. 开启锁
isLoading.value = true
uni.showLoading({
title: '加载中',
mask: true
})
const { pageNum, pageSize } = queryParmas.value
const startId = (pageNum - 1) * pageSize + 1
const arr = Array.from({ length: pageSize }).map((_, index) => {
const realId = startId + index
const randomStatus = Math.floor(Math.random() * 2)
return {
id: realId,
title: '标题巴拉巴拉巴拉',
status: 'processing', // pending/processing/completed
reportType: '公共报修',
category: '路灯',
content: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
createTime: '2026-08-01 10:05:46',
repairman: '',
images: [
'https://picsum.photos/93',
'https://picsum.photos/93',
'https://picsum.photos/93',
'https://picsum.photos/93',
'https://picsum.photos/93'
],
repairStatus: randomStatus // 1待解决 2已完成
}
})
// 追加数据(正确方式)
reportRecords.value = [...reportRecords.value, ...arr]
// 判断是否还有更多
hasMore.value = reportRecords.value.length < total.value
isLoading.value = false
uni.hideLoading()
}
getlist()
function resetList() {
reportRecords.value = []
queryParmas.value.pageNum = 1
hasMore.value = true
getlist()
}
// 切换状态(全部/待分配/待解决/已完成)筛选
const switchStatus = value => {
activeStatus.value = value
}
// 切换类型(公共/个人)筛选
const switchType = value => {
activeType.value = value
}
// 获取状态文本
const statusMap = {
processing: '待解决',
completed: '已完成'
}
const getStatusText = status => {
return statusMap[status] || ''
}
// 返回上一页
const navBack = () => {
uni.navigateBack()
}
// 1待解决 2已完成
function handleComplted(id) {
const find = reportRecords.value.find(item => item.id === id)
if (find) {
const status = find.repairStatus
if (status === 2) {
uni.navigateTo({
url: `/pageSubPack/ticket/repairMain?id=${id}&showbtn=${false}&path=myticket`
})
} else {
uni.navigateTo({
url: `/pageSubPack/ticket/repairMain?id=${id}&showbtn=${true}&path=myticket`
})
}
}
}
/* 预览图片 */
function handlePrivew(imgs) {
// if (imgs.length > 0) {
// uni.previewImage({
// urls: imgs,
// success(e) {
// console.log(JSON.stringify(e))
// }
// })
// }
}
</script>
<style scoped>
.report-record-page {
width: 100%;
height: 100vh;
overflow: hidden;
box-sizing: border-box;
background: linear-gradient(225deg, #e2f0ff 0%, #f9f9f9 50.71%);
}
.hall_body {
padding: 0 40rpx;
overflow: hidden;
}
/* 状态筛选标签 */
.status-tabs {
display: flex;
/* background-color: #fff; */
padding: 0 40rpx;
height: 88rpx;
align-items: center;
}
.status-tab-item {
/* flex: 1; */
display: flex;
justify-content: flex-start;
margin-right: 60rpx;
text-align: left;
font-size: 32rpx;
color: #666;
height: 100%;
line-height: 88rpx;
position: relative;
}
.status-tab-item.active {
color: #4d5dfa;
font-weight: 500;
}
.status-tab-item.active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 80rpx;
height: 8rpx;
background: linear-gradient(to bottom, #4d5dfa, #bac0fa);
border-radius: 6rpx;
}
/* 类型筛选标签 */
.type-tabs {
display: flex;
height: 48rpx;
/* background-color: #fff; */
margin: 20rpx 0;
justify-content: flex-start;
gap: 20rpx;
}
.type-tab-item {
width: 142rpx;
height: 48rpx;
border-radius: 24rpx;
border: 2rpx solid #999999;
font-weight: 400;
font-size: 28rpx;
color: #999999;
line-height: 48rpx;
text-align: center;
}
.type-tab-item.active {
color: #4d5dfa;
border: 2rpx solid #4d5dfa;
border-radius: 24rpx;
}
/* 记录列表 */
.record-list {
box-sizing: border-box;
padding-bottom: 40rpx;
overflow-y: auto;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
color: #ccc;
}
.empty-text {
font-size: 32rpx;
color: #999;
margin-top: 20rpx;
}
/* 记录卡片 */
.record-card {
background-color: #fff;
border-radius: 16rpx;
padding: 34rpx 32rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.card-header {
display: flex;
justify-content: flex-start;
align-items: center;
margin-bottom: 20rpx;
}
.card-title {
margin-right: 20rpx;
font-weight: 450;
font-size: 32rpx;
color: #222222;
line-height: 40rpx;
}
/* 状态标签 */
.status-tag {
width: 82rpx;
height: 30rpx;
border-radius: 15rpx;
font-weight: 400;
font-size: 20rpx;
color: #ffffff;
line-height: 30rpx;
text-align: center;
line-height: 30rpx;
}
.status-tag.pending {
background-color: #e34d59; /* 待分配-红色 */
}
.status-tag.processing {
background-color: #09c2bd; /* 待解决-绿色 */
}
.status-tag.completed {
background-color: #4d5dfa; /* 已完成-蓝色 */
}
/* 类型标签组 */
.tag-group {
display: flex;
gap: 20rpx;
margin-bottom: 20rpx;
}
.tag-item {
width: 112rpx;
height: 42rpx;
border-radius: 8rpx 8rpx 8rpx 8rpx;
border: 1rpx solid #999999;
font-weight: 400;
font-size: 24rpx;
color: #999999;
line-height: 42rpx;
text-align: center;
}
/* 内容 */
.card-content {
margin-bottom: 20rpx;
font-weight: 450;
font-size: 28rpx;
color: #666666;
line-height: 40rpx;
letter-spacing: 2rpx;
}
/* 图片 */
.card-image {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20rpx;
min-width: 186rpx;
min-height: 104rpx;
margin-bottom: 20rpx;
}
.images {
width: 186rpx;
height: 104rpx;
}
/* 底部信息 */
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28rpx;
color: #999;
}
.share {
width: 127rpx;
height: 44rpx;
background: #4d5dfa;
border-radius: 22rpx;
font-weight: 400;
font-size: 24rpx;
color: #ffffff;
line-height: 44rpx;
text-align: center;
}
.repairman {
color: #999;
}
</style>

View File

@@ -0,0 +1,121 @@
<template>
<view class="page_padding">
<view class="repairCompleted">
<view class="item">
<view class="title">
<text>问题描述</text>
</view>
<view class="descrition">
<textarea value="" placeholder-style="color:#e8e8e8;" placeholder="请描述您的问题" />
</view>
</view>
<view class="item">
<view class="title">
<text>问题照片</text>
</view>
<view class="">
<uni-file-picker v-model="form.images" file-mediatype="image">
<view class="addimages images flex flex-center">
<uni-icons type="plusempty" color="#a6a6a6" size="27"></uni-icons>
</view>
</uni-file-picker>
</view>
</view>
<view class="action_btn" @tap="handleSelecthouse">
<text>确 认</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
const form = ref({
descrition: '',
images: [
{
name: 'file1.png',
extname: 'png',
url: 'https://picsum.photos/80'
},
{
name: 'file2.png',
extname: 'png',
url: 'https://picsum.photos/80'
},
{
name: 'file3.png',
extname: 'png',
url: 'https://picsum.photos/80'
},
{
name: 'file4.png',
extname: 'png',
url: 'https://picsum.photos/80'
}
]
})
/** 返回上一页 */
function handleSelecthouse() {
console.log('compltee')
uni.navigateBack()
}
</script>
<style lang="scss" scoped>
.repairCompleted {
padding: 20rpx 40rpx;
background-color: #ffffff;
position: relative;
flex: 1;
.item {
margin-bottom: 28rpx;
.title {
padding-left: 16rpx;
margin-bottom: 30rpx;
font-weight: 400;
font-size: 28rpx;
color: #333333;
line-height: 36rpx;
}
.descrition {
height: 200rpx;
border-radius: 8rpx 8rpx 8rpx 8rpx;
border: 2rpx solid #ececec;
padding: 22rpx 18rpx;
}
.imageboxs {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20rpx;
.images {
width: 160rpx;
height: 160rpx;
border-radius: 0rpx;
}
}
:deep(.file-picker__box-content.is-add) {
background-color: #f3f3f3 !important;
}
}
.action_btn {
width: 670rpx;
height: 80rpx;
font-weight: 400;
font-size: 28rpx;
color: #ffffff;
line-height: 80rpx;
text-align: center;
background: #4d5dfa;
border-radius: 8rpx 8rpx 8rpx 8rpx;
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
}
</style>

View File

@@ -0,0 +1,346 @@
<template>
<view class="page_padding">
<view class="repairMain h-full">
<view class="stop">
<Stops :steps="stepList" :current="currentStep" color="#4d5dfa">
<template #item="{ index, status, data }">
<view
class="items"
:class="{
active: currentStep >= index,
nobottom: index >= stepList.length - 1
}"
>
<view class="title">
<text>{{ data.title }}</text>
</view>
<view class="flex justify-between">
<span class="subtitle">{{ data.desc }}</span>
<span class="subtime" v-if="currentStep > index">2026/08/01 10:30:35</span>
</view>
</view>
</template>
</Stops>
</view>
<view class="info">
<view class="info_title">
<text>保修信息</text>
</view>
<view class="info_body">
<view class="item">
<view class="label"> <text>报修房屋</text> </view>
<view class="value">
<text>{{ repairInfo.houseName }}</text>
</view>
</view>
<view class="item">
<view class="label"> <text>维修项目</text> </view>
<view class="value">
<text>{{ repairInfo.repairProject }} </text>
</view>
</view>
<view class="item">
<view class="label"> <text>标题</text> </view>
<view class="value">
<text> {{ repairInfo.title }}</text>
</view>
</view>
<view class="item descrition">
<view class="label"> <text>问题描述</text> </view>
<view class="value">
<text>{{ repairInfo.descrtion }}</text>
</view>
</view>
<view class="item">
<view class="label"> <text>手机号</text> </view>
<view class="value">
<text> {{ repairInfo.phone }} </text>
</view>
</view>
<view class="item">
<view class="label"> <text>报修时间</text> </view>
<view class="value">
<text> {{ repairInfo.time }} </text>
</view>
</view>
<view class="item photobox">
<view class="label"> <text>问题照片</text> </view>
<view class="value">
<image
v-for="(item, index) in repairInfo.photo"
:key="index"
class="images"
:src="item"
mode=""
></image>
</view>
</view>
</view>
</view>
<view class="info" v-if="(currentStep >= 2 && prevPath === 'hall') || currentStep > 2">
<view class="info_title">
<text>维修信息</text>
</view>
<view class="info_body">
<view class="item">
<view class="label"> <text>姓名</text> </view>
<view class="value"> {{ repairInfo.repairUser.name }} </view>
</view>
<view class="item">
<view class="label"> <text>状态</text> </view>
<view class="value"> {{ repairInfo.repairUser.status }} </view>
</view>
<view class="item">
<view class="label"> <text>角色</text> </view>
<view class="value"> {{ repairInfo.repairUser.role }} </view>
</view>
<view class="item">
<view class="label"> <text>手机号</text> </view>
<view class="value"> {{ repairInfo.repairUser.phone }} </view>
</view>
<view class="" v-if="currentStep >= 3">
<view class="item descrition flex-col">
<view class="label"> <text>问题描述</text> </view>
<view class="value"> {{ repairInfo.repairUser.descrtion }} </view>
</view>
<view class="item photobox">
<view class="label"> <text>问题照片</text> </view>
<view class="value">
<image
v-for="(item, index) in repairInfo.repairUser.photo"
:key="index"
class="images"
:src="item"
mode=""
></image>
</view>
</view>
</view>
</view>
</view>
<view class="action_btn" @tap="handleSelecthouse" v-if="currentStep === 1">
<text>分配</text>
</view>
<view class="action_btn" @tap="handleRepairCompluted" v-if="currentStep === 2 && Showbtn">
<text>维修完成</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import Stops from '@/components/stops/stops.vue'
// --------------------------------------
/** 维修步骤:0提交 1分配 2维修完成 / 待解决 3记录完成 完成*/
const currentStep = ref(2)
// 步骤数据
const stepList = ref([
{ title: '提交维修信息', desc: '提交报修成功', time: '2026/08/01 10:30:26' },
{ title: '待分配', desc: '等待物业派单中', time: '2026/08/01 10:32:21' },
{ title: '维修/待解决', desc: '维修人员进行维修', time: '2026/08/01 10:38:07' },
{ title: '维修完成', desc: '填写维修记录', time: '2026/08/01 10:40:33' }
])
// --------------------------------------
const queryParams = ref({
id: null
})
const Showbtn = ref(false)
const prevPath = ref('')
const repairInfo = ref({
houseName: '桂花园(别墅)2号楼2层',
repairProject: '照明-路灯',
title: '1栋楼下道路灯不亮了',
descrtion: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
phone: '15688888888',
time: '2023-08-01 15:30',
photo: [
'https://picsum.photos/80',
'https://picsum.photos/80',
'https://picsum.photos/80',
'https://picsum.photos/80'
],
repairUser: {
name: '盘下但',
status: '正常',
role: '维修人员',
phone: '15688888888',
descrtion: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
photo: [
'https://picsum.photos/80',
'https://picsum.photos/80',
'https://picsum.photos/80',
'https://picsum.photos/80'
]
}
})
onLoad(options => {
queryParams.value.id = options.id
Showbtn.value = JSON.parse(options.showbtn)
prevPath.value = options.path
})
// --------------------------------------
/** 选择维修人员 */
function handleSelecthouse() {
uni.navigateTo({
url: '/pageSubPack/ticket/shareUser',
events: {
backData: val => {
currentStep.value += 1
//api;
}
}
})
}
/** 跳转到维修完成 */
function handleRepairCompluted() {
uni.navigateTo({
url: '/pageSubPack/ticket/repairCompleted'
})
}
</script>
<style lang="scss" scoped>
.repairMain {
padding: 20rpx 40rpx;
background-color: #f9f9f9;
box-sizing: border-box;
overflow-x: hidden;
overflow-y: auto;
flex: 1;
position: relative;
.stop {
padding: 30rpx 40rpx;
background-color: #fff;
border-radius: 16rpx;
&:deep(.items) {
height: 120rpx;
color: #999999;
margin-bottom: 50rpx;
&.nobottom {
margin-bottom: 0;
}
&.active {
color: #4d5dfa;
}
.title {
margin-bottom: 12rpx;
font-weight: 550;
font-size: 28rpx;
line-height: 44rpx;
}
.subtitle {
font-weight: 400;
font-size: 24rpx;
line-height: 40rpx;
}
.subtime {
font-weight: 400;
font-size: 24rpx;
color: #999999;
line-height: 40rpx;
}
}
}
// 报修信息
.info {
margin-top: 20rpx;
padding: 0 42rpx;
padding-bottom: 34rpx;
background-color: #fff;
border-radius: 16rpx;
box-sizing: border-box;
.info_title {
height: 88rpx;
box-sizing: border-box;
font-weight: 550;
font-size: 32rpx;
color: #222222;
line-height: 88rpx;
border-bottom: 2rpx solid rgba(0, 0, 0, 0.1);
margin-bottom: 30rpx;
}
.item {
display: flex;
justify-content: space-between;
margin-bottom: 46rpx;
&.descrition {
flex-direction: column;
margin-bottom: 30rpx;
.label {
margin-bottom: 32rpx;
}
.value {
height: 162rpx;
border-radius: 8rpx;
border: 2rpx solid rgba(0, 0, 0, 0.1);
box-sizing: border-box;
padding: 24rpx 30rpx 26rpx 28rpx;
font-weight: 400;
font-size: 28rpx;
color: #666666;
line-height: 36rpx;
}
}
&.photobox {
flex-direction: column;
.label {
margin-bottom: 30rpx;
}
.value {
display: flex;
align-items: center;
flex-wrap: wrap;
text-align: left;
.images {
margin: 10rpx;
width: 160rpx;
height: 160rpx;
}
}
}
.label {
font-weight: 400;
font-size: 28rpx;
color: #222222;
line-height: 36rpx;
width: 45%;
}
.value {
flex: 1;
font-weight: 400;
font-size: 28rpx;
color: #666666;
line-height: 36rpx;
text-wrap: wrap;
}
}
}
.action_btn {
width: 670rpx;
height: 80rpx;
font-weight: 400;
font-size: 28rpx;
color: #ffffff;
line-height: 80rpx;
text-align: center;
margin-top: 20rpx;
background: #4d5dfa;
border-radius: 8rpx 8rpx 8rpx 8rpx;
}
}
</style>

View File

@@ -0,0 +1,415 @@
<template>
<view class="report-record-page page_padding">
<!-- 顶部导航栏 -->
<navBar @clickLeft="navBack" title="上报记录"></navBar>
<view class="hall_body flex-1 flex flex-col">
<!-- 状态筛选标签 -->
<TabsComponents
:tabslist="statusTabs"
:options="{ label: 'label', value: 'value' }"
@update="switchStatus"
></TabsComponents>
<!-- 类型筛选标签 -->
<view class="type-tabs">
<view
v-for="item in typeTabs"
:key="item.value"
class="type-tab-item"
:class="{ active: activeType === item.value }"
@tap="switchType(item.value)"
>
{{ item.label }}
</view>
</view>
<!-- 上报记录列表 -->
<scroll-view
class="record-list flex-1"
scroll-y
:style="{ height: `calc(100vh - ${navHeight + statusTabHeight + typeTabHeight}px)` }"
>
<!-- 空状态 -->
<view v-if="filteredRecords.length === 0" class="empty-state">
<uni-icons type="empty" size="60" color="#ccc"></uni-icons>
<text class="empty-text">暂无上报记录</text>
</view>
<!-- 记录卡片 -->
<view v-for="record in filteredRecords" :key="record.id" class="record-card">
<!-- 标题 + 状态标签 -->
<view class="card-header">
<text class="card-title">{{ record.title }}</text>
<view class="status-tag" :class="record.status">
{{ getStatusText(record.status) }}
</view>
</view>
<!-- 类型标签 -->
<view class="tag-group">
<view class="tag-item">{{ record.reportType }}</view>
<view class="tag-item">{{ record.category }}</view>
</view>
<!-- 内容 -->
<view class="card-content">
{{ record.content }}
</view>
<!-- 底部信息 -->
<view class="card-footer">
<text class="create-time">{{ record.createTime }}</text>
<text v-if="record.repairStatus !== 0" class="repairman"
>维修师傅:{{ record.repairman }}</text
>
<!-- <view class="share" v-if="record.repairStatus === 0">
<text>分配</text>
</view> -->
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import { onReady } from '@dcloudio/uni-app'
import TabsComponents from '@/components/tabs/tabs.vue'
import NavBar from '@/components/navbar/navbar.vue'
// 导航栏/标签高度(适配不同设备)
const navHeight = ref(44)
const statusTabHeight = ref(88)
const typeTabHeight = ref(88)
// 筛选状态
const activeStatus = ref('all') // 全部/待分配/待解决/已完成
const activeType = ref('all') // 全部/公共报修/个人报修
// 状态筛选选项
const statusTabs = ref([
{ label: '全部', value: 'all' },
{ label: '待分配', value: 'pending' },
{ label: '待解决', value: 'processing' },
{ label: '已完成', value: 'completed' }
])
// 类型筛选选项
const typeTabs = ref([
{ label: '全部', value: 'all' },
{ label: '公共报修', value: 'public' },
{ label: '个人报修', value: 'private' }
])
// 模拟上报记录数据(实际项目替换为接口请求)
const reportRecords = ref([
{
id: 1,
title: '标题巴拉巴拉巴拉',
status: 'pending', // pending/processing/completed
reportType: '公共报修',
category: '路灯',
content: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
createTime: '2026-08-01 10:05:46',
repairman: '',
repairStatus: 0 // 0 待分配,1待解决 2已完成
},
{
id: 2,
title: '标题巴拉巴拉巴拉',
status: 'processing',
reportType: '公共报修',
category: '路灯',
content: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
createTime: '2026-08-01 10:05:46',
repairman: '张三',
repairStatus: 1 // 0 待分配,1待解决 2已完成
},
{
id: 3,
title: '标题巴拉巴拉巴拉',
status: 'completed',
reportType: '公共报修',
category: '路灯',
content: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。',
createTime: '2026-08-01 10:05:46',
repairman: '张三',
repairStatus: 2 // 0 待分配,1待解决 2已完成
}
])
// 筛选后的记录
const filteredRecords = computed(() => {
return reportRecords.value.filter(record => {
// 状态筛选
const statusMatch = activeStatus.value === 'all' || record.status === activeStatus.value
// 类型筛选
const typeMatch =
activeType.value === 'all' ||
record.reportType === typeTabs.value.find(item => item.value === activeType.value)?.label
return statusMatch && typeMatch
})
})
// 切换状态(全部/待分配/待解决/已完成)筛选
const switchStatus = value => {
activeStatus.value = value
}
// 切换类型(公共/个人)筛选
const switchType = value => {
activeType.value = value
}
// 获取状态文本
const getStatusText = status => {
const statusMap = {
pending: '待分配',
processing: '待解决',
completed: '已完成'
}
return statusMap[status] || ''
}
// 页面渲染完成后适配高度
onReady(() => {
// 可根据实际设备调整高度
uni.getDeviceInfo({
success: res => {
if (res.platform === 'ios') {
navHeight.value = 44
} else {
navHeight.value = 48
}
}
})
})
// 返回上一页
const navBack = () => {
uni.switchTab({
url: '/pages/my/my'
})
}
/* 0 待分配,1待解决 2已完成*/
function handleMain(id) {
const find = reportRecords.value.find(item => item.id === id)
if (find) {
const status = find.repairStatus
if (status === 2) {
uni.navigateTo({
url: `/pageSubPack/ticket/repairMain?id=${id}&showbtn=${false}&path=hall`
})
} else {
uni.navigateTo({
url: `/pageSubPack/ticket/repairMain?id=${id}&showbtn=${false}&path=hall`
})
}
}
}
/* 预览图片 */
function handlePrivew(imgs) {
// if (imgs.length > 0) {
// uni.previewImage({
// urls: imgs,
// success(e) {
// console.log(JSON.stringify(e))
// }
// })
// }
}
</script>
<style scoped>
.report-record-page {
width: 100%;
min-height: 100vh;
max-height: 100vh;
overflow: hidden;
box-sizing: border-box;
background: linear-gradient(225deg, #e2f0ff 0%, #f9f9f9 25.71%);
}
.hall_body {
padding: 0 40rpx;
overflow: hidden;
position: relative;
}
/* 状态筛选标签 */
.status-tabs {
display: flex;
/* background-color: #fff; */
padding: 0 40rpx;
height: 88rpx;
align-items: center;
}
/* 类型筛选标签 */
.type-tabs {
display: flex;
height: 48rpx;
/* background-color: #fff; */
margin: 20rpx 0;
justify-content: flex-start;
gap: 20rpx;
}
.type-tab-item {
width: 142rpx;
height: 48rpx;
border-radius: 24rpx;
border: 2rpx solid #999999;
font-weight: 400;
font-size: 28rpx;
color: #999999;
line-height: 48rpx;
text-align: center;
}
.type-tab-item.active {
color: #4d5dfa;
border: 2rpx solid #4d5dfa;
border-radius: 24rpx;
}
/* 记录列表 */
.record-list {
box-sizing: border-box;
padding-bottom: 40rpx;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
color: #ccc;
}
.empty-text {
font-size: 32rpx;
color: #999;
margin-top: 20rpx;
}
/* 记录卡片 */
.record-card {
background-color: #fff;
border-radius: 16rpx;
padding: 34rpx 32rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.card-header {
display: flex;
justify-content: flex-start;
align-items: center;
margin-bottom: 20rpx;
}
.card-title {
margin-right: 20rpx;
font-weight: 450;
font-size: 32rpx;
color: #222222;
line-height: 40rpx;
}
/* 状态标签 */
.status-tag {
width: 82rpx;
height: 30rpx;
border-radius: 15rpx;
font-weight: 400;
font-size: 20rpx;
color: #ffffff;
line-height: 30rpx;
text-align: center;
line-height: 30rpx;
}
.status-tag.pending {
background-color: #e34d59; /* 待分配-红色 */
}
.status-tag.processing {
background-color: #09c2bd; /* 待解决-绿色 */
}
.status-tag.completed {
background-color: #4d5dfa; /* 已完成-蓝色 */
}
/* 类型标签组 */
.tag-group {
display: flex;
gap: 20rpx;
margin-bottom: 20rpx;
}
.tag-item {
width: 112rpx;
height: 42rpx;
border-radius: 8rpx 8rpx 8rpx 8rpx;
border: 1rpx solid #999999;
font-weight: 400;
font-size: 24rpx;
color: #999999;
line-height: 42rpx;
text-align: center;
}
/* 内容 */
.card-content {
margin-bottom: 20rpx;
font-weight: 450;
font-size: 28rpx;
color: #666666;
line-height: 40rpx;
letter-spacing: 2rpx;
}
/* 图片 */
.card-image {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20rpx;
min-width: 186rpx;
min-height: 104rpx;
margin-bottom: 20rpx;
}
.images {
width: 186rpx;
height: 104rpx;
}
/* 底部信息 */
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28rpx;
color: #999;
}
.share {
width: 127rpx;
height: 44rpx;
background: #4d5dfa;
border-radius: 22rpx;
font-weight: 400;
font-size: 28rpx;
color: #ffffff;
line-height: 44rpx;
text-align: center;
}
.repairman {
color: #999;
}
</style>

View File

@@ -0,0 +1,250 @@
<template>
<view class="page_padding shareUser">
<view class="shareBody flex flex-col flex-1 overflow-hidden">
<!-- 搜索 -->
<view class="search_box">
<view
@click="handleClick"
class="search_view flex-center"
v-if="!queryParmas.value.trim() && Searching"
>
<uni-icons type="search" size="34rpx" color="rgba(0,0,0,0.26)"></uni-icons>
<text>请输入关键字进行搜索</text>
</view>
<uni-easyinput
@blur="handleInput"
@change="handleSearch"
:focus="true"
ref="inputRef"
v-model="queryParmas.value"
v-else
/>
</view>
<!-- 已经选择人数 -->
<view class="checkoutedUser_box">
<text>已选({{ checkoutUserid ? 1 : 0 }}/1)</text>
<text class="checkoutUseName" v-if="checkoutInfo">{{ checkoutInfo.name }}</text>
</view>
<!-- srcoll -->
<scroll-view
scroll-y="true"
class="flex-1 overflow-auto"
lower-threshold="60"
@scrolltolower="scrolltolower"
>
<radio-group @change="radioChange">
<label v-for="(item, index) in searchlist" :key="index">
<view
class="item flex items-center justify-between"
:class="{
active: checkoutUserid === item.id
}"
>
<view class="">
<view class="info">
<text>姓名:</text>
<text>{{ item.name }}</text>
</view>
<view class="info">
<text>角色:</text>
<text>{{ item.roleName }}</text>
</view>
<view class="info">
<text>手机号码:</text>
<text>{{ item.phone }}</text>
</view>
</view>
<radio color="#4d5dfa" :value="item.id" />
</view>
</label>
</radio-group>
</scroll-view>
<!-- submit -->
<view class="action_btn" @tap="handleSelecthouse">
<text>确定派单</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, getCurrentInstance } from 'vue'
import navBar from '@/components/navbar/navbar.vue'
const Searching = ref(true)
/** 点击时切换到input,并聚焦 */
function handleClick() {
Searching.value = false
}
/** 判断 失去焦点时 切换回 view */
function handleInput() {
if (queryParmas.value.value.trim()) {
Searching.value = true
}
}
function handleSearch() {}
// -----------------------------------------
/* 选择列表 */
const searchlist = ref([])
const queryParmas = ref({
pageNum: 1,
pageSize: 10,
value: ''
})
const isLoading = ref(false) // 加载锁
const hasMore = ref(true) // 是否还有更多数据
const total = ref(21)
function scrolltolower() {
// 必须加这两个判断!
if (isLoading.value || !hasMore.value) {
return
}
queryParmas.value.pageNum += 1
getlist()
}
function getlist() {
// 1. 如果正在加载或没有更多数据,直接返回
if (isLoading.value || !hasMore.value) return
// 2. 开启锁
isLoading.value = true
uni.showLoading({
title: '加载中',
mask: true
})
const { pageNum, pageSize } = queryParmas.value
const startId = (pageNum - 1) * pageSize + 1
const arr = Array.from({ length: pageSize }).map((_, index) => {
const realId = startId + index
const randomStatus = Math.floor(Math.random() * 3)
return {
name: '张三' + realId,
roleName: '维修人员',
phone: '19578012123',
id: realId
}
})
// 追加数据(正确方式)
searchlist.value = [...searchlist.value, ...arr]
// 判断是否还有更多
hasMore.value = searchlist.value.length < total.value
isLoading.value = false
uni.hideLoading()
}
getlist()
function resetList() {
reportRecords.value = []
queryParmas.value.pageNum = 1
hasMore.value = true
getlist()
}
//searchlist----------------------
const checkoutUserid = ref(null)
const checkoutInfo = ref(null)
function radioChange(val) {
checkoutUserid.value = Number(val.detail.value)
const find = searchlist.value.find(item => item.id === Number(val.detail.value))
if (find) {
checkoutInfo.value = find
}
}
// ===========================================
let eventChannel = null
// 必须在 onMounted 里获取
onMounted(() => {
const instance = getCurrentInstance().proxy
if (instance && instance.getOpenerEventChannel) {
eventChannel = instance.getOpenerEventChannel()
}
})
/* 确定派单 */
function handleSelecthouse() {
// api
eventChannel.emit('backData', {
checkoutUserid: checkoutUserid.value
})
// 3. 返回上一页
uni.navigateBack()
}
// ===========================================
</script>
<style scoped lang="scss">
.shareUser {
background-color: #f9f9f9;
height: 100vh;
.shareBody {
padding: 0 40rpx;
.search_box {
height: 80rpx;
.search_view {
height: 80rpx;
line-height: 80rpx;
background: #f3f3f3;
border-radius: 8rpx;
font-weight: 400;
font-size: 32rpx;
color: rgba(0, 0, 0, 0.26);
line-height: 48rpx;
text-align: center;
}
}
// 选择人数
.checkoutedUser_box {
margin-top: 20rpx;
margin-bottom: 40rpx;
font-weight: 400;
font-size: 28rpx;
color: #333333;
line-height: 36rpx;
height: 36rpx;
.checkoutUseName {
margin-left: 20rpx;
}
}
// 角色列表
.item {
height: 170rpx;
background: #ffffff;
border: 1rpx solid transparent;
margin-bottom: 20rpx;
padding-left: 26rpx;
padding-right: 32rpx;
.info {
font-weight: 400;
font-size: 28rpx;
color: #333333;
line-height: 36rpx;
height: 36rpx;
margin-bottom: 10rpx;
}
&:deep(radio) {
transform: scale(0.68);
}
&.active {
background: #f5faff;
border: 1rpx solid #4d5dfa;
}
}
// 确定派单
.action_btn {
width: 670rpx;
height: 80rpx;
font-weight: 400;
font-size: 28rpx;
color: #ffffff;
line-height: 80rpx;
text-align: center;
margin-top: 20rpx;
background: #4d5dfa;
border-radius: 8rpx 8rpx 8rpx 8rpx;
}
}
}
</style>

View File

@@ -98,7 +98,8 @@
{ {
"path": "inspection_record", "path": "inspection_record",
"style": { "style": {
"navigationBarTitleText": "巡检记录" "navigationBarTitleText": "巡检记录",
"navigationStyle": "custom"
} }
}, },
{ {
@@ -131,6 +132,79 @@
} }
] ]
}, },
{
"root": "pageSubPack/complaint",
"pages": [{
"path": "Details",
"style": {
"navigationBarTitleText": "投诉详情",
"navigationBarBackgroundColor": "#f9f9f9"
}
},
{
"path": "handle",
"style": {
"navigationBarTitleText": "处理投诉"
}
},
{
"path": "handleUser",
"style": {
"navigationBarTitleText": "选择处理人员"
}
},
{
"path": "Details_handle",
"style": {
"navigationBarTitleText": "投诉详情处理"
}
}
]
},
{
"root": "pageSubPack/ticket",
"pages": [{
"path": "hall",
"style": {
"navigationBarTitleText": "工单大厅",
"navigationStyle": "custom"
}
},
{
"path": "shareUser",
"style": {
"navigationBarTitleText": "分配派单"
}
},
{
"path": "repairMain",
"style": {
"navigationBarTitleText": "维修详情"
}
},
{
"path": "myticket",
"style": {
"navigationBarTitleText": "我的工单",
"navigationStyle": "custom"
}
},
{
"path": "repairCompleted",
"style": {
"navigationBarTitleText": "维修完成",
"navigationBarBackgroundColor": "#fff"
}
},
{
"path": "reportingRecord",
"style": {
"navigationBarTitleText": "上报记录",
"navigationStyle": "custom"
}
}
]
},
{ {
"root": "pageSubPack/oneClickOpen", "root": "pageSubPack/oneClickOpen",
"pages": [{ "pages": [{
@@ -199,8 +273,8 @@
}, },
"condition": { "condition": {
"list": [{ "list": [{
"name": "选择房屋", "name": "维修完成",
"path": "pageSubPack/instrument/checkouthouse" "path": "pageSubPack/ticket/repairCompleted"
}] }]
}, },
"tabBar": { "tabBar": {

View File

@@ -29,11 +29,17 @@
<!-- banner --> <!-- banner -->
<view class="bannarClass"> <view class="bannarClass">
<uni-swiper-dot class="uni-swiper-dot-box" @clickItem="clickItem" :info="bannerList" :current="current" mode="round"> <uni-swiper-dot
class="uni-swiper-dot-box"
@clickItem="clickItem"
:info="bannerList"
:current="current"
mode="round"
>
<swiper class="swiper-box" :current="current" autoplay> <swiper class="swiper-box" :current="current" autoplay>
<swiper-item v-for="(item, index) in bannerList" :key="index"> <swiper-item v-for="(item, index) in bannerList" :key="index">
<view class="swiper-item"> <view class="swiper-item">
<image :src="item.img" style="width: 100%;height: 100%;"></image> <image :src="item.img" style="width: 100%; height: 100%"></image>
</view> </view>
</swiper-item> </swiper-item>
</swiper> </swiper>
@@ -50,8 +56,17 @@
</view> </view>
<!-- 右侧滚动公告 --> <!-- 右侧滚动公告 -->
<view class="notice-right" @click="getNoticeMore"> <view class="notice-right" @click="getNoticeMore">
<uni-notice-bar :text="noticeText" type="line" :showIcon="false" show-get-more scrollable color="#fff" <uni-notice-bar
background-color="transparent" moreColor="#fff" no-border> :text="noticeText"
type="line"
:showIcon="false"
show-get-more
scrollable
color="#fff"
background-color="transparent"
moreColor="#fff"
no-border
>
</uni-notice-bar> </uni-notice-bar>
</view> </view>
</view> </view>
@@ -60,15 +75,15 @@
<view class="statistics-wrap"> <view class="statistics-wrap">
<view class="statistics-item" @click="goToInspectionRecord"> <view class="statistics-item" @click="goToInspectionRecord">
<text class="num">25</text> <text class="num">25</text>
<text class="label">巡检点</text> <text class="label">全部巡检</text>
</view> </view>
<view class="statistics-item" @click="goToDaysInspection"> <view class="statistics-item" @click="goToDaysInspection">
<text class="num">5</text> <text class="num">5</text>
<text class="label">巡检点</text> <text class="label">待巡检</text>
</view> </view>
<view class="statistics-item"> <view class="statistics-item">
<text class="num">25</text> <text class="num">25</text>
<text class="label">巡检点</text> <text class="label">已巡检</text>
</view> </view>
</view> </view>
</view> </view>
@@ -85,18 +100,18 @@
<image class="item-icon" src="/static/imgs/open.png" mode="aspectFit"></image> <image class="item-icon" src="/static/imgs/open.png" mode="aspectFit"></image>
<text class="item-text">一键开门</text> <text class="item-text">一键开门</text>
</view> </view>
<view class="function-item"> <view class="function-item" @tap="goToTicketHall">
<image class="item-icon" src="/static/imgs/work-order.png" mode="aspectFit"></image> <image class="item-icon" src="/static/imgs/work-order.png" mode="aspectFit"></image>
<text class="item-text">工单大厅</text> <text class="item-text">工单大厅</text>
</view> </view>
<view class="function-item"> <view class="function-item" @tap="goToMyTicket">
<image class="item-icon" src="/static/imgs/my-work.png" mode="aspectFit"></image> <image class="item-icon" src="/static/imgs/my-work.png" mode="aspectFit"></image>
<text class="item-text">我的工单</text> <text class="item-text">我的工单</text>
</view> </view>
</view> </view>
<!-- 第二行 --> <!-- 第二行 -->
<view class="function-row" style="margin-top: 2px"> <view class="function-row" style="margin-top: 2px">
<view class="function-item"> <view class="function-item" @tap="goToComplaint">
<image class="item-icon" src="/static/imgs/complaint.png" mode="aspectFit"></image> <image class="item-icon" src="/static/imgs/complaint.png" mode="aspectFit"></image>
<text class="item-text">投诉处理</text> <text class="item-text">投诉处理</text>
</view> </view>
@@ -112,9 +127,7 @@ import { onLoad, onUnload } from '@dcloudio/uni-app'
// 列表数据状态 // 列表数据状态
const current = ref(0) const current = ref(0)
const currentCommunity = ref('碧桂园小区') const currentCommunity = ref('碧桂园小区')
const bannerList = ref([ const bannerList = ref([{ img: '/static/imgs/banner1.png' }])
{ img: '/static/imgs/banner1.png' },
])
const noticeText = ref('公告公告本小区今天安装了摄像头') const noticeText = ref('公告公告本小区今天安装了摄像头')
// =======方法======= // =======方法=======
@@ -136,6 +149,18 @@ const goToOneOpenDoor = () => {
url: '/pageSubPack/oneClickOpen/oneClickOpen' url: '/pageSubPack/oneClickOpen/oneClickOpen'
}) })
} }
// 工单大厅
const goToTicketHall = () => {
uni.navigateTo({
url: '/pageSubPack/ticket/hall'
})
}
// 我的工单
const goToMyTicket = () => {
uni.navigateTo({
url: '/pageSubPack/ticket/myticket'
})
}
// 跳转到 今日巡检 // 跳转到 今日巡检
const goToDaysInspection = () => { const goToDaysInspection = () => {
uni.navigateTo({ uni.navigateTo({
@@ -148,14 +173,20 @@ const goToInspectionRecord = () => {
url: '/pageSubPack/inspection/inspection_record' url: '/pageSubPack/inspection/inspection_record'
}) })
} }
// 跳转到 投诉处理
const goToComplaint = () => {
uni.navigateTo({
url: '/pageSubPack/complaint/Details'
})
}
// 故障上报 // 故障上报
const goToReport = () => { const goToReport = () => {
uni.navigateTo({ uni.navigateTo({
url: "/pageSubPack/fault-reporting/fault-reporting" url: '/pageSubPack/fault-reporting/fault-reporting'
}) })
} }
// 接收选择小区返回的参数 // 接收选择小区返回的参数
const slelctHomeData = (data) => { const slelctHomeData = data => {
currentCommunity.value = data.name currentCommunity.value = data.name
} }
@@ -167,7 +198,7 @@ onUnload(() => {
uni.$off('confirm', slelctHomeData) uni.$off('confirm', slelctHomeData)
}) })
const clickItem = (index) => { const clickItem = index => {
current.value = index current.value = index
} }
</script> </script>
@@ -175,9 +206,8 @@ const clickItem = (index) => {
<style lang="scss" scoped> <style lang="scss" scoped>
.content-class { .content-class {
background-color: #f9f9f9; background-color: #f9f9f9;
padding-bottom: 100rpx;
box-sizing: border-box; box-sizing: border-box;
min-height: 110vh; min-height: 100vh;
overflow-y: auto; overflow-y: auto;
.nar-top { .nar-top {

View File

@@ -1,18 +1,19 @@
<template> <template>
<view class="contentStyle" style=""> <view class="contentStyle" style="">
<view class="status-bar"></view> <view class="status-bar"></view>
<uni-nav-bar title="我的" backgroundColor="transparent" :border="false"> <uni-nav-bar title="我的" backgroundColor="transparent" :border="false"> </uni-nav-bar>
</uni-nav-bar>
<scroll-view class="page" scroll-y> <scroll-view class="page" scroll-y>
<view class="topBox"> <view class="topBox">
<view class="name"> <view class="name">
<image src="/static/imgs/touxiang.png" class="img" /> <image src="/static/imgs/touxiang.png" class="img" />
<span style="padding: 5rpx 5rpx;display: flex;flex-direction: column;line-height: 60rpx;"> <span
<span style="font-size: 36rpx;font-weight: 600;"> style="padding: 5rpx 5rpx; display: flex; flex-direction: column; line-height: 60rpx"
{{loginName}} >
<span style="font-size: 36rpx; font-weight: 600">
{{ loginName }}
</span> </span>
<span style="font-size: 26rpx;color:#666666;"> <span style="font-size: 26rpx; color: #666666">
{{phoneNum}} {{ phoneNum }}
</span> </span>
</span> </span>
</view> </view>
@@ -20,91 +21,100 @@
<view class="contentBox"> <view class="contentBox">
<div class="page-container"> <div class="page-container">
<view class="list-container"> <view class="list-container">
<view class="list-item" v-for="(item, index) in list" :key="index" @click="onItemClick(item)"> <view
class="list-item"
v-for="(item, index) in list"
:key="index"
@click="onItemClick(item)"
>
<image :src="item.icon" class="item-icon" /> <image :src="item.icon" class="item-icon" />
<view class="item-content"> <view class="item-content">
<text class="item-title">{{ item.title }}</text> <text class="item-title">{{ item.title }}</text>
<span class="item-desc" v-if="item.desc"><span <span class="item-desc" v-if="item.desc"
style="align-self: flex-end;font-size: 24rpx;vertical-align: baseline;">{{ item.desc }}</span></span> ><span style="align-self: flex-end; font-size: 24rpx; vertical-align: baseline">{{
item.desc
}}</span></span
>
</view> </view>
<view class="item-arrow"></view> <view class="item-arrow"></view>
<!-- <image src="http://47.104.199.163:9090/images/propertyImgs/arrow-right.png" class="arrow-icon" /> --> <!-- <image src="http://47.104.199.163:9090/images/propertyImgs/arrow-right.png" class="arrow-icon" /> -->
</view> </view>
</view> </view>
</div> </div>
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
</template> </template>
<script setup> <script setup>
import { import { ref } from 'vue'
ref
} from 'vue';
const list = ref([
const list = ref([{ {
icon: "/static/imgs/shangbao.png", icon: '/static/imgs/shangbao.png',
title: "上报记录" title: '上报记录'
}, },
{ {
icon: "/static/imgs/xunjian.png", icon: '/static/imgs/xunjian.png',
title: "巡检记录", title: '巡检记录'
// desc: "您可以在这里添加家人、朋友、租客~" // desc: "您可以在这里添加家人、朋友、租客~"
}, },
{ {
icon: "http://47.104.199.163:9090/images/propertyImgs/setting.png", icon: '/static/imgs/xunjian.png',
title: "设置" title: '仪表换新'
// desc: "您可以在这里添加家人、朋友、租客~"
},
{
icon: 'http://47.104.199.163:9090/images/propertyImgs/setting.png',
title: '设置'
} }
]) ])
const loginName = ref("wangh") const loginName = ref('wangh')
const phoneNum = ref('156*****1212') const phoneNum = ref('156*****1212')
// --方法---- // --方法----
const goToDetail = () => { const goToDetail = () => {
uni.navigateTo({ uni.navigateTo({
url: '/pageSubPack/notice-list/noticeListDetails' url: '/pageSubPack/notice-list/noticeListDetails'
}) })
} }
// 返回 // 返回
const onItemClick = (item) => { const onItemClick = item => {
if (item.title == '上报记录') { if (item.title == '上报记录') {
uni.redirectTo({ uni.navigateTo({
url: '/pageSubPack/report-record/report-record', url: '/pageSubPack/report-record/report-record'
}); })
} else if (item.title == '巡检记录') { } else if (item.title == '巡检记录') {
uni.redirectTo({ uni.navigateTo({
url: '', url: '/pageSubPack/inspection/inspection_record'
}); })
} else if (item.title == '仪表换新') {
uni.navigateTo({
url: '/pageSubPack/instrument/replacement_record'
})
} else if (item.title == '设置') { } else if (item.title == '设置') {
uni.redirectTo({ uni.redirectTo({
url: '/pageSubPack/setting/setting', url: '/pageSubPack/setting/setting'
}); })
}
} }
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.contentStyle { .contentStyle {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 100vh; min-height: 100vh;
background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%) background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%);
} }
.topBox { .topBox {
width: 100%; width: 100%;
padding: 20rpx 0px; padding: 20rpx 0px;
position: relative; position: relative;
// background-color: #1A75FF; // background-color: #1A75FF;
} }
.titleBox { .titleBox {
width: 100%; width: 100%;
position: relative; position: relative;
@@ -121,38 +131,32 @@
height: 48rpx; height: 48rpx;
top: 16rpx; top: 16rpx;
right: 0px; right: 0px;
}
} }
}
.img { .img {
width: 60px; width: 60px;
height: 60px; height: 60px;
margin-right: 10rpx; margin-right: 10rpx;
border-radius: 50%; border-radius: 50%;
} }
.name {
.name {
bottom: 30rpx; bottom: 30rpx;
left: 30rpx; left: 30rpx;
display: flex; display: flex;
align-items: center; align-items: center;
}
.contentBox {
}
.contentBox {
position: relative; position: relative;
} }
.status-bar { .status-bar {
width: 100vw; width: 100vw;
height: 46px; height: 46px;
} }
.list-item { .list-item {
display: flex; display: flex;
align-items: center; align-items: center;
/* background: #fff; */ /* background: #fff; */
@@ -160,30 +164,30 @@
border-radius: 8rpx; border-radius: 8rpx;
/* border-bottom:2rpx solid #f2f2f2; */ /* border-bottom:2rpx solid #f2f2f2; */
/* margin-bottom: 10rpx; */ /* margin-bottom: 10rpx; */
} }
.item-icon { .item-icon {
width: 84rpx; width: 84rpx;
height: 84rpx; height: 84rpx;
/* margin-right: 20rpx; */ /* margin-right: 20rpx; */
} }
.item-content { .item-content {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
} }
.item-title { .item-title {
font-size: 32rpx; font-size: 32rpx;
/* color: #333; */ /* color: #333; */
font-weight: 400; font-weight: 400;
margin: 10rpx 20rpx; margin: 10rpx 20rpx;
/* margin-bottom: 6rpx; */ /* margin-bottom: 6rpx; */
} }
.item-desc { .item-desc {
font-size: 24rpx; font-size: 24rpx;
color: #b5b5b5; color: #b5b5b5;
white-space: nowrap; white-space: nowrap;
@@ -191,27 +195,21 @@
overflow: hidden; overflow: hidden;
/* 超出隐藏 */ /* 超出隐藏 */
text-overflow: ellipsis; text-overflow: ellipsis;
}
.item-arrow {
}
.item-arrow {
width: 16rpx; width: 16rpx;
height: 16rpx; height: 16rpx;
border-right: 3rpx solid #999999; border-right: 3rpx solid #999999;
border-bottom: 3rpx solid #999999; border-bottom: 3rpx solid #999999;
transform: rotate(-45deg); transform: rotate(-45deg);
margin-right: 10rpx; margin-right: 10rpx;
} }
.page {
.page {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
padding: 0px 40rpx; padding: 0px 40rpx;
box-sizing: border-box; box-sizing: border-box;
} }
</style> </style>