Commit 37c26bd3e0113311b7648a2b92802693935200c8
1 parent
8957a764
巡查计划
Showing
8 changed files
with
590 additions
and
454 deletions
api/patrol-manage/patrol-plan.js
0 → 100644
| 1 | +// api/user.js | ||
| 2 | +import { post, get } from '@/common/utils/request'; | ||
| 3 | + | ||
| 4 | +/** | ||
| 5 | + * 登录接口 | ||
| 6 | + * @param {Object} params {mobile, password, code} | ||
| 7 | + * @returns {Promise} | ||
| 8 | + */ | ||
| 9 | +export const login = (params) => { | ||
| 10 | + return post('/admin-api/system/auth/login', params); | ||
| 11 | +}; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * 巡检计划汇总 | ||
| 15 | + * @returns {Promise} | ||
| 16 | + */ | ||
| 17 | +export const inspectionPlanPage = (params) => { | ||
| 18 | + return get('/app-api/app/garden/inspection-plan/page',params); | ||
| 19 | +}; | ||
| 20 | + | ||
| 21 | +/** | ||
| 22 | + * 退出登录 | ||
| 23 | + * @returns {Promise} | ||
| 24 | + */ | ||
| 25 | +export const logout = () => { | ||
| 26 | + return post('/admin-api/system/auth/logout'); | ||
| 27 | +}; | ||
| 28 | + | ||
| 29 | +/** | ||
| 30 | + * 模块列表用这个 | ||
| 31 | + * @returns {Promise} | ||
| 32 | + */ | ||
| 33 | +export const moduleList = () => { | ||
| 34 | + return get('/app-api/member/app-module/list'); | ||
| 35 | +}; |
components/empty-view/empty-view.vue
0 → 100644
| 1 | +<script setup lang="ts"> | ||
| 2 | +// 补充TS类型,非TS项目可删除lang="ts" | ||
| 3 | +import { defineProps } from 'vue'; | ||
| 4 | + | ||
| 5 | +const props = defineProps({ | ||
| 6 | + text: { | ||
| 7 | + type: String, | ||
| 8 | + default: 'zanwushuju' // 确保默认文本是zanwushuju | ||
| 9 | + } | ||
| 10 | +}); | ||
| 11 | +</script> | ||
| 12 | + | ||
| 13 | +<template> | ||
| 14 | + <up-empty | ||
| 15 | + mode="list"> | ||
| 16 | + </up-empty> | ||
| 17 | +</template> | ||
| 18 | + | ||
| 19 | +<style scoped lang="scss"> | ||
| 20 | + | ||
| 21 | +</style> | ||
| 0 | \ No newline at end of file | 22 | \ No newline at end of file |
main.js
| @@ -3,7 +3,7 @@ import App from './App' | @@ -3,7 +3,7 @@ import App from './App' | ||
| 3 | import uviewPlus from '@/uni_modules/uview-plus' | 3 | import uviewPlus from '@/uni_modules/uview-plus' |
| 4 | // 导入 Pinia 实例(你的 stores/index.js 导出的 pinia) | 4 | // 导入 Pinia 实例(你的 stores/index.js 导出的 pinia) |
| 5 | import pinia from '@/pinia/index' | 5 | import pinia from '@/pinia/index' |
| 6 | - | 6 | +import EmptyView from '@/components/empty-view/empty-view.vue'; |
| 7 | // #ifdef VUE3 | 7 | // #ifdef VUE3 |
| 8 | import { createSSRApp } from 'vue' | 8 | import { createSSRApp } from 'vue' |
| 9 | 9 | ||
| @@ -16,7 +16,7 @@ export function createApp() { | @@ -16,7 +16,7 @@ export function createApp() { | ||
| 16 | 16 | ||
| 17 | // 4. 注册 Pinia(核心:在 app 挂载前注册) | 17 | // 4. 注册 Pinia(核心:在 app 挂载前注册) |
| 18 | app.use(pinia) | 18 | app.use(pinia) |
| 19 | - | 19 | + app.component('EmptyView', EmptyView) |
| 20 | // 5. 返回 app + pinia(可选,便于调试) | 20 | // 5. 返回 app + pinia(可选,便于调试) |
| 21 | return { | 21 | return { |
| 22 | app, | 22 | app, |
pages-sub/daily/patrol-manage/add-patrol-record/index.vue
| 1 | <template> | 1 | <template> |
| 2 | - <view class="form-page"> | ||
| 3 | - <!-- uview表单容器 --> | ||
| 4 | - <u-form | ||
| 5 | - ref="formRef" | ||
| 6 | - :model="formData" | ||
| 7 | - :rules="formRules" | ||
| 8 | - label-width="180rpx" | 2 | + <view class="u-page"> |
| 3 | + <!-- 表单容器:统一label单位为rpx --> | ||
| 4 | + <up-form | ||
| 5 | + :model="form" | ||
| 6 | + ref="uFormRef" | ||
| 7 | + :rules="rules" | ||
| 9 | label-position="left" | 8 | label-position="left" |
| 10 | > | 9 | > |
| 11 | - <!-- 1. 文本域区域 --> | ||
| 12 | - <u-form-item | ||
| 13 | - label="问题描述" | 10 | + <!-- 1. 文本域(必填,无label但保留必填验证) --> |
| 11 | + <up-form-item | ||
| 14 | prop="content" | 12 | prop="content" |
| 15 | class="form-item" | 13 | class="form-item" |
| 16 | > | 14 | > |
| 17 | - <u-textarea | ||
| 18 | - v-model="formData.content" | ||
| 19 | - placeholder="请输入问题描述..." | 15 | + <up-textarea |
| 16 | + v-model="form.content" | ||
| 17 | + placeholder="请输入巡查描述" | ||
| 20 | maxlength="200" | 18 | maxlength="200" |
| 21 | :show-word-limit="true" | 19 | :show-word-limit="true" |
| 22 | - :height="200" | ||
| 23 | border | 20 | border |
| 21 | + :count="true" | ||
| 24 | class="textarea" | 22 | class="textarea" |
| 25 | - ></u-textarea> | ||
| 26 | - <view class="word-count">{{ formData.content.length }}/200</view> | ||
| 27 | - </u-form-item> | 23 | + ></up-textarea> |
| 24 | + </up-form-item> | ||
| 28 | 25 | ||
| 29 | - <!-- 2. 图片上传区域(带星号,限制1-3张) --> | ||
| 30 | - <u-form-item | ||
| 31 | - label="上传图片" | 26 | + <!-- 2. 上传图片:修复labelWidth单位为rpx --> |
| 27 | + <up-form-item | ||
| 32 | prop="images" | 28 | prop="images" |
| 33 | class="form-item" | 29 | class="form-item" |
| 34 | required | 30 | required |
| 31 | + label="上传图片" | ||
| 32 | + label-width="140rpx" | ||
| 35 | > | 33 | > |
| 36 | - <u-upload | ||
| 37 | - v-model="formData.images" | 34 | + <up-upload |
| 35 | + v-model="form.images" | ||
| 38 | :action="uploadUrl" | 36 | :action="uploadUrl" |
| 39 | :max-count="3" | 37 | :max-count="3" |
| 40 | :multiple="true" | 38 | :multiple="true" |
| @@ -44,173 +42,131 @@ | @@ -44,173 +42,131 @@ | ||
| 44 | upload-text="选择图片" | 42 | upload-text="选择图片" |
| 45 | del-color="#ff4d4f" | 43 | del-color="#ff4d4f" |
| 46 | class="upload-wrap" | 44 | class="upload-wrap" |
| 47 | - ></u-upload> | 45 | + ></up-upload> |
| 48 | <view class="tips">(最少1张,最多3张)</view> | 46 | <view class="tips">(最少1张,最多3张)</view> |
| 49 | - <view v-if="uploadTips" class="upload-tips">{{ uploadTips }}</view> | ||
| 50 | - </u-form-item> | 47 | + </up-form-item> |
| 51 | 48 | ||
| 52 | - <!-- 3. 单选选择区域 --> | ||
| 53 | - <u-form-item | ||
| 54 | - label="是否转为工单" | 49 | + <!-- 3. 转为工单:修复radio绑定 + labelWidth单位 --> |
| 50 | + <up-form-item | ||
| 55 | prop="isWorkOrder" | 51 | prop="isWorkOrder" |
| 56 | class="form-item" | 52 | class="form-item" |
| 57 | required | 53 | required |
| 54 | + label="转为工单" | ||
| 55 | + label-width="140rpx" | ||
| 58 | > | 56 | > |
| 59 | - <u-radio-group | ||
| 60 | - v-model="formData.isWorkOrder" | ||
| 61 | - :list="radioList" | 57 | + <up-radio-group |
| 58 | + v-model="form.isWorkOrder" | ||
| 62 | active-color="#1989fa" | 59 | active-color="#1989fa" |
| 60 | + direction="row" | ||
| 63 | class="radio-group" | 61 | class="radio-group" |
| 64 | - ></u-radio-group> | ||
| 65 | - </u-form-item> | ||
| 66 | - </u-form> | 62 | + > |
| 63 | + <!-- 修复:radio绑定value而非name --> | ||
| 64 | + <up-radio | ||
| 65 | + :custom-style="{marginRight: '40rpx'}" | ||
| 66 | + v-for="(item, index) in radioList" | ||
| 67 | + :label="item.label" | ||
| 68 | + :name="item.value" | ||
| 69 | + > | ||
| 70 | + {{ item.label }} | ||
| 71 | + </up-radio> | ||
| 72 | + </up-radio-group> | ||
| 73 | + </up-form-item> | ||
| 74 | + </up-form> | ||
| 67 | 75 | ||
| 68 | <!-- 底部提交按钮 --> | 76 | <!-- 底部提交按钮 --> |
| 69 | - <view class="btn-wrap"> | ||
| 70 | - <u-button | 77 | + <view class="submit-btn-wrap"> |
| 78 | + <up-button | ||
| 71 | type="primary" | 79 | type="primary" |
| 72 | size="default" | 80 | size="default" |
| 73 | - @click="handleSubmit" | 81 | + @click="submit" |
| 74 | :style="{ width: '100%', height: '88rpx', fontSize: '32rpx', borderRadius: 0 }" | 82 | :style="{ width: '100%', height: '88rpx', fontSize: '32rpx', borderRadius: 0 }" |
| 75 | > | 83 | > |
| 76 | 提交 | 84 | 提交 |
| 77 | - </u-button> | 85 | + </up-button> |
| 78 | </view> | 86 | </view> |
| 79 | </view> | 87 | </view> |
| 80 | </template> | 88 | </template> |
| 81 | 89 | ||
| 82 | <script setup> | 90 | <script setup> |
| 83 | import { ref, reactive } from 'vue'; | 91 | import { ref, reactive } from 'vue'; |
| 84 | - | ||
| 85 | -// 表单实例 | ||
| 86 | -const formRef = ref(null); | ||
| 87 | - | ||
| 88 | -// 1. 表单数据(统一管理) | ||
| 89 | -const formData = reactive({ | ||
| 90 | - content: '', // 问题描述 | ||
| 91 | - images: [], // 上传图片列表 | ||
| 92 | - isWorkOrder: '' // 是否转为工单 1:是 2:否 | 92 | +// 表单数据 |
| 93 | +const form = reactive({ | ||
| 94 | + content: '', // 文本域内容 | ||
| 95 | + images: [], // 上传图片列表 | ||
| 96 | + isWorkOrder: '1' // 是否转为工单 1:是 2:否 | ||
| 93 | }); | 97 | }); |
| 94 | - | ||
| 95 | -// 2. 图片上传相关 | ||
| 96 | -const uploadUrl = ref(''); // 替换为真实上传接口地址 | ||
| 97 | -const uploadTips = ref(''); // 上传提示文字 | ||
| 98 | - | ||
| 99 | -// 3. 单选列表 | 98 | +// 图片上传地址 |
| 99 | +const uploadUrl = ref(''); | ||
| 100 | +// 单选列表 | ||
| 100 | const radioList = ref([ | 101 | const radioList = ref([ |
| 101 | - { label: '是', value: '1' }, | ||
| 102 | - { label: '否', value: '2' } | 102 | + {label: '是', value: '1'}, |
| 103 | + {label: '否', value: '2'} | ||
| 103 | ]); | 104 | ]); |
| 104 | - | ||
| 105 | -// 4. 表单验证规则 | ||
| 106 | -const formRules = ref({ | ||
| 107 | - // 问题描述验证 | 105 | +// 校验规则 |
| 106 | +const rules = reactive({ | ||
| 108 | content: [ | 107 | content: [ |
| 109 | - { required: true, message: '请输入问题描述', trigger: 'blur' }, | ||
| 110 | - { max: 200, message: '问题描述不能超过200字', trigger: 'blur' } | 108 | + {required: true, message: '请输入巡查描述', trigger: ['blur', 'change']}, |
| 109 | + {max: 200, message: '内容不能超过200字', trigger: ['blur', 'change']} | ||
| 111 | ], | 110 | ], |
| 112 | - // 图片验证(自定义规则) | ||
| 113 | images: [ | 111 | images: [ |
| 114 | { | 112 | { |
| 115 | validator: (rule, value, callback) => { | 113 | validator: (rule, value, callback) => { |
| 116 | - if (value.length < 1) { | ||
| 117 | - callback(new Error('最少需要上传1张图片')); | ||
| 118 | - uploadTips.value = '最少需要上传1张图片!'; | ||
| 119 | - } else if (value.length > 3) { | ||
| 120 | - callback(new Error('最多只能上传3张图片')); | ||
| 121 | - uploadTips.value = '最多只能上传3张图片!'; | ||
| 122 | - } else { | ||
| 123 | - uploadTips.value = ''; | ||
| 124 | - callback(); | ||
| 125 | - } | 114 | + if (value.length < 1) callback(new Error('最少需要上传1张图片')); |
| 115 | + else if (value.length > 3) callback(new Error('最多只能上传3张图片')); | ||
| 116 | + else callback(); | ||
| 126 | }, | 117 | }, |
| 127 | - trigger: ['change', 'blur'] | 118 | + trigger: ['change'] |
| 128 | } | 119 | } |
| 129 | ], | 120 | ], |
| 130 | - // 单选验证 | ||
| 131 | isWorkOrder: [ | 121 | isWorkOrder: [ |
| 132 | - { required: true, message: '请选择是否转为工单', trigger: 'change' } | 122 | + {required: true, message: '请选择是否转为工单', trigger: ['change']} |
| 133 | ] | 123 | ] |
| 134 | }); | 124 | }); |
| 135 | - | ||
| 136 | -// 图片上传后处理 | 125 | +// 表单引用 |
| 126 | +const uFormRef = ref(null); | ||
| 127 | +// 图片上传/删除/超出处理 | ||
| 137 | const handleAfterRead = (event) => { | 128 | const handleAfterRead = (event) => { |
| 138 | - const { file } = event; | ||
| 139 | - // 模拟上传(实际项目替换为真实接口请求) | 129 | + const {file} = event; |
| 140 | setTimeout(() => { | 130 | setTimeout(() => { |
| 141 | - // 上传成功后将文件加入列表 | ||
| 142 | - formData.images.push({ | ||
| 143 | - url: file.url, // 本地临时路径/线上地址 | ||
| 144 | - name: file.name, | ||
| 145 | - status: 'success' | ||
| 146 | - }); | ||
| 147 | - // 手动触发图片验证 | ||
| 148 | - formRef.value.validateField('images'); | 131 | + form.images.push({url: file.url, name: file.name || 'image.png', status: 'success'}); |
| 132 | + uFormRef.value.validateField('images'); | ||
| 149 | }, 500); | 133 | }, 500); |
| 150 | }; | 134 | }; |
| 151 | - | ||
| 152 | -// 删除图片 | ||
| 153 | const handleDelete = (index) => { | 135 | const handleDelete = (index) => { |
| 154 | - formData.images.splice(index, 1); | ||
| 155 | - // 手动触发图片验证 | ||
| 156 | - formRef.value.validateField('images'); | ||
| 157 | -}; | ||
| 158 | - | ||
| 159 | -// 超出最大数量提示 | ||
| 160 | -const handleExceed = () => { | ||
| 161 | - uni.showToast({ | ||
| 162 | - title: '最多只能上传3张图片', | ||
| 163 | - icon: 'none' | ||
| 164 | - }); | 136 | + form.images.splice(index, 1); |
| 137 | + uFormRef.value.validateField('images'); | ||
| 165 | }; | 138 | }; |
| 166 | - | ||
| 167 | -// 提交表单 | ||
| 168 | -const handleSubmit = () => { | ||
| 169 | - // 触发表单整体验证 | ||
| 170 | - formRef.value.validate((valid) => { | ||
| 171 | - if (!valid) { | ||
| 172 | - return; // 验证失败则返回 | 139 | +const handleExceed = () => uni.$u.toast('最多只能上传3张图片', 'none'); |
| 140 | +// 提交方法 | ||
| 141 | +const submit = () => { | ||
| 142 | + uFormRef.value.validate().then(valid => { | ||
| 143 | + if (valid) { | ||
| 144 | + uni.showLoading({title: '提交中...'}); | ||
| 145 | + setTimeout(() => { | ||
| 146 | + uni.hideLoading(); | ||
| 147 | + uni.$u.toast('提交成功'); | ||
| 148 | + uFormRef.value.resetFields(); | ||
| 149 | + form.images = []; | ||
| 150 | + }, 1500); | ||
| 173 | } | 151 | } |
| 174 | - | ||
| 175 | - // 验证通过,构造提交数据 | ||
| 176 | - const submitData = { | ||
| 177 | - content: formData.content, | ||
| 178 | - images: formData.images.map(item => item.url), // 提取图片地址 | ||
| 179 | - isWorkOrder: formData.isWorkOrder | ||
| 180 | - }; | ||
| 181 | - | ||
| 182 | - // 模拟接口提交 | ||
| 183 | - uni.showLoading({ title: '提交中...' }); | ||
| 184 | - setTimeout(() => { | ||
| 185 | - uni.hideLoading(); | ||
| 186 | - uni.showToast({ title: '提交成功', icon: 'success' }); | ||
| 187 | - // 重置表单 | ||
| 188 | - formRef.value.resetFields(); | ||
| 189 | - formData.images = []; // 手动清空图片列表 | ||
| 190 | - uploadTips.value = ''; | ||
| 191 | - }, 1500); | ||
| 192 | - }); | 152 | + }).catch(() => uni.$u.toast('表单验证失败,请检查必填项', 'none')); |
| 193 | }; | 153 | }; |
| 194 | </script> | 154 | </script> |
| 195 | 155 | ||
| 196 | <style scoped lang="scss"> | 156 | <style scoped lang="scss"> |
| 197 | -.form-page { | ||
| 198 | - background-color: #f8f8f8; | ||
| 199 | - min-height: 100vh; | 157 | +// 基础布局:避免内容重叠 |
| 158 | +.u-page { | ||
| 200 | padding: 20rpx; | 159 | padding: 20rpx; |
| 201 | - box-sizing: border-box; | ||
| 202 | - display: flex; | ||
| 203 | - flex-direction: column; | ||
| 204 | - padding-bottom: 100rpx; // 给底部按钮留空间 | ||
| 205 | } | 160 | } |
| 206 | 161 | ||
| 207 | -// 表单项通用样式 | 162 | +// 表单项样式:分隔区域,避免label和内容重叠 |
| 208 | .form-item { | 163 | .form-item { |
| 209 | background-color: #fff; | 164 | background-color: #fff; |
| 210 | border-radius: 12rpx; | 165 | border-radius: 12rpx; |
| 211 | - padding: 20rpx; | 166 | + padding:0 30rpx; |
| 212 | margin-bottom: 20rpx; | 167 | margin-bottom: 20rpx; |
| 213 | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03); | 168 | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03); |
| 169 | + position: relative; // 为文本域必填*号定位 | ||
| 214 | } | 170 | } |
| 215 | 171 | ||
| 216 | // 文本域样式 | 172 | // 文本域样式 |
| @@ -218,13 +174,6 @@ const handleSubmit = () => { | @@ -218,13 +174,6 @@ const handleSubmit = () => { | ||
| 218 | margin-bottom: 10rpx; | 174 | margin-bottom: 10rpx; |
| 219 | } | 175 | } |
| 220 | 176 | ||
| 221 | -.word-count { | ||
| 222 | - font-size: 24rpx; | ||
| 223 | - color: #999; | ||
| 224 | - text-align: right; | ||
| 225 | - margin-top: 5rpx; | ||
| 226 | -} | ||
| 227 | - | ||
| 228 | // 上传区域样式 | 177 | // 上传区域样式 |
| 229 | .upload-wrap { | 178 | .upload-wrap { |
| 230 | margin-top: 10rpx; | 179 | margin-top: 10rpx; |
| @@ -233,22 +182,23 @@ const handleSubmit = () => { | @@ -233,22 +182,23 @@ const handleSubmit = () => { | ||
| 233 | .tips { | 182 | .tips { |
| 234 | font-size: 24rpx; | 183 | font-size: 24rpx; |
| 235 | color: #999; | 184 | color: #999; |
| 236 | - margin: 10rpx 0 5rpx 0; | 185 | + margin-top: 10rpx; |
| 237 | } | 186 | } |
| 238 | 187 | ||
| 239 | -.upload-tips { | ||
| 240 | - font-size: 24rpx; | ||
| 241 | - color: #ff4d4f; | ||
| 242 | - margin-top: 5rpx; | 188 | +// ========== 核心:修复必填*号显示 ========== |
| 189 | +// 1. 放大+高亮uView默认必填*号 | ||
| 190 | +:deep(.up-form-item__required) { | ||
| 191 | + color: #ff4d4f !important; // 醒目红色 | ||
| 192 | + font-size: 28rpx !important; // 放大字号 | ||
| 193 | + font-weight: bold !important; // 加粗 | ||
| 194 | + margin-left: 5rpx !important; // 与label保持间距 | ||
| 243 | } | 195 | } |
| 244 | 196 | ||
| 245 | -// 单选组样式 | ||
| 246 | -.radio-group { | ||
| 247 | - margin-top: 10rpx; | ||
| 248 | -} | ||
| 249 | 197 | ||
| 250 | -// 底部按钮容器 | ||
| 251 | -.btn-wrap { | 198 | + |
| 199 | + | ||
| 200 | +// 底部按钮样式 | ||
| 201 | +.submit-btn-wrap { | ||
| 252 | position: fixed; | 202 | position: fixed; |
| 253 | bottom: 0; | 203 | bottom: 0; |
| 254 | left: 0; | 204 | left: 0; |
| @@ -256,27 +206,10 @@ const handleSubmit = () => { | @@ -256,27 +206,10 @@ const handleSubmit = () => { | ||
| 256 | z-index: 999; | 206 | z-index: 999; |
| 257 | padding: 0; | 207 | padding: 0; |
| 258 | background-color: #f8f8f8; | 208 | background-color: #f8f8f8; |
| 259 | - // 兼容微信小程序底部安全区 | ||
| 260 | /* #ifdef MP-WEIXIN */ | 209 | /* #ifdef MP-WEIXIN */ |
| 261 | - padding-bottom: constant(safe-area-inset-bottom); | ||
| 262 | - padding-bottom: env(safe-area-inset-bottom); | 210 | + //padding-bottom: constant(safe-area-inset-bottom); |
| 211 | + //padding-bottom: env(safe-area-inset-bottom); | ||
| 263 | /* #endif */ | 212 | /* #endif */ |
| 264 | } | 213 | } |
| 265 | 214 | ||
| 266 | -// 覆盖u-form默认样式(可选) | ||
| 267 | -:deep(.u-form-item__content) { | ||
| 268 | - padding-left: 0 !important; | ||
| 269 | -} | ||
| 270 | - | ||
| 271 | -:deep(.u-form-item__label) { | ||
| 272 | - font-size: 30rpx !important; | ||
| 273 | - color: #333 !important; | ||
| 274 | - font-weight: 500 !important; | ||
| 275 | -} | ||
| 276 | - | ||
| 277 | -:deep(.u-form-item__error-message) { | ||
| 278 | - font-size: 24rpx !important; | ||
| 279 | - color: #ff4d4f !important; | ||
| 280 | - margin-top: 5rpx !important; | ||
| 281 | -} | ||
| 282 | </style> | 215 | </style> |
| 283 | \ No newline at end of file | 216 | \ No newline at end of file |
pages-sub/daily/patrol-manage/patrol-plan/index.vue
| @@ -2,200 +2,189 @@ | @@ -2,200 +2,189 @@ | ||
| 2 | <view class="page-container"> | 2 | <view class="page-container"> |
| 3 | <!-- 顶部固定区域:Tabs + 搜索框 --> | 3 | <!-- 顶部固定区域:Tabs + 搜索框 --> |
| 4 | <view class="top-fixed"> | 4 | <view class="top-fixed"> |
| 5 | - <!-- uView Tabs 标签 --> | ||
| 6 | <u-tabs | 5 | <u-tabs |
| 7 | - v-model="activeTab" | ||
| 8 | :list="tabList" | 6 | :list="tabList" |
| 9 | :is-scroll="false" | 7 | :is-scroll="false" |
| 10 | :activeStyle="{ | 8 | :activeStyle="{ |
| 11 | - color: '#3c9cff', | ||
| 12 | - fontWeight: 'bold', | ||
| 13 | - transform: 'scale(1.05)' | 9 | + color: '#3c9cff', |
| 10 | + fontWeight: 'bold', | ||
| 11 | + transform: 'scale(1.05)' | ||
| 14 | }" | 12 | }" |
| 15 | :inactiveStyle="{ | 13 | :inactiveStyle="{ |
| 16 | - color: '#606266', | ||
| 17 | - transform: 'scale(1)' | 14 | + color: '#606266', |
| 15 | + transform: 'scale(1)' | ||
| 18 | }" | 16 | }" |
| 19 | font-size="28rpx" | 17 | font-size="28rpx" |
| 20 | @change="handleTabChange" | 18 | @change="handleTabChange" |
| 21 | ></u-tabs> | 19 | ></u-tabs> |
| 22 | 20 | ||
| 23 | - <!-- 道路名称搜索框 --> | ||
| 24 | - <u-search | 21 | + <up-search |
| 25 | v-model="searchValue" | 22 | v-model="searchValue" |
| 26 | placeholder="请输入道路名称" | 23 | placeholder="请输入道路名称" |
| 27 | bg-color="#f5f5f5" | 24 | bg-color="#f5f5f5" |
| 28 | - border-radius="8rpx" | ||
| 29 | - :clearabled="true" | 25 | + shape="round" |
| 30 | @search="handleSearch" | 26 | @search="handleSearch" |
| 31 | - @clear="handleSearchClear" | ||
| 32 | maxlength="50" | 27 | maxlength="50" |
| 33 | - style="margin: 10rpx 20rpx 0" | ||
| 34 | - ></u-search> | 28 | + style="margin: 20rpx 20rpx 0" |
| 29 | + ></up-search> | ||
| 35 | </view> | 30 | </view> |
| 36 | 31 | ||
| 37 | - <!-- z-paging 分页列表 --> | 32 | + <!-- 修复点1:绑定total + 修正ref名称 + 移除auto=false(改用默认auto=true更易控制) --> |
| 38 | <z-paging | 33 | <z-paging |
| 34 | + ref="paging" | ||
| 39 | v-model="planList" | 35 | v-model="planList" |
| 40 | - :page-size="pageSize" | ||
| 41 | - :total="total" | ||
| 42 | - @query="loadData" | ||
| 43 | - :scroll-top-reset-type="1" | ||
| 44 | - :top="160" | ||
| 45 | - bg-color="#f8f8f8" | 36 | + @query="queryList" |
| 37 | + | ||
| 46 | > | 38 | > |
| 39 | + <template #empty> | ||
| 40 | + <empty-view/> | ||
| 41 | + </template> | ||
| 47 | <!-- 计划卡片列表 --> | 42 | <!-- 计划卡片列表 --> |
| 48 | <view class="card-list"> | 43 | <view class="card-list"> |
| 49 | - <view class="plan-card" v-for="(item, index) in planList" :key="index"> | 44 | + |
| 45 | + <view class="plan-card" v-for="(item, index) in planList" :key="item.batchNo"> | ||
| 50 | <view class="card-content"> | 46 | <view class="card-content"> |
| 51 | - <!-- 道路名称 --> | ||
| 52 | <view class="row-item"> | 47 | <view class="row-item"> |
| 53 | <text class="label">道路名称:</text> | 48 | <text class="label">道路名称:</text> |
| 54 | <text class="value up-line-1">{{ item.roadName }}</text> | 49 | <text class="value up-line-1">{{ item.roadName }}</text> |
| 55 | </view> | 50 | </view> |
| 56 | - <!-- 所属街道 --> | ||
| 57 | <view class="row-item"> | 51 | <view class="row-item"> |
| 58 | <text class="label">所属街道:</text> | 52 | <text class="label">所属街道:</text> |
| 59 | - <text class="value up-line-1">{{ item.street }}</text> | 53 | + <text class="value up-line-1">{{ item.streetName }}</text> |
| 60 | </view> | 54 | </view> |
| 61 | - <!-- 养护级别 + 计划明细按钮 --> | ||
| 62 | <view class="row-item flex-between"> | 55 | <view class="row-item flex-between"> |
| 63 | <view> | 56 | <view> |
| 64 | <text class="label">养护级别:</text> | 57 | <text class="label">养护级别:</text> |
| 65 | - <text class="value">{{ item.maintainLevel }}</text> | 58 | + <text class="value">{{ levelMap[item.levelId] || '未知级别' }}</text> |
| 66 | </view> | 59 | </view> |
| 67 | <text class="detail-btn" @click="gotoDetail(item)">计划明细</text> | 60 | <text class="detail-btn" @click="gotoDetail(item)">计划明细</text> |
| 68 | </view> | 61 | </view> |
| 69 | - <!-- 计划类型 --> | ||
| 70 | <view class="row-item"> | 62 | <view class="row-item"> |
| 71 | <text class="label">计划类型:</text> | 63 | <text class="label">计划类型:</text> |
| 72 | - <text class="value up-line-1">{{ item.planType }}</text> | 64 | + <text class="value up-line-1">{{ planTypeMap[item.planTypeId] || '未知类型' }}</text> |
| 73 | </view> | 65 | </view> |
| 74 | - <!-- 计划时间 --> | ||
| 75 | <view class="row-item"> | 66 | <view class="row-item"> |
| 76 | <text class="label">计划时间:</text> | 67 | <text class="label">计划时间:</text> |
| 77 | - <text class="value up-line-1">{{ item.planTime }}</text> | 68 | + <text class="value up-line-1">{{ formatPlanTime(item.beginTime, item.endTime) }}</text> |
| 78 | </view> | 69 | </view> |
| 79 | </view> | 70 | </view> |
| 80 | </view> | 71 | </view> |
| 81 | </view> | 72 | </view> |
| 82 | 73 | ||
| 83 | - <!-- 空数据占位 --> | ||
| 84 | - <template #empty> | ||
| 85 | - <view class="empty-wrap"> | ||
| 86 | - <text class="empty-text">暂无相关计划数据</text> | ||
| 87 | - </view> | ||
| 88 | - </template> | ||
| 89 | </z-paging> | 74 | </z-paging> |
| 90 | </view> | 75 | </view> |
| 91 | </template> | 76 | </template> |
| 92 | 77 | ||
| 93 | <script setup> | 78 | <script setup> |
| 94 | -import { ref, reactive, onMounted } from 'vue'; | 79 | +import { ref } from 'vue'; |
| 95 | import { onLoad, onShow } from '@dcloudio/uni-app'; | 80 | import { onLoad, onShow } from '@dcloudio/uni-app'; |
| 81 | +import { inspectionPlanPage } from "@/api/patrol-manage/patrol-plan"; | ||
| 96 | // Tabs 配置 | 82 | // Tabs 配置 |
| 97 | const tabList = ref([ | 83 | const tabList = ref([ |
| 98 | - {name: '待完成', id: 'pending'}, | ||
| 99 | - {name: '已失效', id: 'invalid'}, | ||
| 100 | - {name: '已完成', id: 'completed'} | 84 | + {name: '待完成', id: '1'}, |
| 85 | + {name: '已失效', id: '3'}, | ||
| 86 | + {name: '已完成', id: '2'} | ||
| 101 | ]); | 87 | ]); |
| 102 | -const activeTab = ref('pending'); // 当前激活的Tab | 88 | + |
| 89 | +const activeTab = ref('1'); | ||
| 103 | // 搜索相关 | 90 | // 搜索相关 |
| 104 | -const searchValue = ref(''); // 搜索框值 | 91 | +const searchValue = ref(''); |
| 105 | // 分页相关 | 92 | // 分页相关 |
| 106 | -const pageNo = ref(1); // 当前页码 | ||
| 107 | -const pageSize = ref(10); // 每页条数 | ||
| 108 | -const total = ref(100); // 总记录数(模拟大量数据) | ||
| 109 | -const planList = ref([]); // 计划列表数据 | ||
| 110 | -// Tab切换事件(清空搜索条件) | ||
| 111 | -const handleTabChange = () => { | ||
| 112 | - // 清空搜索框 | ||
| 113 | - searchValue.value = ''; | ||
| 114 | - // 重置分页 | ||
| 115 | - pageNo.value = 1; | ||
| 116 | - // 重新加载数据 | ||
| 117 | - loadData(true); | 93 | +const pageNo = ref(1); |
| 94 | +const pageSize = ref(10); | ||
| 95 | +const total = ref(0); | ||
| 96 | +const planList = ref([]); | ||
| 97 | +const paging = ref(null); // 修复点2:ref名称与模板中一致 | ||
| 98 | +// 养护级别/计划类型映射 | ||
| 99 | +const levelMap = { | ||
| 100 | + 11: '一级养护', | ||
| 101 | + 12: '二级养护', | ||
| 102 | + 13: '三级养护' | ||
| 103 | +}; | ||
| 104 | +const planTypeMap = { | ||
| 105 | + 3001: '日常养护', | ||
| 106 | + 3002: '专项养护', | ||
| 107 | + 3003: '应急养护' | ||
| 108 | +}; | ||
| 109 | + | ||
| 110 | +// 时间格式化 | ||
| 111 | +const formatPlanTime = (beginTime, endTime) => { | ||
| 112 | + if (!beginTime || !endTime) return '暂无'; | ||
| 113 | + const formatDate = (timestamp) => { | ||
| 114 | + const date = new Date(timestamp); | ||
| 115 | + const year = date.getFullYear(); | ||
| 116 | + const month = (date.getMonth() + 1).toString().padStart(2, '0'); | ||
| 117 | + const day = date.getDate().toString().padStart(2, '0'); | ||
| 118 | + return `${year}-${month}-${day}`; | ||
| 119 | + }; | ||
| 120 | + return `${formatDate(beginTime)} 至 ${formatDate(endTime)}`; | ||
| 118 | }; | 121 | }; |
| 119 | -// 搜索事件 | 122 | +// Tab切换 |
| 123 | +const handleTabChange = (val) => { | ||
| 124 | + console.log(val) | ||
| 125 | + console.log(activeTab.value) | ||
| 126 | + activeTab.value = val.id | ||
| 127 | + searchValue.value = '' | ||
| 128 | + // paging.value.reload() | ||
| 129 | + // queryList(pageNo, pageSize) | ||
| 130 | + paging.value.reload() | ||
| 131 | +}; | ||
| 132 | +// 搜索/清空搜索 | ||
| 120 | const handleSearch = () => { | 133 | const handleSearch = () => { |
| 121 | - pageNo.value = 1; | ||
| 122 | - loadData(true); | 134 | + // searchValue.value = ''; |
| 135 | + console.log(searchValue.value) | ||
| 136 | + paging.value.reload() | ||
| 123 | }; | 137 | }; |
| 124 | -// 清空搜索框 | ||
| 125 | const handleSearchClear = () => { | 138 | const handleSearchClear = () => { |
| 126 | - pageNo.value = 1; | ||
| 127 | - loadData(true); | 139 | + // searchValue.value = '' |
| 140 | + paging.value.reload() | ||
| 128 | }; | 141 | }; |
| 129 | -// 生成模拟数据 | ||
| 130 | -const generateMockData = (page) => { | ||
| 131 | - const mockList = []; | ||
| 132 | - const tabMap = { | ||
| 133 | - pending: '待完成', | ||
| 134 | - invalid: '已失效', | ||
| 135 | - completed: '已完成' | ||
| 136 | - }; | ||
| 137 | - const maintainLevels = ['一级养护', '二级养护', '三级养护']; | ||
| 138 | - const planTypes = ['日常养护', '专项养护', '应急养护']; | ||
| 139 | - const streets = ['西长安街街道西长安街街道西长安街街道西长安街街道', '东城区东华门街道', '朝阳区望京街道', '海淀区中关村街道', '丰台区马家堡街道']; | ||
| 140 | - const roadNames = [ | ||
| 141 | - '长安街王府井大街王府井大街王府井大街王府井大街王府井大街', '王府井大街王府井大街王府井大街王府井大街王府井大街', '中关村南大街', '朝阳北路', '建国路', | ||
| 142 | - '复兴路', '西直门外大街', '广渠路', '阜成门外大街', '安定门东大街' | ||
| 143 | - ]; | ||
| 144 | - // 生成当前页数据 | ||
| 145 | - for (let i = 0; i < 35; i++) { | ||
| 146 | - const idx = (page - 1) * pageSize.value + i; | ||
| 147 | - mockList.push({ | ||
| 148 | - id: idx + 1, | ||
| 149 | - roadName: `${roadNames[i]}${roadNames[i]}${roadNames[i]}${idx + 1}段`, | ||
| 150 | - street: streets[Math.floor(Math.random() * streets.length)], | ||
| 151 | - maintainLevel: maintainLevels[Math.floor(Math.random() * maintainLevels.length)], | ||
| 152 | - planType: planTypes[Math.floor(Math.random() * planTypes.length)], | ||
| 153 | - planTime: `2025-${Math.floor(Math.random() * 12) + 1}-${Math.floor(Math.random() * 28) + 1} 至 2025-${Math.floor(Math.random() * 12) + 1}-${Math.floor(Math.random() * 28) + 1}`, | ||
| 154 | - status: tabMap[activeTab.value] | ||
| 155 | - }); | 142 | +// 加载数据(核心修复) |
| 143 | +const queryList = async (pageNo, pageSize) => { | ||
| 144 | + try { | ||
| 145 | + const params = { | ||
| 146 | + roadName: searchValue.value.trim() || '', | ||
| 147 | + pageNo: pageNo, | ||
| 148 | + pageSize: pageSize, | ||
| 149 | + finishState: activeTab.value | ||
| 150 | + }; | ||
| 151 | + console.log('请求参数:', params); | ||
| 152 | + const res = await inspectionPlanPage(params); | ||
| 153 | + console.log('接口返回:', res); | ||
| 154 | + paging.value.complete(res.list); | ||
| 155 | + } catch (error) { | ||
| 156 | + console.error('加载失败:', error); | ||
| 157 | + // 修复点4:加载失败调用complete(false) | ||
| 158 | + paging.value?.complete(false); | ||
| 159 | + uni.showToast({title: '加载失败,请重试', icon: 'none'}); | ||
| 156 | } | 160 | } |
| 157 | - return mockList; | ||
| 158 | -}; | ||
| 159 | -// 加载数据(z-paging 核心方法) | ||
| 160 | -const loadData = (isRefresh = false) => { | ||
| 161 | - // 模拟接口请求延迟 | ||
| 162 | - setTimeout(() => { | ||
| 163 | - const newData = generateMockData(pageNo.value); | ||
| 164 | - if (isRefresh) { | ||
| 165 | - planList.value = newData; // 刷新:替换数据 | ||
| 166 | - } else { | ||
| 167 | - planList.value = [...planList.value, ...newData]; // 加载更多:追加数据 | ||
| 168 | - } | ||
| 169 | - }, 500); | ||
| 170 | }; | 161 | }; |
| 171 | -// 跳转到待完成计划明细页面 | 162 | +// 跳转明细 |
| 172 | const gotoDetail = (item) => { | 163 | const gotoDetail = (item) => { |
| 173 | uni.navigateTo({ | 164 | uni.navigateTo({ |
| 174 | - url: `/pages-sub/daily/patrol-manage/pending-plan-detail/index?id=${item.id}&status=${activeTab.value}` | 165 | + url: `/pages-sub/daily/patrol-manage/pending-plan-detail/index?batchNo=${item.batchNo}&status=${activeTab.value}` |
| 175 | }); | 166 | }); |
| 176 | }; | 167 | }; |
| 177 | -// 页面加载初始化 | 168 | +// 修复点5:优化页面生命周期逻辑 |
| 178 | onLoad(() => { | 169 | onLoad(() => { |
| 179 | - loadData(true); | 170 | + // auto=true时,z-paging会自动触发query,此处可省略手动调用;若需强制初始化,可加: |
| 171 | + // paging.value?.triggerQuery(); | ||
| 180 | }); | 172 | }); |
| 181 | - | ||
| 182 | -// 页面每次显示时刷新(包括回退) | 173 | +// 仅在页面从明细页返回时,若有数据则刷新当前tab数据(避免干扰上拉加载) |
| 183 | onShow(() => { | 174 | onShow(() => { |
| 184 | - // 避免首次加载重复请求(onLoad已执行) | ||
| 185 | - if (planList.value.length > 0) { | ||
| 186 | - pageNo.value = 1; | ||
| 187 | - loadData(true); // 重新请求接口,刷新列表 | 175 | + if (planList.value.length > 0 && pageNo.value === 1) { |
| 176 | + queryList(); | ||
| 188 | } | 177 | } |
| 189 | }); | 178 | }); |
| 190 | </script> | 179 | </script> |
| 191 | 180 | ||
| 192 | <style scoped lang="scss"> | 181 | <style scoped lang="scss"> |
| 182 | +/* 样式保持不变,仅补充z-paging容器高度 */ | ||
| 193 | .page-container { | 183 | .page-container { |
| 194 | min-height: 100vh; | 184 | min-height: 100vh; |
| 195 | background-color: #f8f8f8; | 185 | background-color: #f8f8f8; |
| 196 | } | 186 | } |
| 197 | 187 | ||
| 198 | -/* 顶部固定区域 */ | ||
| 199 | .top-fixed { | 188 | .top-fixed { |
| 200 | position: fixed; | 189 | position: fixed; |
| 201 | top: 0; | 190 | top: 0; |
| @@ -207,9 +196,10 @@ onShow(() => { | @@ -207,9 +196,10 @@ onShow(() => { | ||
| 207 | box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); | 196 | box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
| 208 | } | 197 | } |
| 209 | 198 | ||
| 210 | -/* 计划卡片样式 */ | 199 | +/* 修复:确保列表容器不被顶部遮挡,且z-paging能识别滚动区域 */ |
| 211 | .card-list { | 200 | .card-list { |
| 212 | padding: 170rpx 20rpx 20rpx; | 201 | padding: 170rpx 20rpx 20rpx; |
| 202 | + /* 关键:给z-paging足够的滚动空间 */ | ||
| 213 | } | 203 | } |
| 214 | 204 | ||
| 215 | .plan-card { | 205 | .plan-card { |
| @@ -256,17 +246,5 @@ onShow(() => { | @@ -256,17 +246,5 @@ onShow(() => { | ||
| 256 | padding: 0 10rpx; | 246 | padding: 0 10rpx; |
| 257 | } | 247 | } |
| 258 | 248 | ||
| 259 | -/* 空数据样式 */ | ||
| 260 | -.empty-wrap { | ||
| 261 | - display: flex; | ||
| 262 | - flex-direction: column; | ||
| 263 | - align-items: center; | ||
| 264 | - justify-content: center; | ||
| 265 | - padding: 100rpx 0; | ||
| 266 | -} | ||
| 267 | 249 | ||
| 268 | -.empty-text { | ||
| 269 | - font-size: 28rpx; | ||
| 270 | - color: #999; | ||
| 271 | -} | ||
| 272 | </style> | 250 | </style> |
| 273 | \ No newline at end of file | 251 | \ No newline at end of file |
pages.json
| @@ -15,7 +15,8 @@ | @@ -15,7 +15,8 @@ | ||
| 15 | { | 15 | { |
| 16 | "path": "pages/workbench/index", | 16 | "path": "pages/workbench/index", |
| 17 | "style": { | 17 | "style": { |
| 18 | - "navigationBarTitleText": "工作台" | 18 | + "navigationBarTitleText": "工作台", |
| 19 | + "navigationStyle": "custom" | ||
| 19 | } | 20 | } |
| 20 | }, | 21 | }, |
| 21 | { | 22 | { |
| @@ -32,8 +33,7 @@ | @@ -32,8 +33,7 @@ | ||
| 32 | { | 33 | { |
| 33 | "path": "patrol-manage/patrol-plan/index", | 34 | "path": "patrol-manage/patrol-plan/index", |
| 34 | "style": { | 35 | "style": { |
| 35 | - "navigationBarTitleText": "巡查计划", | ||
| 36 | - "enablePullDownRefresh": false | 36 | + "navigationBarTitleText": "巡查计划" |
| 37 | } | 37 | } |
| 38 | }, | 38 | }, |
| 39 | { | 39 | { |
| @@ -150,7 +150,8 @@ | @@ -150,7 +150,8 @@ | ||
| 150 | "custom": { | 150 | "custom": { |
| 151 | "^u--(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue", | 151 | "^u--(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue", |
| 152 | "^up-(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue", | 152 | "^up-(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue", |
| 153 | - "^u-([^-].*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue" | 153 | + "^u-([^-].*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue", |
| 154 | + "^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue" | ||
| 154 | } | 155 | } |
| 155 | }, | 156 | }, |
| 156 | "globalStyle": { | 157 | "globalStyle": { |
pages/login/index.vue
| 1 | <template> | 1 | <template> |
| 2 | <view class="login-page"> | 2 | <view class="login-page"> |
| 3 | - <!-- 登录表单区域 --> | 3 | + <!-- 纯CSS渐变动效背景(替代粒子动画,兼容所有版本) --> |
| 4 | + <view class="bg-animation"></view> | ||
| 5 | + | ||
| 6 | + <!-- 登录表单区域(悬浮层) --> | ||
| 4 | <view class="login-form"> | 7 | <view class="login-form"> |
| 8 | + <!-- 登录标题(简约大气) --> | ||
| 9 | + <view class="login-title">园林登录</view> | ||
| 10 | + | ||
| 5 | <!-- 账号输入框 --> | 11 | <!-- 账号输入框 --> |
| 6 | <view class="form-item"> | 12 | <view class="form-item"> |
| 7 | <up-input | 13 | <up-input |
| @@ -12,9 +18,13 @@ | @@ -12,9 +18,13 @@ | ||
| 12 | input-align="left" | 18 | input-align="left" |
| 13 | :disabled="isLoading" | 19 | :disabled="isLoading" |
| 14 | @blur="checkAccount" | 20 | @blur="checkAccount" |
| 21 | + :custom-style="{ | ||
| 22 | + backgroundColor: 'rgba(255, 255, 255, 0.9)', | ||
| 23 | + borderColor: '#e5e7eb' | ||
| 24 | + }" | ||
| 15 | > | 25 | > |
| 16 | <template #prefix> | 26 | <template #prefix> |
| 17 | - <up-icon name="account" color="#909399" size="20"></up-icon> | 27 | + <up-icon name="account" color="#6b7280" size="20"></up-icon> |
| 18 | </template> | 28 | </template> |
| 19 | </up-input> | 29 | </up-input> |
| 20 | <!-- 账号错误提示 --> | 30 | <!-- 账号错误提示 --> |
| @@ -32,9 +42,13 @@ | @@ -32,9 +42,13 @@ | ||
| 32 | type="password" | 42 | type="password" |
| 33 | :disabled="isLoading" | 43 | :disabled="isLoading" |
| 34 | @blur="checkPassword" | 44 | @blur="checkPassword" |
| 45 | + :custom-style="{ | ||
| 46 | + backgroundColor: 'rgba(255, 255, 255, 0.9)', | ||
| 47 | + borderColor: '#e5e7eb' | ||
| 48 | + }" | ||
| 35 | > | 49 | > |
| 36 | <template #prefix> | 50 | <template #prefix> |
| 37 | - <up-icon name="lock" color="#909399" size="20"></up-icon> | 51 | + <up-icon name="lock" color="#6b7280" size="20"></up-icon> |
| 38 | </template> | 52 | </template> |
| 39 | </up-input> | 53 | </up-input> |
| 40 | <!-- 密码错误提示 --> | 54 | <!-- 密码错误提示 --> |
| @@ -48,6 +62,14 @@ | @@ -48,6 +62,14 @@ | ||
| 48 | size="large" | 62 | size="large" |
| 49 | :loading="isLoading" | 63 | :loading="isLoading" |
| 50 | @click="handleLogin" | 64 | @click="handleLogin" |
| 65 | + :custom-style="{ | ||
| 66 | + backgroundColor: '#3b82f6', | ||
| 67 | + borderColor: '#3b82f6', | ||
| 68 | + borderRadius: '44rpx', | ||
| 69 | + height: '88rpx', | ||
| 70 | + lineHeight: '88rpx', | ||
| 71 | + fontSize: '32rpx' | ||
| 72 | + }" | ||
| 51 | > | 73 | > |
| 52 | 登录 | 74 | 登录 |
| 53 | </up-button> | 75 | </up-button> |
| @@ -76,8 +98,14 @@ const error = reactive({ | @@ -76,8 +98,14 @@ const error = reactive({ | ||
| 76 | // 实例化 Pinia 用户仓库 | 98 | // 实例化 Pinia 用户仓库 |
| 77 | const userStore = useUserStore(); | 99 | const userStore = useUserStore(); |
| 78 | 100 | ||
| 79 | -// 页面加载时校验登录态 | 101 | +// 页面加载时初始化 |
| 80 | onMounted(() => { | 102 | onMounted(() => { |
| 103 | + // 检查登录态 | ||
| 104 | + checkLoginStatus(); | ||
| 105 | +}); | ||
| 106 | + | ||
| 107 | +// 检查登录状态 | ||
| 108 | +const checkLoginStatus = () => { | ||
| 81 | try { | 109 | try { |
| 82 | // 已登录则直接跳首页 | 110 | // 已登录则直接跳首页 |
| 83 | if (userStore.isLogin) { | 111 | if (userStore.isLogin) { |
| @@ -93,7 +121,7 @@ onMounted(() => { | @@ -93,7 +121,7 @@ onMounted(() => { | ||
| 93 | } catch (err) { | 121 | } catch (err) { |
| 94 | console.warn('检查登录状态失败:', err); | 122 | console.warn('检查登录状态失败:', err); |
| 95 | } | 123 | } |
| 96 | -}); | 124 | +}; |
| 97 | 125 | ||
| 98 | // 校验账号 | 126 | // 校验账号 |
| 99 | const checkAccount = () => { | 127 | const checkAccount = () => { |
| @@ -124,7 +152,7 @@ const validateForm = () => { | @@ -124,7 +152,7 @@ const validateForm = () => { | ||
| 124 | return !error.account && !error.password; | 152 | return !error.account && !error.password; |
| 125 | }; | 153 | }; |
| 126 | 154 | ||
| 127 | -// 登录处理(核心:补充跳转逻辑) | 155 | +// 登录处理 |
| 128 | const handleLogin = async () => { | 156 | const handleLogin = async () => { |
| 129 | if (!validateForm()) return; | 157 | if (!validateForm()) return; |
| 130 | 158 | ||
| @@ -138,24 +166,22 @@ const handleLogin = async () => { | @@ -138,24 +166,22 @@ const handleLogin = async () => { | ||
| 138 | 166 | ||
| 139 | uni.showToast({ title: '登录成功', icon: 'success', duration: 1500 }); | 167 | uni.showToast({ title: '登录成功', icon: 'success', duration: 1500 }); |
| 140 | 168 | ||
| 141 | - // 登录成功后跳转首页(优先tabBar,兼容普通页面) | 169 | + // 登录成功后跳转首页 |
| 142 | setTimeout(() => { | 170 | setTimeout(() => { |
| 143 | - // 方式1:跳tabBar首页(推荐,需在pages.json配置tabBar) | ||
| 144 | uni.switchTab({ | 171 | uni.switchTab({ |
| 145 | url: globalConfig.router.tabBarList[1].path, | 172 | url: globalConfig.router.tabBarList[1].path, |
| 146 | fail: (err) => { | 173 | fail: (err) => { |
| 147 | console.warn('tabBar跳转失败,切换为普通跳转:', err); | 174 | console.warn('tabBar跳转失败,切换为普通跳转:', err); |
| 148 | - // 方式2:跳普通首页(非tabBar页面) | ||
| 149 | uni.redirectTo({ url: '/pages/workbench/index' }); | 175 | uni.redirectTo({ url: '/pages/workbench/index' }); |
| 150 | } | 176 | } |
| 151 | }); | 177 | }); |
| 152 | }, 1500); | 178 | }, 1500); |
| 153 | - | ||
| 154 | } catch (err) { | 179 | } catch (err) { |
| 155 | console.error('登录失败详情:', err); | 180 | console.error('登录失败详情:', err); |
| 156 | - const errorMsg = err.message === '网络异常,请稍后重试' | ||
| 157 | - ? '网络异常,请稍后重试' | ||
| 158 | - : err.message || '登录失败,请检查账号密码'; | 181 | + const errorMsg = |
| 182 | + err.message === '网络异常,请稍后重试' | ||
| 183 | + ? '网络异常,请稍后重试' | ||
| 184 | + : err.message || '登录失败,请检查账号密码'; | ||
| 159 | uni.showToast({ | 185 | uni.showToast({ |
| 160 | title: errorMsg, | 186 | title: errorMsg, |
| 161 | icon: 'none', | 187 | icon: 'none', |
| @@ -168,39 +194,151 @@ const handleLogin = async () => { | @@ -168,39 +194,151 @@ const handleLogin = async () => { | ||
| 168 | </script> | 194 | </script> |
| 169 | 195 | ||
| 170 | <style scoped lang="scss"> | 196 | <style scoped lang="scss"> |
| 197 | +// 核心布局 | ||
| 171 | .login-page { | 198 | .login-page { |
| 172 | min-height: 100vh; | 199 | min-height: 100vh; |
| 173 | - background-color: #f5f5f5; | 200 | + position: relative; |
| 174 | padding: 40rpx 30rpx; | 201 | padding: 40rpx 30rpx; |
| 175 | box-sizing: border-box; | 202 | box-sizing: border-box; |
| 203 | + overflow: hidden; // 隐藏动效溢出 | ||
| 204 | + | ||
| 205 | + // 适配小程序安全区 | ||
| 206 | + /* #ifdef MP-WEIXIN */ | ||
| 207 | + padding-bottom: constant(safe-area-inset-bottom); | ||
| 208 | + padding-bottom: env(safe-area-inset-bottom); | ||
| 209 | + /* #endif */ | ||
| 210 | +} | ||
| 211 | + | ||
| 212 | +// 纯CSS渐变动效背景(替代粒子,兼容所有版本) | ||
| 213 | +.bg-animation { | ||
| 214 | + position: absolute; | ||
| 215 | + top: 0; | ||
| 216 | + left: 0; | ||
| 217 | + width: 100%; | ||
| 218 | + height: 100%; | ||
| 219 | + z-index: 0; | ||
| 220 | + // 基础渐变底色 | ||
| 221 | + background: linear-gradient(120deg, #f0f9ff 0%, #e6f7ff 100%); | ||
| 222 | + // 伪粒子动效(多个透明渐变圆缓慢移动) | ||
| 223 | + &::before, | ||
| 224 | + &::after { | ||
| 225 | + content: ''; | ||
| 226 | + position: absolute; | ||
| 227 | + border-radius: 50%; | ||
| 228 | + background: rgba(59, 130, 246, 0.05); | ||
| 229 | + animation: float 20s infinite linear; | ||
| 230 | + } | ||
| 231 | + | ||
| 232 | + &::before { | ||
| 233 | + width: 600rpx; | ||
| 234 | + height: 600rpx; | ||
| 235 | + top: -200rpx; | ||
| 236 | + left: -200rpx; | ||
| 237 | + } | ||
| 238 | + | ||
| 239 | + &::after { | ||
| 240 | + width: 800rpx; | ||
| 241 | + height: 800rpx; | ||
| 242 | + bottom: -300rpx; | ||
| 243 | + right: -300rpx; | ||
| 244 | + animation-delay: -10s; // 错开动画时间 | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + // 额外小动效点(模拟粒子) | ||
| 248 | + & > view { | ||
| 249 | + position: absolute; | ||
| 250 | + width: 400rpx; | ||
| 251 | + height: 400rpx; | ||
| 252 | + border-radius: 50%; | ||
| 253 | + background: rgba(59, 130, 246, 0.03); | ||
| 254 | + top: 50%; | ||
| 255 | + left: 50%; | ||
| 256 | + transform: translate(-50%, -50%); | ||
| 257 | + animation: float 15s infinite linear reverse; | ||
| 258 | + } | ||
| 259 | +} | ||
| 260 | + | ||
| 261 | +// 浮动动画(模拟粒子运动) | ||
| 262 | +@keyframes float { | ||
| 263 | + 0% { | ||
| 264 | + transform: translate(0, 0) rotate(0deg); | ||
| 265 | + } | ||
| 266 | + 25% { | ||
| 267 | + transform: translate(50rpx, 30rpx) rotate(90deg); | ||
| 268 | + } | ||
| 269 | + 50% { | ||
| 270 | + transform: translate(0, 60rpx) rotate(180deg); | ||
| 271 | + } | ||
| 272 | + 75% { | ||
| 273 | + transform: translate(-50rpx, 30rpx) rotate(270deg); | ||
| 274 | + } | ||
| 275 | + 100% { | ||
| 276 | + transform: translate(0, 0) rotate(360deg); | ||
| 277 | + } | ||
| 176 | } | 278 | } |
| 177 | 279 | ||
| 280 | +// 登录表单(悬浮层) | ||
| 178 | .login-form { | 281 | .login-form { |
| 179 | - background-color: #fff; | ||
| 180 | - padding: 40rpx 30rpx; | ||
| 181 | - border-radius: 12rpx; | ||
| 182 | - box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); | 282 | + position: relative; |
| 283 | + z-index: 10; | ||
| 284 | + background-color: rgba(255, 255, 255, 0.95); | ||
| 285 | + padding: 60rpx 40rpx; | ||
| 286 | + border-radius: 16rpx; | ||
| 287 | + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08); | ||
| 288 | + max-width: 600rpx; | ||
| 289 | + margin: 0 auto; | ||
| 290 | + margin-top: 100rpx; | ||
| 291 | + | ||
| 292 | + // 登录标题 | ||
| 293 | + .login-title { | ||
| 294 | + font-size: 36rpx; | ||
| 295 | + font-weight: 600; | ||
| 296 | + color: #111827; | ||
| 297 | + text-align: center; | ||
| 298 | + margin-bottom: 40rpx; | ||
| 299 | + letter-spacing: 2rpx; | ||
| 300 | + } | ||
| 183 | } | 301 | } |
| 184 | 302 | ||
| 303 | +// 表单项 | ||
| 185 | .form-item { | 304 | .form-item { |
| 186 | margin-bottom: 30rpx; | 305 | margin-bottom: 30rpx; |
| 187 | position: relative; | 306 | position: relative; |
| 188 | -} | ||
| 189 | 307 | ||
| 190 | -.error-tip { | ||
| 191 | - font-size: 24rpx; | ||
| 192 | - color: #f56c6c; | ||
| 193 | - margin-top: 10rpx; | ||
| 194 | - line-height: 1.2; | 308 | + // 错误提示 |
| 309 | + .error-tip { | ||
| 310 | + font-size: 24rpx; | ||
| 311 | + color: #ef4444; | ||
| 312 | + margin-top: 10rpx; | ||
| 313 | + line-height: 1.2; | ||
| 314 | + padding-left: 10rpx; | ||
| 315 | + } | ||
| 195 | } | 316 | } |
| 196 | 317 | ||
| 318 | +// 登录按钮 | ||
| 197 | .login-btn { | 319 | .login-btn { |
| 198 | width: 100%; | 320 | width: 100%; |
| 199 | - height: 88rpx; | ||
| 200 | - line-height: 88rpx; | ||
| 201 | - font-size: 32rpx; | ||
| 202 | - border-radius: 44rpx; | ||
| 203 | - background-color: #409eff; | ||
| 204 | - margin-top: 10rpx; | 321 | + margin-top: 20rpx; |
| 322 | + | ||
| 323 | + // 按钮点击反馈 | ||
| 324 | + &:active { | ||
| 325 | + transform: scale(0.98); | ||
| 326 | + transition: transform 0.1s ease; | ||
| 327 | + } | ||
| 328 | +} | ||
| 329 | + | ||
| 330 | +// 覆盖uview默认样式 | ||
| 331 | +:deep(.up-input) { | ||
| 332 | + border-radius: 8rpx !important; | ||
| 333 | + height: 80rpx !important; | ||
| 334 | + line-height: 80rpx !important; | ||
| 335 | +} | ||
| 336 | + | ||
| 337 | +:deep(.up-input__prefix) { | ||
| 338 | + margin-right: 15rpx !important; | ||
| 339 | +} | ||
| 340 | + | ||
| 341 | +:deep(.up-button__loading) { | ||
| 342 | + color: #fff !important; | ||
| 205 | } | 343 | } |
| 206 | </style> | 344 | </style> |
| 207 | \ No newline at end of file | 345 | \ No newline at end of file |
pages/workbench/index.vue
| 1 | <template> | 1 | <template> |
| 2 | + <!-- 外层容器:包含蓝色块 + 原始内容 --> | ||
| 2 | <view class="workbench-container"> | 3 | <view class="workbench-container"> |
| 3 | - <view class="card"> | ||
| 4 | - <view class="card-title">日常管理</view> | ||
| 5 | - <view class="menu-grid"> | ||
| 6 | - <view class="menu-item" v-for="item in dailyMenuList" :key="item.id" @click="handleMenuClick(item)"> | ||
| 7 | - <image class="menu-icon" :src="item.icon || '/static/imgs/logo.png'" mode="aspectFit"></image> | ||
| 8 | - <view class="menu-text">{{ item.name }}</view> | ||
| 9 | - </view> | ||
| 10 | - </view> | ||
| 11 | - </view> | 4 | + <!-- 蓝色装饰块:#577ee3 --> |
| 5 | + <view class="blue-decor-block"></view> | ||
| 12 | 6 | ||
| 13 | - <view class="card"> | ||
| 14 | - <view class="card-title">问题管理</view> | ||
| 15 | - <view class="menu-grid"> | ||
| 16 | - <view class="menu-item" v-for="item in problemMenuList" :key="item.id" @click="handleMenuClick(item)"> | ||
| 17 | - <image class="menu-icon" :src="item.icon || '/static/imgs/logo.png'" mode="aspectFit"></image> | ||
| 18 | - <view class="menu-text">{{ item.name }}</view> | ||
| 19 | - </view> | ||
| 20 | - </view> | ||
| 21 | - </view> | 7 | + <!-- 原始内容容器:仅添加定位和顶部偏移实现压边 --> |
| 8 | + <view class="content-wrap"> | ||
| 9 | + <!-- uview-plus空状态组件 --> | ||
| 10 | + <u-empty | ||
| 11 | + v-if="!moduleList.length" | ||
| 12 | + mode="list" | ||
| 13 | + text="暂无菜单数据" | ||
| 14 | + color="#999" | ||
| 15 | + font-size="28rpx" | ||
| 16 | + ></u-empty> | ||
| 17 | + | ||
| 18 | + <!-- 菜单卡片列表(修复header插槽语法,恢复标题显示) --> | ||
| 19 | + <view v-else class="menu-card-wrap"> | ||
| 20 | + | ||
| 21 | + <up-card | ||
| 22 | + :title-size="18" | ||
| 23 | + v-for="(parentModule, index) in moduleList" | ||
| 24 | + :key="parentModule.id" | ||
| 25 | + :title="parentModule.name" | ||
| 26 | + > | ||
| 22 | 27 | ||
| 23 | - <view class="card"> | ||
| 24 | - <view class="card-title">数据管理</view> | ||
| 25 | - <view class="menu-grid"> | ||
| 26 | - <view class="menu-item" v-for="item in dataMenuList" :key="item.id" @click="handleMenuClick(item)"> | ||
| 27 | - <image class="menu-icon" :src="item.icon || '/static/imgs/logo.png'" mode="aspectFit"></image> | ||
| 28 | - <view class="menu-text">{{ item.name }}</view> | ||
| 29 | - </view> | 28 | + <template #body> |
| 29 | + <view> | ||
| 30 | + <up-grid | ||
| 31 | + :border="false" | ||
| 32 | + col="4" | ||
| 33 | + > | ||
| 34 | + <up-grid-item | ||
| 35 | + v-for="(listItem,listIndex) in parentModule.children" | ||
| 36 | + :key="listItem.id" | ||
| 37 | + @click="handleMenuClick(listItem)" | ||
| 38 | + > | ||
| 39 | + <u-image | ||
| 40 | + :src="listItem.icon " | ||
| 41 | + mode="aspectFit" | ||
| 42 | + width="80rpx" | ||
| 43 | + height="80rpx" | ||
| 44 | + lazy-load | ||
| 45 | + radius="16rpx" | ||
| 46 | + ></u-image> | ||
| 47 | + <text class="grid-text">{{ listItem.name }}</text> | ||
| 48 | + </up-grid-item> | ||
| 49 | + </up-grid> | ||
| 50 | + <up-toast ref="uToastRef"/> | ||
| 51 | + </view> | ||
| 52 | + </template> | ||
| 53 | + </up-card> | ||
| 30 | </view> | 54 | </view> |
| 31 | </view> | 55 | </view> |
| 32 | </view> | 56 | </view> |
| 33 | </template> | 57 | </template> |
| 34 | 58 | ||
| 35 | -<script setup> | ||
| 36 | -import { ref } from 'vue'; | ||
| 37 | -// 关键:导入 uni-app 页面生命周期钩子 | ||
| 38 | -import { onLoad } from '@dcloudio/uni-app'; | ||
| 39 | -import { useUserStore } from '@/pinia/user'; | ||
| 40 | -import { getDailyManageMenu, getProblemManageMenu } from '@/api/workbench'; | 59 | +<script setup lang="ts"> |
| 60 | +// 原始代码完全保留 | ||
| 61 | +import {ref, nextTick} from 'vue'; | ||
| 62 | +import {onShow} from '@dcloudio/uni-app'; | ||
| 63 | +import {useUserStore} from '@/pinia/user'; | ||
| 64 | + | ||
| 65 | +interface MenuItem { | ||
| 66 | + id: number; | ||
| 67 | + name: string; | ||
| 68 | + subtitle?: string; | ||
| 69 | + type: number; | ||
| 70 | + sort: number; | ||
| 71 | + parentId: number; | ||
| 72 | + icon: string; | ||
| 73 | + jumpUrl: string; | ||
| 74 | + miniAppId?: string; | ||
| 75 | + badgeText?: string; | ||
| 76 | + badgeColor?: string; | ||
| 77 | + statisticCount: number | null; | ||
| 78 | + hasStatistic: boolean; | ||
| 79 | + extra: any; | ||
| 80 | + children: MenuItem[]; | ||
| 81 | +} | ||
| 41 | 82 | ||
| 42 | const userStore = useUserStore(); | 83 | const userStore = useUserStore(); |
| 43 | -const dailyMenuList = ref([ | ||
| 44 | - { id: 1, name: '巡查计划', path: '/pages-sub/daily/patrol-manage/patrol-plan/index' }, | ||
| 45 | - { id: 2, name: '工单上报', path: '/pages-sub/daily/work-order/index' }, | ||
| 46 | - { id: 3, name: '快速工单', path: '/pages-sub/daily/quick-order/index' }, | ||
| 47 | - { id: 4, name: '12345工单', path: '/pages-sub/daily/12345-order/index' }, | ||
| 48 | - { id: 5, name: '巡查管理', path: '/pages-sub/daily/patrol-manage/index' }, | ||
| 49 | - { id: 6, name: '养护管理', path: '/pages-sub/daily/maintain-manage/index' } | ||
| 50 | -]); | ||
| 51 | -const problemMenuList = ref([]); | ||
| 52 | -const dataMenuList = ref([ | ||
| 53 | - { id: 1, name: '基础数据', path: '/pages-sub/data/base-data/index' }, | ||
| 54 | - { id: 2, name: '人员轨迹', path: '/pages-sub/data/personnel-track/index' }, | ||
| 55 | - { id: 3, name: '人员管理', path: '/pages-sub/data/personnel-manage/index' }, | ||
| 56 | - { id: 4, name: '行道树档案', path: '/pages-sub/data/tree-archive/index' } | ||
| 57 | -]); | ||
| 58 | - | ||
| 59 | -// 页面加载时加载菜单数据 | ||
| 60 | -onLoad(async () => { | ||
| 61 | - // await loadMenuData(); | ||
| 62 | -}); | 84 | +const moduleList = ref<MenuItem[]>([]); |
| 85 | + | ||
| 86 | +// 无用变量(保留避免警告) | ||
| 87 | +const subTitle = ref(''); | ||
| 88 | +const thumb = ref(''); | ||
| 89 | +const border = ref(true); | ||
| 90 | +const click = () => { | ||
| 91 | +}; | ||
| 92 | +const headClick = () => { | ||
| 93 | +}; | ||
| 63 | 94 | ||
| 64 | -const loadMenuData = async () => { | 95 | +onShow(async () => { |
| 65 | try { | 96 | try { |
| 66 | - const [dailyRes, problemRes] = await Promise.all([getDailyManageMenu(), getProblemManageMenu()]); | ||
| 67 | - dailyMenuList.value = dailyRes || [ | ||
| 68 | - { id: 1, name: '巡查计划', path: '/pages-sub/daily/patrol-manage/patrol-plan/index' }, | ||
| 69 | - { id: 2, name: '工单上报', path: '/pages-sub/daily/work-order/index' }, | ||
| 70 | - { id: 3, name: '快速工单', path: '/pages-sub/daily/quick-order/index' }, | ||
| 71 | - { id: 4, name: '12345工单', path: '/pages-sub/daily/12345-order/index' }, | ||
| 72 | - { id: 5, name: '巡查管理', path: '/pages-sub/daily/patrol-manage/index' }, | ||
| 73 | - { id: 6, name: '养护管理', path: '/pages-sub/daily/maintain-manage/index' } | ||
| 74 | - ]; | ||
| 75 | - problemMenuList.value = problemRes || [ | ||
| 76 | - { id: 1, name: '工单管理', path: '/pages-sub/problem/order-manage/index' }, | ||
| 77 | - { id: 2, name: '问题分配', path: '/pages-sub/problem/problem-allot/index' } | ||
| 78 | - ]; | ||
| 79 | - } catch (err) { | ||
| 80 | - console.error('加载菜单失败:', err); | ||
| 81 | - // 兜底数据 | ||
| 82 | - dailyMenuList.value = [ | ||
| 83 | - { id: 1, name: '巡查计划', path: '/pages-sub/daily/patrol-manage/patrol-plan/index' }, | ||
| 84 | - { id: 2, name: '工单上报', path: '/pages-sub/daily/work-order/index' }, | ||
| 85 | - { id: 3, name: '快速工单', path: '/pages-sub/daily/quick-order/index' }, | ||
| 86 | - { id: 4, name: '12345工单', path: '/pages-sub/daily/12345-order/index' }, | ||
| 87 | - { id: 5, name: '巡查管理', path: '/pages-sub/daily/patrol-manage/index' }, | ||
| 88 | - { id: 6, name: '养护管理', path: '/pages-sub/daily/maintain-manage/index' } | ||
| 89 | - ]; | ||
| 90 | - problemMenuList.value = [ | ||
| 91 | - { id: 1, name: '工单管理', path: '/pages-sub/problem/order-manage/index' }, | ||
| 92 | - { id: 2, name: '问题分配', path: '/pages-sub/problem/problem-allot/index' } | ||
| 93 | - ]; | 97 | + const rawMenuData = userStore.moduleListInfo as MenuItem[]; |
| 98 | + const menuData = rawMenuData || []; | ||
| 99 | + moduleList.value = menuData; | ||
| 100 | + await nextTick(); | ||
| 101 | + console.log('菜单数据:', moduleList.value); | ||
| 102 | + } catch (error) { | ||
| 103 | + console.error('获取菜单数据失败:', error); | ||
| 104 | + moduleList.value = []; | ||
| 94 | } | 105 | } |
| 95 | -}; | 106 | +}); |
| 96 | 107 | ||
| 97 | -const handleMenuClick = (item) => { | ||
| 98 | - // 权限校验 | ||
| 99 | - if (item.permission && !userStore.permissions.includes(item.permission)) { | ||
| 100 | - return uni.showToast({ title: '暂无权限', icon: 'none' }); | 108 | +const handleMenuClick = (item: MenuItem) => { |
| 109 | + if (!item.jumpUrl) { | ||
| 110 | + uni.showToast({ | ||
| 111 | + title: '暂无跳转链接', | ||
| 112 | + icon: 'none', | ||
| 113 | + duration: 2000 | ||
| 114 | + }); | ||
| 115 | + return; | ||
| 101 | } | 116 | } |
| 102 | - // 页面跳转 | ||
| 103 | - uni.navigateTo({ url: item.path }); | 117 | + console.log(item.jumpUrl) |
| 118 | + uni.navigateTo({ | ||
| 119 | + url: item.jumpUrl, | ||
| 120 | + fail: (err) => { | ||
| 121 | + console.error('页面跳转失败:', err); | ||
| 122 | + uni.showToast({ | ||
| 123 | + title: '页面路径错误', | ||
| 124 | + icon: 'none', | ||
| 125 | + duration: 2000 | ||
| 126 | + }); | ||
| 127 | + } | ||
| 128 | + }); | ||
| 104 | }; | 129 | }; |
| 105 | </script> | 130 | </script> |
| 106 | 131 | ||
| 107 | <style scoped> | 132 | <style scoped> |
| 133 | +/* 仅新增/保留必要样式,不修改卡片核心样式 */ | ||
| 108 | .workbench-container { | 134 | .workbench-container { |
| 109 | - padding: 20rpx; | ||
| 110 | - background: #f8f8f8; | 135 | + width: 100%; |
| 111 | min-height: 100vh; | 136 | min-height: 100vh; |
| 137 | + position: relative; | ||
| 138 | + background-color: #fff; | ||
| 112 | } | 139 | } |
| 113 | -.card { | ||
| 114 | - background: #fff; | ||
| 115 | - border-radius: 10rpx; | ||
| 116 | - padding: 20rpx; | ||
| 117 | - margin-bottom: 20rpx; | ||
| 118 | -} | ||
| 119 | -.card-title { | ||
| 120 | - font-size: 32rpx; | ||
| 121 | - font-weight: bold; | ||
| 122 | - margin-bottom: 20rpx; | ||
| 123 | - padding-bottom: 10rpx; | ||
| 124 | - border-bottom: 1rpx solid #eee; | 140 | + |
| 141 | +/* 蓝色块样式 */ | ||
| 142 | +.blue-decor-block { | ||
| 143 | + position: absolute; | ||
| 144 | + top: 0; | ||
| 145 | + left: 0; | ||
| 146 | + width: 100%; | ||
| 147 | + height: 200px; | ||
| 148 | + background-color: #577ee3; | ||
| 149 | + z-index: 1; | ||
| 125 | } | 150 | } |
| 126 | -.menu-grid { | ||
| 127 | - display: flex; | ||
| 128 | - flex-wrap: wrap; | ||
| 129 | - justify-content: flex-start; | 151 | + |
| 152 | +/* 内容容器样式 */ | ||
| 153 | +.content-wrap { | ||
| 154 | + position: relative; | ||
| 155 | + z-index: 2; | ||
| 156 | + padding: 0 20rpx; | ||
| 157 | + padding-top: 160px; | ||
| 130 | } | 158 | } |
| 131 | -.menu-item { | ||
| 132 | - width: 25%; | ||
| 133 | - display: flex; | ||
| 134 | - flex-direction: column; | ||
| 135 | - align-items: center; | ||
| 136 | - padding: 20rpx 10rpx; | ||
| 137 | - box-sizing: border-box; | 159 | + |
| 160 | +/* 第一张卡片层级 */ | ||
| 161 | +.first-card-position { | ||
| 162 | + position: relative; | ||
| 163 | + z-index: 3; | ||
| 138 | } | 164 | } |
| 139 | -.menu-icon { | ||
| 140 | - width: 80rpx; | ||
| 141 | - height: 80rpx; | ||
| 142 | - margin-bottom: 10rpx; | 165 | + |
| 166 | +/* 仅补充标题文字基础样式(确保显示,不修改卡片其他样式) */ | ||
| 167 | +.card-title-text { | ||
| 168 | + font-size: 32rpx; | ||
| 169 | + color: #333; | ||
| 170 | + font-weight: 600; | ||
| 143 | } | 171 | } |
| 144 | -.menu-text { | 172 | + |
| 173 | +/* 网格文字样式(保留原始) */ | ||
| 174 | +.grid-text { | ||
| 145 | font-size: 24rpx; | 175 | font-size: 24rpx; |
| 146 | - color: #666; | 176 | + color: #333; |
| 177 | + text-align: center; | ||
| 178 | + margin-top: 10rpx; | ||
| 147 | } | 179 | } |
| 148 | -</style> | ||
| 149 | -<script setup lang="ts"> | ||
| 150 | -</script> | ||
| 151 | \ No newline at end of file | 180 | \ No newline at end of file |
| 181 | +</style> | ||
| 152 | \ No newline at end of file | 182 | \ No newline at end of file |