diff --git a/api/fad/location.js b/api/fad/location.js new file mode 100644 index 0000000..c3130f7 --- /dev/null +++ b/api/fad/location.js @@ -0,0 +1,13 @@ +import request from '@/util/oaRequest' + +// 根据经纬度获取城市 +export function getCityByLocation(latitude, longitude) { + return request({ + url: '/fadapp/location/city', + method: 'get', + params: { + latitude, + longitude + } + }) +} diff --git a/common/config.js b/common/config.js index 17531a7..e331c32 100644 --- a/common/config.js +++ b/common/config.js @@ -4,6 +4,7 @@ // const WS_URL = `ws://${BASE_HOST}:10001` const BASE_DOMAIN = '49.232.154.205:10006' + const CHAT_URL = `http://${BASE_DOMAIN}/chat` const API_URL = `http://${BASE_DOMAIN}/api` const WS_URL = `ws://${BASE_DOMAIN}/msg_gateway` diff --git a/manifest.json b/manifest.json index 2070123..375b8d3 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "name" : "德讯", - "appid" : "__UNI__D705A34", + "appid" : "__UNI__A77A477", "description" : "", "versionName" : "fad-im 5.0.0", "versionCode" : 345, @@ -26,6 +26,8 @@ "distribute" : { "android" : { "permissions" : [ + "android.permission.ACCESS_FINE_LOCATION", + "android.permission.ACCESS_COARSE_LOCATION", "", "", "", @@ -127,8 +129,8 @@ "description" : "OpenIM:由IM技术专家打造的基于 Go 实现的即时通讯(IM)项目,从服务端到客户端SDK开源即时通讯(IM)整体解决方案,可以轻松替代第三方IM云服务,打造具备聊天、社交功能的app。", "platforms" : "Android,iOS", "url" : "https://ext.dcloud.net.cn/plugin?id=6577", - "android_package_name" : "com.fad.im", - "ios_bundle_id" : "com.fad.im", + "android_package_name" : "", + "ios_bundle_id" : "", "isCloud" : true, "bought" : 1, "pid" : "6577", diff --git a/pages/workbench/reportWork/reportWork.vue b/pages/workbench/reportWork/reportWork.vue index 632311e..751fd05 100644 --- a/pages/workbench/reportWork/reportWork.vue +++ b/pages/workbench/reportWork/reportWork.vue @@ -37,43 +37,34 @@ :show-avatar="activeTab === 'all'" @card-click="handleCardClick" > - - 暂无报工数据 - - - + - - {{ title }} - 工作地点 - + + + + 已自动获取当前位置,无需手动填写 - 是否出差 @@ -81,7 +72,6 @@ - 国内/国外 @@ -89,43 +79,28 @@ - 请选择项目 - + - 报工内容 - + - 备注 - 确定 取消 - - - + + @@ -133,487 +108,100 @@ import { listProjectReport, addProjectReport, getProjectReport } from '@/api/oa/projectReport' import { listProject } from '@/api/oa/project' import { listDept } from '@/api/oa/dept' +import { getCityByLocation } from '@/api/fad/location' import ReportCard from '@/components/ReportCard/index.vue' import ReportDetail from '@/components/ReportDetail/index.vue' import { mapState } from 'vuex' export default { - components: { - ReportCard, - ReportDetail, - }, - computed: { - ...mapState('user', ['selfInfo']) - }, + components: { ReportCard, ReportDetail }, + computed: { ...mapState('user', ['selfInfo']) }, data() { return { - // 当前激活的tab - activeTab: 'all', - // 滚动区域高度 - scrollHeight: 0, - // 按钮loading - buttonLoading: false, - // 遮罩层 - loading: true, - // 总条数 - total: 0, - // 项目报工列表数据 - projectReportList: [], - // 弹出层标题 - title: "", - - // 项目列表 - projectList: [], - // 部门列表 - deptList: [], - // 表单参数 - form: {}, - // 加载更多状态 - loadMoreStatus: 'loadmore', - // 下拉刷新状态 - refreshing: false, - // 报工详情数据 - reportDetail: {}, - // 分页参数 - queryParams: { - pageNum: 1, - pageSize: 10 - }, - // 表单校验规则 - rules: { - workPlace: [ - { required: true, message: "工作地点不能为空", trigger: "blur" } - ], - projectId: [ - { required: true, message: "项目不能为空", trigger: "blur" } - ], - content: [ - { required: true, message: "报工内容不能为空", trigger: "blur" } - ], - isTrip: [ - { required: true, message: '请选择是否出差', trigger: 'change' } - ], - workType: [ - { - required: true, - message: '请选择出差地点', - trigger: 'change', - validator: (rule, value, callback) => { - if (this.form.isTrip === 1 && !value && value !== 0) { - callback(new Error('请选择出差地点')); - } else { - callback(); - } - } - } - ] - } + activeTab: 'all', scrollHeight: 0, buttonLoading: false, locationLoading: false, + locationCanEdit: false, + cachedLocation: { city: '', latitude: null, longitude: null, locationText: '' }, + loading: true, total: 0, projectReportList: [], title: '', projectList: [], deptList: [], + form: {}, loadMoreStatus: 'loadmore', refreshing: false, reportDetail: {}, + queryParams: { pageNum: 1, pageSize: 10 }, + rules: { workPlace: [{ required: true, message: '工作地点不能为空', trigger: 'blur' }], projectId: [{ required: true, message: '项目不能为空', trigger: 'blur' }], content: [{ required: true, message: '报工内容不能为空', trigger: 'blur' }], isTrip: [{ required: true, message: '请选择是否出差', trigger: 'change' }], workType: [{ required: true, message: '请选择出差地点', trigger: 'change', validator: (rule, value, callback) => { if (this.form.isTrip === 1 && !value && value !== 0) callback(new Error('请选择出差地点')); else callback(); } }] } } }, onLoad() { this.calculateScrollHeight(); - this.getList(); - }, - onReady() { - // 页面渲染完成后重新计算高度 - this.calculateScrollHeight(); + this.checkLocationPermissionAndInit(); }, + onReady() { this.calculateScrollHeight(); }, methods: { - // 计算滚动区域高度 - calculateScrollHeight() { - const systemInfo = uni.getSystemInfoSync(); - const tabHeight = 100; // tab高度 - const containerPadding = 40; // 容器padding - // 悬浮按钮是固定定位,不需要预留空间,让卡片列表到达底部 - // 我也不知道为什么要 + 80, 不然滚动高度下面一大片空白,这个不太好调试 - this.scrollHeight = systemInfo.windowHeight - tabHeight - containerPadding + 80; - }, - - // 切换tab - switchTab(tab) { - if (this.activeTab === tab) return; - this.activeTab = tab; - this.queryParams.pageNum = 1; - this.projectReportList = []; - this.getList(); - }, - - // 下拉刷新 - onRefresh() { - this.refreshing = true; - this.queryParams.pageNum = 1; - this.getList().finally(() => { - this.refreshing = false; - }); - }, - - // 获取项目列表 - getProjectList() { - listProject({ pageNum: 1, pageSize: 9999 }).then(res => { - const rawData = res.rows || []; - // 按照 uni-data-select 的标准格式处理数据 - this.projectList = rawData.map(item => ({ - value: item.projectId, - text: item.projectName || '未命名项目', - // 保留原始数据用于提交 - projectId: item.projectId, - projectName: item.projectName || '未命名项目', - projectNum: item.projectNum, - projectCode: item.projectCode - })); - console.log('处理后的项目列表数据:', this.projectList); - // 检查数据结构 - if (this.projectList.length > 0) { - console.log('第一个项目数据:', this.projectList[0]); - } - }).catch(err => { - console.error('获取项目列表失败:', err); - uni.showToast({ - title: '获取项目列表失败', - icon: 'none' - }); - }); - }, - - // 获取部门列表 - getDeptList() { - listDept().then(res => { - this.deptList = res.data || []; - }).catch(err => { - console.error('获取部门列表失败:', err); - }); - }, - - // 查询项目报工列表 - getList() { - return new Promise((resolve, reject) => { - this.loading = true; - - // 根据当前tab设置查询参数 - const params = { - ...this.queryParams - }; - - // 如果是我的报工,添加用户ID过滤 - if (this.activeTab === 'my') { - // 使用当前登录用户的ID - const oaId = uni.getStorageSync('oaId'); - if (oaId) { - params.userId = oaId; + calculateScrollHeight() { const systemInfo = uni.getSystemInfoSync(); this.scrollHeight = systemInfo.windowHeight - 100 - 40 + 80; }, + switchTab(tab) { if (this.activeTab === tab) return; this.activeTab = tab; this.queryParams.pageNum = 1; this.projectReportList = []; this.getList(); }, + onRefresh() { this.refreshing = true; this.queryParams.pageNum = 1; this.getList().finally(() => { this.refreshing = false; }); }, + checkLocationPermissionAndInit() { + if (typeof uni.getSetting !== 'function') { + this.getList(); + this.getCurrentLocation(true); + return; + } + uni.getSetting({ + success: (settingRes) => { + const auth = settingRes.authSetting || {}; + if (auth['scope.userLocation']) { + this.getList(); + this.getCurrentLocation(true); + return; } - } - - listProjectReport(params).then(response => { - if (this.queryParams.pageNum === 1) { - this.projectReportList = response.rows || []; - } else { - this.projectReportList = [...this.projectReportList, ...(response.rows || [])]; - } - this.total = response.total || 0; - this.loading = false; - - // 更新加载更多状态 - if (this.queryParams.pageNum > 1) { - this.loadMoreStatus = 'loadmore'; - } - - this.getProjectList(); - // this.getDeptList(); - resolve(response); - }).catch(err => { - console.error('获取报工列表失败:', err); - this.loading = false; - this.loadMoreStatus = 'loadmore'; - uni.showToast({ - title: '获取数据失败', - icon: 'none' + uni.showModal({ + title: '需要定位权限', + content: '报工页面必须先开启定位权限才能进入。', + confirmText: '去设置', + cancelText: '返回', + success: (res) => { + if (res.confirm && typeof uni.openSetting === 'function') { + uni.openSetting({ + success: (openRes) => { + if (openRes.authSetting && openRes.authSetting['scope.userLocation']) { + this.getList(); + this.getCurrentLocation(true); + } else { + uni.navigateBack(); + } + }, + fail: () => uni.navigateBack() + }); + } else { + uni.navigateBack(); + } + } }); - reject(err); - }); + }, + fail: () => { + uni.navigateBack(); + } }); }, - - // 新增 - handleAdd() { - this.reset(); - this.title = "添加项目报工"; - this.$refs.popup.open(); - }, - - // 卡片点击事件 - handleCardClick(item) { - console.log('点击了报工卡片:', item); - // 获取报工详情 - this.getReportDetail(item.reportId); - }, - - // 获取报工详情 - getReportDetail(reportId) { - // 先打开弹窗显示加载状态 - this.$refs.reportDetail.open(); - - getProjectReport(reportId).then(response => { - // 根据API返回的数据结构处理 - this.reportDetail = response.data || response || {}; - }).catch(err => { - console.error('获取报工详情失败:', err); - uni.showToast({ - title: '获取详情失败', - icon: 'none' - }); - // 关闭弹窗 - this.$refs.reportDetail.close(); - }); - }, - - // 提交表单 - submitForm() { - // 手动验证表单 - if (!this.form.workPlace) { - uni.showToast({ - title: '工作地点不能为空', - icon: 'none' - }); - return; - } - - if (this.form.isTrip === undefined) { - uni.showToast({ - title: '请选择是否出差', - icon: 'none' - }); - return; - } - - if (this.form.isTrip === 1 && this.form.workType === undefined) { - uni.showToast({ - title: '请选择出差地点', - icon: 'none' - }); - return; - } - - if (!this.form.projectId) { - uni.showToast({ - title: '项目不能为空', - icon: 'none' - }); - return; - } - - if (!this.form.content) { - uni.showToast({ - title: '报工内容不能为空', - icon: 'none' - }); - return; - } - - // 验证通过,提交表单 - this.buttonLoading = true; - - // 获取选中的项目信息 - const selectedProject = this.projectList.find(p => p.value === this.form.projectId); - - const submitData = { - ...this.form, - projectName: selectedProject?.projectName || '', - projectNum: selectedProject?.projectNum || '', - projectCode: selectedProject?.projectCode || null - }; - - addProjectReport(submitData).then(response => { - this.buttonLoading = false; - uni.showToast({ - title: '新增成功', - icon: 'success' - }); - this.$refs.popup.close(); - this.getList(); // 重新获取列表 - }).catch(err => { - this.buttonLoading = false; - console.error('新增失败:', err); - uni.showToast({ - title: '新增失败', - icon: 'none' - }); - }); - }, - - // 取消 - cancel() { - this.$refs.popup.close(); - this.reset(); - }, - - // 重置表单 - reset() { - this.form = { - workPlace: '', - projectId: null, - content: '', - remark: '', - isTrip: undefined, - workType: undefined - }; - }, - - // 加载更多 - loadMore() { - if (this.projectReportList.length >= this.total) { - this.loadMoreStatus = 'nomore'; - return; - } - - this.queryParams.pageNum++; - this.loadMoreStatus = 'loading'; - - this.getList(); - }, + getProjectList() { listProject({ pageNum: 1, pageSize: 9999 }).then(res => { const rawData = res.rows || []; this.projectList = rawData.map(item => ({ value: item.projectId, text: item.projectName || '未命名项目', projectId: item.projectId, projectName: item.projectName || '未命名项目', projectNum: item.projectNum, projectCode: item.projectCode })); }).catch(() => {}); }, + getDeptList() { listDept().then(res => { this.deptList = res.data || []; }).catch(() => {}); }, + getList() { return new Promise((resolve, reject) => { this.loading = true; const params = { ...this.queryParams }; if (this.activeTab === 'my') { const oaId = uni.getStorageSync('oaId'); if (oaId) params.userId = oaId; } listProjectReport(params).then(response => { this.projectReportList = this.queryParams.pageNum === 1 ? (response.rows || []) : [...this.projectReportList, ...(response.rows || [])]; this.total = response.total || 0; this.loading = false; if (this.queryParams.pageNum > 1) this.loadMoreStatus = 'loadmore'; this.getProjectList(); resolve(response); }).catch(err => { this.loading = false; this.loadMoreStatus = 'loadmore'; uni.showToast({ title: '获取数据失败', icon: 'none' }); reject(err); }); }); }, + handleAdd() { this.reset(); this.title = '添加项目报工'; if (this.cachedLocation.city) { this.form.city = this.cachedLocation.city; this.form.latitude = this.cachedLocation.latitude; this.form.longitude = this.cachedLocation.longitude; this.form.locationText = this.cachedLocation.locationText; this.form.workPlace = this.cachedLocation.city; this.locationCanEdit = false; } else { this.locationCanEdit = true; } this.$refs.popup.open(); }, + handleCardClick(item) { this.getReportDetail(item.reportId); }, + getReportDetail(reportId) { this.$refs.reportDetail.open(); getProjectReport(reportId).then(response => { this.reportDetail = response.data || response || {}; }).catch(() => { uni.showToast({ title: '获取详情失败', icon: 'none' }); this.$refs.reportDetail.close(); }); }, + submitForm() { if (!this.form.workPlace) { uni.showToast({ title: '工作地点不能为空', icon: 'none' }); return; } if (this.form.isTrip === undefined) { uni.showToast({ title: '请选择是否出差', icon: 'none' }); return; } if (this.form.isTrip === 1 && this.form.workType === undefined) { uni.showToast({ title: '请选择出差地点', icon: 'none' }); return; } if (!this.form.projectId) { uni.showToast({ title: '项目不能为空', icon: 'none' }); return; } if (!this.form.content) { uni.showToast({ title: '报工内容不能为空', icon: 'none' }); return; } this.buttonLoading = true; const selectedProject = this.projectList.find(p => p.value === this.form.projectId); const submitData = { ...this.form, projectName: selectedProject?.projectName || '', projectNum: selectedProject?.projectNum || '', projectCode: selectedProject?.projectCode || null, locationText: this.form.locationText || '' }; addProjectReport(submitData).then(() => { this.buttonLoading = false; uni.showToast({ title: '新增成功', icon: 'success' }); this.$refs.popup.close(); this.getList(); }).catch(() => { this.buttonLoading = false; uni.showToast({ title: '新增失败', icon: 'none' }); }); }, + cancel() { this.$refs.popup.close(); this.reset(); }, + getCurrentLocation(silent = false) { if (this.cachedLocation.city && this.cachedLocation.latitude && this.cachedLocation.longitude) return Promise.resolve(this.cachedLocation); if (this.locationLoading) return Promise.resolve(null); this.locationLoading = true; if (typeof uni.authorize !== 'function') return this.requestLocation(silent); uni.authorize({ scope: 'scope.userLocation', success: () => { this.requestLocation(silent); }, fail: () => { this.locationLoading = false; this.locationCanEdit = true; if (!silent) { uni.showModal({ title: '需要定位权限', content: '请先授权定位权限后再获取当前位置。', confirmText: '去设置', cancelText: '取消', success: (res) => { if (res.confirm && typeof uni.openSetting === 'function') { uni.openSetting({ success: () => { this.requestLocation(silent); } }); } } }); } } }); }, + requestLocation(silent = false) { if (typeof uni.getLocation !== 'function') { this.locationLoading = false; this.locationCanEdit = true; return Promise.resolve(null); } return new Promise((resolve) => { uni.getLocation({ type: 'wgs84', isHighAccuracy: true, highAccuracyExpireTime: 3000, success: (res) => { this.locationLoading = false; this.locationCanEdit = false; this.form.latitude = res.latitude; this.form.longitude = res.longitude; this.form.locationText = `纬度:${res.latitude}, 经度:${res.longitude}`; this.form.workPlace = this.form.locationText; this.fetchCityByLocation(res.latitude, res.longitude, silent).then(resolve); }, fail: (err) => { this.locationLoading = false; this.locationCanEdit = true; console.error('获取位置失败:', err); if (!silent) uni.showToast({ title: '获取位置失败', icon: 'none' }); resolve(null); } }); }); }, + fetchCityByLocation(latitude, longitude, silent = false) { return getCityByLocation(latitude, longitude).then(res => { const city = res?.msg || res?.data || ''; if (city) { this.cachedLocation = { city, latitude, longitude, locationText: `城市:${city}` }; this.form.city = city; this.form.workPlace = `城市:${city}`; this.form.locationText = `城市:${city}`; this.locationCanEdit = false; return city; } this.locationCanEdit = true; if (!silent) { this.form.workPlace = `纬度:${latitude}, 经度:${longitude}`; this.form.locationText = this.form.workPlace; uni.showToast({ title: '已获取定位,但未识别到城市', icon: 'none' }); } return ''; }).catch(() => { this.locationCanEdit = true; if (!silent) { this.form.workPlace = `纬度:${latitude}, 经度:${longitude}`; this.form.locationText = this.form.workPlace; uni.showToast({ title: '城市解析失败', icon: 'none' }); } return ''; }); }, + reset() { this.form = { workPlace: '', locationText: '', city: '', latitude: null, longitude: null, projectId: null, content: '', remark: '', isTrip: undefined, workType: undefined }; this.locationCanEdit = false; }, + loadMore() { if (this.projectReportList.length >= this.total) { this.loadMoreStatus = 'nomore'; return; } this.queryParams.pageNum++; this.loadMoreStatus = 'loading'; this.getList(); } } } diff --git a/util/oaRequest.js b/util/oaRequest.js index eb6537a..1cd3942 100644 --- a/util/oaRequest.js +++ b/util/oaRequest.js @@ -5,7 +5,7 @@ import { getSMSCodeFromOa, loginOaByPhone } from '../api/oa/login' let timeout = 10000 const baseUrl = 'http://49.232.154.205:18081' -// const baseUrl = 'http://localhost:8080' +//const baseUrl = 'http://192.168.31.116:8080' // 显示loading提示 const showLoading = (title = '正在登录OA系统...') => {