应急抢险模块

main
严飞永 2 weeks ago
parent acd66bee0b
commit 20433f8923

@ -1,37 +1,39 @@
<script setup> <script setup>
import config from './config' import config from './config';
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth';
import { useConfigStore } from '@/store' import { useConfigStore } from '@/store';
import { getCurrentInstance } from "vue" import { getCurrentInstance } from 'vue';
import { onLaunch } from '@dcloudio/uni-app' import { onLaunch } from '@dcloudio/uni-app';
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance();
onLaunch(() => { onLaunch(() => {
initApp() initApp();
}) });
// //
function initApp() { function initApp() {
// //
initConfig() initConfig();
// //
//#ifdef H5 //#ifdef H5
checkLogin() checkLogin();
//#endif //#endif
} }
function initConfig() { function initConfig() {
useConfigStore().setConfig(config) useConfigStore().setConfig(config);
} }
function checkLogin() { function checkLogin() {
if (!getToken()) { if (!getToken()) {
proxy.$tab.reLaunch('/pages/login') proxy.$tab.reLaunch('/pages/login');
} }
} }
</script> </script>
<style lang="scss"> <style lang="scss">
@import '@/static/scss/index.scss' @import '@/static/scss/index.scss';
@import 'leaflet/dist/leaflet.css';
@import 'mars2d/mars2d.css';
</style> </style>

@ -0,0 +1,213 @@
<template>
<view class="form-container">
<!-- 工单类型 -->
<view class="form-item" style="flex-direction: column; justify-content: left; gap: 20rpx; margin-bottom: 20px">
<text class="form-label">工单类型</text>
<uni-data-checkbox v-model="formData.workOrderType" :localdata="workOrderTypes" @change="handleWorkOrderTypeChange" />
</view>
<!-- 倒伏相关选项 -->
<view class="form-item" v-if="showFallOptions">
<uni-data-checkbox v-model="formData.fallAffect" :localdata="fallAffectOptions" />
</view>
<!-- 影响类型 - 改为方形多选框 -->
<view class="form-item">
<text class="form-label">影响类型</text>
<checkbox-group @change="handleLevelChange">
<label class="checkbox-item" v-for="item in workOrderLevels" :key="item.value">
<checkbox :value="item.value" :checked="formData.workOrderLevel.includes(item.value)" />
<text>{{ item.text }}</text>
</label>
</checkbox-group>
</view>
<!-- 工单描述 -->
<view class="form-item" style="flex-direction: column; align-items: flex-start; gap: 20rpx">
<text class="form-label">工单描述</text>
<textarea
v-model="formData.description"
placeholder="请详细描述问题情况..."
style="width: 100%; min-height: 200rpx; padding: 20rpx; border: 1px solid #eee; border-radius: 8rpx"
/>
</view>
<!-- 工单图片 -->
<view class="form-item" style="margin-bottom: 200rpx">
<text class="form-label" style="width: 150rpx">工单图片</text>
<uni-file-picker v-model="imageValue" fileMediatype="image" mode="grid" @select="select" @progress="progress" @success="success" @fail="fail" />
</view>
<view class="bottom-btn">
<button type="default" class="back-btn" @click="handleBack">退</button>
<button type="primary" @click="handleSubmit('pending')"></button>
<button type="primary" @click="handleSubmit('processed')"></button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
//
const formData = ref({
workOrderType: '',
fallAffect: '',
workOrderLevel: [],
description: '',
images: []
});
//
const imageValue = ref([]);
//
const showFallOptions = ref(false);
//
const workOrderTypes = [
{ value: 'fall', text: '行道树/乔木' },
{ value: 'tilt', text: '花箱设施' },
{ value: 'broken', text: '建筑物' },
{ value: 'others', text: '其他' }
];
const fallAffectOptions = [
{ value: 'wire', text: '倒伏' },
{ value: 'vehicle', text: '倾斜' },
{ value: 'wall', text: '断枝' },
{ value: 'eaves', text: '其他' }
];
const workOrderLevels = [
{ value: 'wire', text: '线缆' },
{ value: 'road', text: '道路' },
{ value: 'vehicle', text: '车辆' },
{ value: 'building', text: '建筑物' },
{ value: 'others', text: '其他' }
];
//
const handleWorkOrderTypeChange = (e) => {
showFallOptions.value = formData.value.workOrderType === 'fall';
if (!showFallOptions.value) {
formData.value.fallAffect = '';
}
};
//
const handleLevelChange = (e) => {
formData.value.workOrderLevel = e.detail.value;
};
//
const select = (e) => {
console.log('选择文件:', e);
};
const progress = (e) => {
console.log('上传进度:', e);
};
const success = (e) => {
console.log('上传成功:', e);
formData.value.images.push(e.tempFilePaths[0]);
};
const fail = (e) => {
console.log('上传失败:', e);
};
//
const handleBack = () => {
uni.showModal({
title: '提示',
content: '确定要退单吗?',
success: (res) => {
if (res.confirm) {
console.log('退单操作');
}
}
});
};
const handleSubmit = (type) => {
//
if (!formData.value.workOrderType) {
uni.showToast({ title: '请选择工单类型', icon: 'none' });
return;
}
if (showFallOptions.value && !formData.value.fallAffect) {
uni.showToast({ title: '请选择倒伏状态', icon: 'none' });
return;
}
if (formData.value.workOrderLevel.length === 0) {
uni.showToast({ title: '请选择影响类型', icon: 'none' });
return;
}
if (!formData.value.description) {
uni.showToast({ title: '请输入工单描述', icon: 'none' });
return;
}
console.log('提交数据:', {
...formData.value,
submitType: type
});
uni.showToast({ title: '提交成功', icon: 'success' });
};
</script>
<style>
.form-container {
padding: 30rpx;
padding-bottom: 160rpx; /* 给底部按钮留空间 */
}
.form-item {
display: flex;
margin-bottom: 40rpx;
}
.form-label {
width: 230rpx;
font-size: 30rpx;
color: #767d9c;
}
.checkbox-item {
margin-right: 30rpx;
margin-bottom: 20rpx;
display: inline-flex;
align-items: center;
}
checkbox {
margin-right: 10rpx;
transform: scale(0.9);
}
.bottom-btn {
width: 100%;
display: flex;
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx;
background-color: #fff;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
}
.bottom-btn button {
flex: 1;
margin: 0 10rpx;
font-size: 28rpx;
}
button[type='default'] {
background-color: #f8f8f8;
color: #333;
}
</style>

@ -1,26 +1,26 @@
// 应用全局配置 // 应用全局配置
export default { export default {
baseUrl: 'https://vue.ruoyi.vip/prod-api', baseUrl: 'https://vue.ruoyi.vip/prod-api',
// baseUrl: 'http://localhost:8080', // baseUrl: 'http://221.229.220.83:9028',
// 应用信息 // 应用信息
appInfo: { appInfo: {
// 应用名称 // 应用名称
name: "ruoyi-app", name: "ruoyi-app",
// 应用版本 // 应用版本
version: "1.2.0", version: "1.2.0",
// 应用logo // 应用logo
logo: "/static/logo.png", logo: "/static/logo.png",
// 官方网站 // 官方网站
site_url: "http://ruoyi.vip", site_url: "http://ruoyi.vip",
// 政策协议 // 政策协议
agreements: [{ agreements: [{
title: "隐私政策", title: "隐私政策",
url: "https://ruoyi.vip/protocol.html" url: "https://ruoyi.vip/protocol.html"
}, },
{ {
title: "用户服务协议", title: "用户服务协议",
url: "https://ruoyi.vip/protocol.html" url: "https://ruoyi.vip/protocol.html"
} }
] ]
} }
} }

@ -0,0 +1,7 @@
{
"dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1",
"leaflet": "^1.9.4",
"mars2d": "^3.3.2"
}
}

@ -1,87 +1,103 @@
{ {
"pages": [ "pages": [{
{ "path": "pages/login",
"path": "pages/login", "style": {
"style": { "navigationBarTitleText": "登录"
"navigationBarTitleText": "登录" }
} }, {
}, { "path": "pages/register",
"path": "pages/register", "style": {
"style": { "navigationBarTitleText": "注册"
"navigationBarTitleText": "注册" }
} }, {
}, { "path": "pages/index",
"path": "pages/index", "style": {
"style": { "navigationBarTitleText": "若依移动端框架",
"navigationBarTitleText": "若依移动端框架", "navigationStyle": "custom"
"navigationStyle": "custom" }
} }, {
}, { "path": "pages/work/index",
"path": "pages/work/index", "style": {
"style": { "navigationBarTitleText": "工作台"
"navigationBarTitleText": "工作台" }
} }, {
}, { "path": "pages/mine/index",
"path": "pages/mine/index", "style": {
"style": { "navigationBarTitleText": "我的"
"navigationBarTitleText": "我的" }
} }, {
}, { "path": "pages/mine/avatar/index",
"path": "pages/mine/avatar/index", "style": {
"style": { "navigationBarTitleText": "修改头像"
"navigationBarTitleText": "修改头像" }
} }, {
}, { "path": "pages/mine/info/index",
"path": "pages/mine/info/index", "style": {
"style": { "navigationBarTitleText": "个人信息"
"navigationBarTitleText": "个人信息" }
} }, {
}, { "path": "pages/mine/info/edit",
"path": "pages/mine/info/edit", "style": {
"style": { "navigationBarTitleText": "编辑资料"
"navigationBarTitleText": "编辑资料" }
} }, {
}, { "path": "pages/mine/pwd/index",
"path": "pages/mine/pwd/index", "style": {
"style": { "navigationBarTitleText": "修改密码"
"navigationBarTitleText": "修改密码" }
} }, {
}, { "path": "pages/mine/setting/index",
"path": "pages/mine/setting/index", "style": {
"style": { "navigationBarTitleText": "应用设置"
"navigationBarTitleText": "应用设置" }
} }, {
}, { "path": "pages/mine/help/index",
"path": "pages/mine/help/index", "style": {
"style": { "navigationBarTitleText": "常见问题"
"navigationBarTitleText": "常见问题" }
} }, {
}, { "path": "pages/mine/about/index",
"path": "pages/mine/about/index", "style": {
"style": { "navigationBarTitleText": "关于我们"
"navigationBarTitleText": "关于我们" }
} }, {
}, { "path": "pages/common/webview/index",
"path": "pages/common/webview/index", "style": {
"style": { "navigationBarTitleText": "浏览网页"
"navigationBarTitleText": "浏览网页" }
} }, {
}, { "path": "pages/common/textview/index",
"path": "pages/common/textview/index", "style": {
"style": { "navigationBarTitleText": "浏览文本"
"navigationBarTitleText": "浏览文本" }
} },
}], {
"tabBar": { "path": "pages/homePage/emergency",
"color": "#000000", "style": {
"selectedColor": "#000000", "navigationBarTitleText": "应急抢险工单",
"borderStyle": "white", "navigationBarBackgroundColor": "#537CF7",
"backgroundColor": "#ffffff", "navigationBarTextStyle": "#ffffff",
"navigationStyle": "custom"
}, }
"globalStyle": { },
"navigationBarTextStyle": "black", {
"navigationBarTitleText": "RuoYi", "path": "pages/homePage/emergencyEntry",
"navigationBarBackgroundColor": "#FFFFFF" "style": {
} "navigationBarTitleText": "应急抢险工单录入",
} "navigationStyle": "custom"
}
}
],
"tabBar": {
"color": "#000000",
"selectedColor": "#000000",
"borderStyle": "white",
"backgroundColor": "#ffffff"
},
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "RuoYi",
"navigationBarBackgroundColor": "#FFFFFF"
}
}

@ -0,0 +1,498 @@
<template>
<view class="container">
<!-- 自定义标题 -->
<uni-nav-bar left-icon="left" left-text="" background-color="#537CF7" color="#ffffff" @clickLeft="goBack">
<text class="navbar-title">应急抢险工单</text>
</uni-nav-bar>
<!-- 页面内容 -->
<view class="content">
<!-- 顶部标签切换 -->
<view class="top-section">
<view class="tab" :class="{ active: activeTab === 'pending' }" @click="activeTab = 'pending'">待处理工单</view>
<view class="divider"></view>
<view class="tab" :class="{ active: activeTab === 'completed' }" @click="activeTab = 'completed'">已完成工单</view>
</view>
<!-- 内容区域 -->
<view class="content-main">
<!-- 时间选择器 -->
<view>
<uni-datetime-picker type="daterange" v-model="dateRange" start="2023-01-01" end="2023-12-31" @change="onDateChange" placeholder="请选择时间范围" />
</view>
<!-- 工单类型选择器 -->
<view class="filter-section">
<uni-data-select v-model="value" :localdata="options" placeholder="请选择工单类型" />
<button class="custom-button" type="primary" size="default" @click="onSearch"></button>
</view>
<!-- 待处理工单 -->
<view v-if="activeTab === 'pending'" style="overflow: auto; padding-bottom: 200rpx">
<view class="order-list">
<view
class="order-item"
v-for="(item, index) in pendingOrders"
:key="index"
:class="{
'to-handle': item.status === 'toHandle',
'to-dispatch': item.status === 'toDispatch'
}"
>
<view class="status-tag" :class="item.status">
{{ item.status === 'toHandle' ? '待处理' : '待派发' }}
</view>
<view class="form-group">
<view class="item-form">
<text>工单描述</text>
<text class="item-description">{{ item.description }}</text>
</view>
<view class="item-form">
<text>工单类型</text>
<text class="item-description">{{ item.type }}</text>
</view>
<view class="item-form">
<text>工单地址</text>
<text class="item-description">{{ item.address }}</text>
</view>
<view class="item-form">
<text>录入时间</text>
<text class="item-description">{{ item.createTime }}</text>
</view>
<!-- 工单等级显示 -->
<view class="item-form">
<text>工单等级</text>
<text class="level-tag" :class="getLevelClass(item.level)">
{{ item.level }}
</text>
</view>
<view class="item-form" v-if="item.status === 'toHandle'">
<text>处理班组</text>
<text class="item-description">{{ item.team || '暂无' }}</text>
</view>
</view>
<!-- 待处理的操作 -->
<view class="actionstwo" v-if="item.status === 'toHandle'">
<view class="action-text" @click="handleTransfer(item)"></view>
<view class="divider-vertical"></view>
<view class="action-text" @click="handleTransfer(item)"></view>
</view>
<!-- 待派发的操作 -->
<view class="actionsone" v-if="item.status === 'toDispatch'">
<view class="action-text-center" @click="dispatchOrder(item)"></view>
</view>
</view>
</view>
</view>
<!-- 已完成工单 -->
<view v-if="activeTab === 'completed'">
<view class="order-list">
<view class="order-item to-completed" v-for="(item, index) in completedOrders" :key="index">
<view class="completed status-tag">已完成</view>
<view class="item-form">
<text>工单描述</text>
{{ item.description }}
</view>
<view class="item-form">
<text>工单类型</text>
{{ item.type }}
</view>
<view class="item-form">
<text>工单地址</text>
{{ item.address }}
</view>
<view class="item-form">
<text>完成时间</text>
{{ item.completeTime }}
</view>
<view class="item-form">
<text>处理结果</text>
{{ item.result }}
</view>
<!-- <view class="action-buttons">
<button class="action-btn view-detail" @click="viewDetail(item)"></button>
</view> -->
</view>
</view>
</view>
<view class="bottom-btn">
<button @click="navigateToPage"></button>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { getDicts } from '@/api/system/dict/data';
const activeTab = ref('pending');
const dateRange = ref([]);
//
const value = ref(1);
const options = ref([]);
// -
const pendingOrders = ref([
{
orderNo: 'GD20230601001',
description: '行道树发生倒伏,影响车辆和行人正常通行',
type: '行道树倒伏',
address: '徐汇区xx街道xx路',
createTime: '2023-06-01 09:30',
level: '高危险',
team: '一分公司一组',
status: 'toHandle'
},
{
orderNo: 'GD20230601002',
description: '行道树出现明显倾斜,可能存在安全隐患',
type: '行道树倾斜',
address: '徐汇区XX路与XX路交叉口',
createTime: '2023-06-01 10:15',
level: '中危险',
team: null,
status: 'toDispatch'
}
]);
// -
const completedOrders = ref([
{
orderNo: 'GD20230531001',
description: '某小区停电故障处理',
type: '电力维修',
address: 'XX小区配电室',
createTime: '2023-05-31 08:20',
completeTime: '2023-05-31 10:45',
level: '紧急',
team: '电力维修组',
result: '已修复,恢复正常供电'
}
]);
//
const getLevelClass = (level) => {
const levelMap = {
高危险: 'high-risk',
中危险: 'medium-risk',
紧急: 'urgent'
};
return levelMap[level] || '';
};
//
const goBack = () => {
uni.navigateBack();
};
//
const fetchDicts = async () => {
const response = await getDicts('gdlx');
options.value = response.data.map((item) => ({
value: item.dictValue,
text: item.dictLabel
}));
};
//
onMounted(() => {
fetchDicts();
});
const onDateChange = (e) => {
console.log('时间范围变更:', e);
};
const onSearch = () => {
console.log('查询条件:', {
activeTab: activeTab.value,
dateRange: dateRange.value
});
};
//
const dispatchOrder = (order) => {
uni.showToast({
title: `派发工单 ${order.orderNo}`,
icon: 'none'
});
};
const handleOrder = (order) => {
uni.showToast({
title: `开始处理 ${order.orderNo}`,
icon: 'none'
});
};
const viewDetail = (order) => {
uni.showToast({
title: `查看详情 ${order.orderNo}`,
icon: 'none'
});
};
//
const navigateToPage = () => {
uni.navigateTo({
url: '/pages/homePage/emergencyEntry'
});
};
</script>
<style scoped>
.container {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
}
/* 自定义导航栏标题字体 */
.navbar-title {
font-family: 'Alimama ShuHeiTi-Bold';
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
}
/* 内容区域(自动扩展剩余空间) */
.content {
flex: 1;
overflow-y: auto;
background-color: #ffffff;
}
/* 标签切换样式 */
.top-section {
display: flex;
height: 90rpx;
background-color: #f5f8fd;
font-size: 32rpx;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
font-family: 'MiSans-Regular';
}
/* 分割线 */
.divider {
width: 1rpx;
background-color: #dbe5f4;
height: 32rpx;
align-self: center;
}
.tab {
flex: 1;
text-align: center;
padding: 20rpx;
color: black;
}
.tab.active {
color: #537cf7;
}
/* 主要内容区域 */
.content-main {
padding: 24rpx;
}
/* 工单类型选择区域 */
.filter-section {
display: flex;
margin-top: 20rpx;
gap: 5%;
}
.custom-button {
width: 38%;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
line-height: normal;
font-size: 32rpx;
background-color: #566dff;
}
/* 工单列表样式 */
.order-list {
margin-top: 25rpx;
display: flex;
flex-direction: column;
gap: 35rpx;
width: 100%;
margin-bottom: 20rpx;
}
.order-item {
width: 100%;
height: 550rpx;
border-radius: 25rpx;
display: flex;
flex-direction: column;
position: relative;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
/* 状态标签样式 */
.status-tag {
width: 27%;
padding: 16rpx;
border-top-left-radius: 25rpx;
border-bottom-right-radius: 120rpx;
font-size: 30rpx;
margin-bottom: 15rpx;
color: white;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
padding-left: 30rpx;
}
.to-handle .status-tag {
background-color: #eb4646;
}
.to-dispatch .status-tag {
background-color: #faad14;
}
.completed {
background-color: #52c41a;
}
/* 待处理工单背景 */
.to-handle {
background: linear-gradient(to bottom, #ffdddd, #ffffff);
}
/* 待派发工单背景 */
.to-dispatch {
background: linear-gradient(to bottom, #fff3dd, #ffffff);
}
/* 已完成工单背景 */
.to-completed {
background: linear-gradient(to bottom, #ddffdd, #ffffff);
}
/* 工单等级标签基础样式 */
.level-tag {
display: inline-block;
padding: 2rpx 16rpx;
width: 120rpx;
border-radius: 25rpx;
text-align: center;
color: white !important;
font-size: 28rpx;
margin-left: 10rpx;
}
/* 高危险(红色) */
.level-tag.high-risk {
background-color: #eb4646;
}
/* 中危险(黄色) */
.level-tag.medium-risk {
background-color: #faad14;
}
/* 紧急(深红色) */
.level-tag.urgent {
background-color: #ff4d4f;
}
/* 工单项样式 */
.form-group {
margin-top: 10rpx;
}
.item-form {
display: flex; /* 使用flex布局保持同行 */
align-items: center; /* 垂直居中 */
margin: 8rpx 0;
font-size: 32rpx;
line-height: 1.5;
padding-left: 30rpx;
font-family: 'MiSans-Regular';
overflow: hidden; /* 隐藏溢出内容 */
white-space: nowrap; /* 禁止换行 */
}
.item-form text:first-child {
color: #767d9c;
font-family: 'MiSans-Regular';
flex-shrink: 0; /* 防止标签被压缩 */
}
/* 内容部分 */
.item-description {
flex: 1; /* 占据剩余空间 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding-left: 10rpx; /* 和标签保持间距 */
}
/* 操作文字通用样式 */
.action-text,
.action-text-center {
color: #566dff;
font-size: 30rpx;
text-align: center;
width: 100%;
font-family: 'MiSans-Regular';
}
/* 左右分开的文字容器 */
.actionstwo {
display: flex;
justify-content: space-between;
align-items: center;
margin: 30rpx;
gap: 20rpx;
margin-top: 50rpx;
font-family: 'MiSans-Regular';
}
/* 居中的文字容器 */
.actionsone {
display: flex;
justify-content: center;
margin: 30rpx;
margin-top: 80rpx;
}
/* 垂直分隔线 */
.divider-vertical {
width: 2rpx;
height: 40rpx;
background-color: #566dff;
opacity: 0.5;
}
/* 居中的文字样式 */
.action-text-center {
text-align: center;
}
/* 已完成的工单 */
.view-detail {
background-color: #52c41a;
}
.bottom-btn {
margin-top: 60rpx;
}
.bottom-btn button {
color: #ffffff;
background-color: #566dff;
height: 76rpx;
font-size: 30rpx;
}
</style>

@ -0,0 +1,408 @@
<template>
<view class="container">
<!-- 自定义标题 -->
<uni-nav-bar left-icon="left" left-text="" background-color="#537CF7" color="#ffffff" @clickLeft="goBack">
<text class="navbar-title">应急抢险工单录入</text>
</uni-nav-bar>
<!-- 地图容器 -->
<view class="map-container" id="map"></view>
<!-- 搜索框 -->
<view class="top-container">
<image src="/static/images/地图icon.png" mode=""></image>
<view class="search-container">
<uni-easyinput v-model="keyword" placeholder="请在地图上选择点位或手动输入" class="search-input" @confirm="toSearch" @input="handleInput"></uni-easyinput>
<!-- 搜索结果列表 -->
<view class="search-results" v-if="searchBox && searchList.length > 0">
<view class="search-item" v-for="(item, index) in searchList" :key="index" @click="centerMap(item)">
<view class="item-name">{{ item.name }}</view>
<view class="item-address">{{ item.address }}</view>
</view>
</view>
</view>
</view>
<!-- 表单 -->
<view class="form-container">
<emergencyForm />
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import 'mars2d/mars2d.css';
import * as mars2d from 'mars2d';
import emergencyForm from '/components/emergencyForm';
const map = ref(null);
const selectedMarker = ref(null);
const pointForm = ref({
name: '',
lat: '',
lng: '',
address: ''
});
const keyword = ref('');
const searchList = ref([]);
const searchBox = ref(false);
const gaodeKey = 'bd665f6310bb41cdaea4494ec86fcbfa';
const searchDebounce = ref(null);
//
function goBack() {
uni.navigateBack();
}
//
function initMap() {
map.value = new mars2d.Map('map', {
zoom: 17,
copyright: false,
center: { lng: 121.438097, lat: 31.199193 },
basemaps: [{ name: '高德地图', type: 'gaode', layer: 'vec', show: true }],
graphicLayer: {
id: 'graphicLayer',
isAutoAdd: true
}
});
map.value.on('ready', () => {
setTimeout(() => {
if (map.value) map.value.invalidateSize();
}, 100);
});
//
const center = { lat: 31.199193, lng: 121.438097 };
addDefaultMarker(center);
reverseGeocode(center).then(() => {
pointForm.value = {
name: pointForm.value.address,
lat: center.lat,
lng: center.lng,
address: pointForm.value.address
};
keyword.value = pointForm.value.address;
});
//
map.value.on('click', (event) => {
selectedMarker.value.setLatLng(event.latlng);
updatePointForm(event.latlng);
reverseGeocode(event.latlng).then(() => {
pointForm.value.name = pointForm.value.address;
keyword.value = pointForm.value.address;
});
});
}
function addDefaultMarker(latlng) {
selectedMarker.value = new mars2d.graphic.Marker({
latlng: [latlng.lat, latlng.lng],
style: {
image: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
width: 20,
height: 30,
draggable: true
}
});
map.value.graphicLayer.addGraphic(selectedMarker.value);
//
selectedMarker.value.on('dragend', (event) => {
const newLatLng = event.target.getLatLng();
updatePointForm(newLatLng);
reverseGeocode(newLatLng).then(() => {
pointForm.value.name = pointForm.value.address;
keyword.value = pointForm.value.address;
});
});
}
function updatePointForm(latlng) {
pointForm.value.lat = latlng.lat;
pointForm.value.lng = latlng.lng;
}
function submitLocation() {
if (!pointForm.value.address) {
uni.showToast({ title: '请选择一个地址', icon: 'none' });
return;
}
uni.showToast({ title: '提交成功', icon: 'success' });
console.log('提交地址信息:', pointForm.value);
}
function handleInput() {
if (searchDebounce.value) clearTimeout(searchDebounce.value);
searchDebounce.value = setTimeout(() => {
toSearch();
}, 300);
}
function toSearch() {
if (!keyword.value.trim()) {
searchList.value = [];
searchBox.value = false;
return;
}
const url = `https://restapi.amap.com/v3/place/text?key=${gaodeKey}&keywords=${keyword.value}&city=亳州市`;
uni.request({
url,
success: (res) => {
if (res.data.pois && res.data.pois.length > 0) {
searchList.value = res.data.pois.map((poi) => ({
name: poi.name,
address: poi.address,
location: poi.location
}));
searchBox.value = true;
} else {
uni.showToast({ title: '未找到相关地点', icon: 'none' });
}
},
fail: () => {
uni.showToast({ title: '搜索失败,请稍后重试', icon: 'none' });
}
});
}
function centerMap(item) {
const [lng, lat] = item.location.split(',').map(Number);
//
map.value.flyTo([lat, lng], 17);
//
if (selectedMarker.value) {
map.value.graphicLayer.removeGraphic(selectedMarker.value);
}
//
selectedMarker.value = new mars2d.graphic.Marker({
latlng: [lat, lng],
style: {
image: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
width: 32,
height: 44,
draggable: true
}
});
map.value.graphicLayer.addGraphic(selectedMarker.value);
//
selectedMarker.value.on('dragend', (event) => {
const newLatLng = event.target.getLatLng();
updatePointForm(newLatLng);
reverseGeocode(newLatLng).then(() => {
pointForm.value.name = pointForm.value.address;
keyword.value = pointForm.value.address;
});
});
//
pointForm.value = {
name: item.name,
lat: lat,
lng: lng,
address: item.address
};
//
keyword.value = item.name;
//
searchBox.value = false;
//
map.value.off('click');
map.value.on('click', (event) => {
selectedMarker.value.setLatLng(event.latlng);
updatePointForm(event.latlng);
reverseGeocode(event.latlng).then(() => {
pointForm.value.name = pointForm.value.address;
keyword.value = pointForm.value.address;
});
});
}
function reverseGeocode(latlng) {
return new Promise((resolve, reject) => {
const url = `https://restapi.amap.com/v3/geocode/regeo?key=${gaodeKey}&location=${latlng.lng},${latlng.lat}`;
uni.request({
url,
success: (res) => {
if (res.data.status === '1') {
pointForm.value.address = res.data.regeocode.formatted_address;
resolve();
} else {
reject();
}
},
fail: () => {
reject();
}
});
});
}
//
onMounted(() => {
setTimeout(() => {
initMap();
}, 100);
});
</script>
<style>
.container {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
background-color: #f5f5f5;
}
.navbar-title {
font-family: 'Alimama ShuHeiTi-Bold';
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #fff;
}
.top-container {
width: 100%;
display: flex;
align-items: center;
background-color: #fff;
gap: 20rpx;
padding-left: 30rpx;
padding-right: 20rpx;
}
.top-container image {
width: 45rpx;
height: 45rpx;
}
.search-container {
padding: 20rpx;
flex: 1;
position: relative;
display: flex;
flex-direction: column;
gap: 20rpx;
background-color: #fff;
z-index: 999;
}
.search-input {
width: 100%;
}
.search-results {
position: absolute;
top: 100%;
left: 20rpx;
width: calc(100% - 40rpx);
max-height: 300px;
overflow-y: auto;
background-color: white;
border: 1px solid #ebeef5;
border-radius: 8rpx;
box-shadow: 0 4rpx 24rpx 0 rgba(0, 0, 0, 0.1);
z-index: 1000;
}
.search-item {
padding: 20rpx 30rpx;
cursor: pointer;
border-bottom: 1px solid #ebeef5;
}
.search-item:last-child {
border-bottom: none;
}
.search-item:hover {
background-color: #f5f7fa;
}
.item-name {
font-weight: bold;
margin-bottom: 10rpx;
}
.item-address {
color: #909399;
font-size: 24rpx;
}
.map-container {
width: 100%;
height: 800rpx;
background-color: #e6e6e6;
overflow: hidden;
position: relative;
z-index: 1;
}
.button-container {
padding: 20rpx;
background-color: #fff;
margin-top: 20rpx;
}
.selected-location {
padding: 20rpx;
background-color: #fff;
margin: 20rpx;
border-radius: 8rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.location-item {
margin-bottom: 15rpx;
font-size: 28rpx;
display: flex;
align-items: center;
}
.back-btn {
background-color: #f8f8f8;
color: #333;
position: relative;
}
/* .back-btn::after {
content: '';
position: absolute;
right: -10rpx;
top: 50%;
transform: translateY(-50%);
height: 60%;
width: 1rpx;
background-color: #ddd;
} */
button[type='primary'] {
margin-left: 20rpx;
}
.label {
font-weight: bold;
width: 120rpx;
color: #666;
}
.form-container {
height: 100%;
padding: 5rpx 20rpx;
overflow: auto;
background-color: #fff;
}
</style>

@ -13,7 +13,7 @@
</view> </view>
<view class="menu-list"> <view class="menu-list">
<view class="menu-item" v-for="(item,index) in menuList" :key="index"> <view class="menu-item" v-for="(item,index) in menuList" :key="index" @click="navigateTo(item.path)">
<view class="icon-name"> <view class="icon-name">
<view class="icon"> <view class="icon">
<image :src="`/static/images/${item.icon}.png`" class="image-icon" mode="aspectFit"></image> <image :src="`/static/images/${item.icon}.png`" class="image-icon" mode="aspectFit"></image>
@ -34,7 +34,8 @@
const menuList = [{ const menuList = [{
name: "应急抢险", name: "应急抢险",
icon:'yj-icon', icon:'yj-icon',
bg:'yj-bg' bg:'yj-bg',
path:'/pages/homePage/emergency'
}, },
{ {
name: "绿化养护", name: "绿化养护",
@ -51,7 +52,16 @@
icon:'zy-icon', icon:'zy-icon',
bg:'zy-bg' bg:'zy-bg'
}, },
] ];
const navigateTo = (path) => {
if (path) {
uni.navigateTo({
url: path
});
} else {
console.warn("未配置跳转路径");
}
};
</script> </script>
<style> <style>

@ -1,203 +1,205 @@
<template> <template>
<view class="normal-login-container"> <view class="normal-login-container">
<view class="logo-content align-center justify-center flex"> <view class="logo-content align-center justify-center flex">
<image style="width: 100rpx;height: 100rpx;" :src="globalConfig.appInfo.logo" mode="widthFix"> <image style="width: 100rpx; height: 100rpx" :src="globalConfig.appInfo.logo" mode="widthFix"></image>
</image> <text class="title">若依移动端登录</text>
<text class="title">若依移动端登录</text> </view>
</view> <view class="login-form-content">
<view class="login-form-content"> <view class="input-item flex align-center">
<view class="input-item flex align-center"> <view class="iconfont icon-user icon"></view>
<view class="iconfont icon-user icon"></view> <input v-model="loginForm.username" class="input" type="text" placeholder="请输入账号" maxlength="30" />
<input v-model="loginForm.username" class="input" type="text" placeholder="请输入账号" maxlength="30" /> </view>
</view> <view class="input-item flex align-center">
<view class="input-item flex align-center"> <view class="iconfont icon-password icon"></view>
<view class="iconfont icon-password icon"></view> <input v-model="loginForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20" />
<input v-model="loginForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20" /> </view>
</view> <view class="input-item flex align-center" style="width: 60%; margin: 0px" v-if="captchaEnabled">
<view class="input-item flex align-center" style="width: 60%;margin: 0px;" v-if="captchaEnabled"> <view class="iconfont icon-code icon"></view>
<view class="iconfont icon-code icon"></view> <input v-model="loginForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4" />
<input v-model="loginForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4" /> <view class="login-code">
<view class="login-code"> <image :src="codeUrl" @click="getCode" class="login-code-img"></image>
<image :src="codeUrl" @click="getCode" class="login-code-img"></image> </view>
</view> </view>
</view> <view class="action-btn">
<view class="action-btn"> <button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button>
<button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button> </view>
</view> <view class="reg text-center" v-if="register">
<view class="reg text-center" v-if="register"> <text class="text-grey1">没有账号</text>
<text class="text-grey1">没有账号</text> <text @click="handleUserRegister" class="text-blue">立即注册</text>
<text @click="handleUserRegister" class="text-blue">立即注册</text> </view>
</view> <view class="xieyi text-center">
<view class="xieyi text-center"> <text class="text-grey1">登录即代表同意</text>
<text class="text-grey1">登录即代表同意</text> <text @click="handleUserAgrement" class="text-blue">用户协议</text>
<text @click="handleUserAgrement" class="text-blue">用户协议</text> <text @click="handlePrivacy" class="text-blue">隐私协议</text>
<text @click="handlePrivacy" class="text-blue">隐私协议</text> </view>
</view> </view>
</view> </view>
</view>
</template> </template>
<script setup> <script setup>
import { getCodeImg } from '@/api/login' import { getCodeImg } from '@/api/login';
import { useConfigStore, useUserStore } from '@/store' import { useConfigStore, useUserStore } from '@/store';
import { ref, getCurrentInstance } from "vue" import { ref, getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance();
const globalConfig = useConfigStore().config const globalConfig = useConfigStore().config;
const codeUrl = ref("") const codeUrl = ref('');
// //
const captchaEnabled = ref(true) const captchaEnabled = ref(true);
// //
const register = ref(false) const register = ref(false);
const loginForm = ref({ const loginForm = ref({
username: "admin", username: 'admin',
password: "admin123", password: 'admin123',
code: "", code: '',
uuid: "" uuid: ''
}) });
// //
function handleUserRegister() { function handleUserRegister() {
proxy.$tab.redirectTo(`/pages/register`) proxy.$tab.redirectTo(`/pages/register`);
} }
// //
function handlePrivacy() { function handlePrivacy() {
let site = globalConfig.appInfo.agreements[0] let site = globalConfig.appInfo.agreements[0];
proxy.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`) proxy.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`);
} }
// //
function handleUserAgrement() { function handleUserAgrement() {
let site = globalConfig.appInfo.agreements[1] let site = globalConfig.appInfo.agreements[1];
proxy.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`) proxy.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`);
} }
// //
function getCode() { function getCode() {
getCodeImg().then(res => { getCodeImg().then((res) => {
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled;
if (captchaEnabled.value) { if (captchaEnabled.value) {
codeUrl.value = 'data:image/gif;base64,' + res.img codeUrl.value = 'data:image/gif;base64,' + res.img;
loginForm.value.uuid = res.uuid loginForm.value.uuid = res.uuid;
} }
}) });
} }
// //
async function handleLogin() { async function handleLogin() {
if (loginForm.value.username === "") { if (loginForm.value.username === '') {
proxy.$modal.msgError("请输入账号") proxy.$modal.msgError('请输入账号');
} else if (loginForm.value.password === "") { } else if (loginForm.value.password === '') {
proxy.$modal.msgError("请输入密码") proxy.$modal.msgError('请输入密码');
} else if (loginForm.value.code === "" && captchaEnabled.value) { } else if (loginForm.value.code === '' && captchaEnabled.value) {
proxy.$modal.msgError("请输入验证码") proxy.$modal.msgError('请输入验证码');
} else { } else {
proxy.$modal.loading("登录中,请耐心等待...") proxy.$modal.loading('登录中,请耐心等待...');
pwdLogin() pwdLogin();
} }
} }
// //
async function pwdLogin() { async function pwdLogin() {
useUserStore().login(loginForm.value).then(() => { useUserStore()
proxy.$modal.closeLoading() .login(loginForm.value)
loginSuccess() .then(() => {
}).catch(() => { proxy.$modal.closeLoading();
if (captchaEnabled.value) { loginSuccess();
getCode() })
} .catch(() => {
}) if (captchaEnabled.value) {
} getCode();
}
// });
function loginSuccess(result) { }
//
useUserStore().getInfo().then(res => { //
proxy.$tab.reLaunch('/pages/index') function loginSuccess(result) {
}) //
} useUserStore()
.getInfo()
getCode() .then((res) => {
proxy.$tab.reLaunch('/pages/index');
});
}
getCode();
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
page { page {
background-color: #ffffff; background-color: #ffffff;
} }
.normal-login-container { .normal-login-container {
width: 100%; width: 100%;
.logo-content { .logo-content {
width: 100%; width: 100%;
font-size: 21px; font-size: 21px;
text-align: center; text-align: center;
padding-top: 15%; padding-top: 15%;
image { image {
border-radius: 4px; border-radius: 4px;
} }
.title { .title {
margin-left: 10px; margin-left: 10px;
} }
} }
.login-form-content { .login-form-content {
text-align: center; text-align: center;
margin: 20px auto; margin: 20px auto;
margin-top: 15%; margin-top: 15%;
width: 80%; width: 80%;
.input-item { .input-item {
margin: 20px auto; margin: 20px auto;
background-color: #f5f6f7; background-color: #f5f6f7;
height: 45px; height: 45px;
border-radius: 20px; border-radius: 20px;
.icon { .icon {
font-size: 38rpx; font-size: 38rpx;
margin-left: 10px; margin-left: 10px;
color: #999; color: #999;
} }
.input { .input {
width: 100%; width: 100%;
font-size: 14px; font-size: 14px;
line-height: 20px; line-height: 20px;
text-align: left; text-align: left;
padding-left: 15px; padding-left: 15px;
} }
}
}
.login-btn {
.login-btn { margin-top: 40px;
margin-top: 40px; height: 45px;
height: 45px; }
}
.reg {
.reg { margin-top: 15px;
margin-top: 15px; }
}
.xieyi {
.xieyi { color: #333;
color: #333; margin-top: 20px;
margin-top: 20px; }
}
.login-code {
.login-code { height: 38px;
height: 38px; float: right;
float: right;
.login-code-img {
.login-code-img { height: 38px;
height: 38px; position: absolute;
position: absolute; margin-left: 10px;
margin-left: 10px; width: 200rpx;
width: 200rpx; }
} }
} }
} }
}
</style> </style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Loading…
Cancel
Save