| | |
| | | <script setup> |
| | | import { ref, onMounted } from "vue"; |
| | | import Splash from "./components/Splash.vue"; |
| | | import { confirmMessage } from "@/api/login.js"; |
| | | |
| | | const showSplash = ref(true); |
| | | onMounted(() => { |
| | |
| | | // 处理推送消息点击事件 |
| | | const handlePushClick = msg => { |
| | | console.log("点击推送消息:", msg); |
| | | console.log("解析后:", msg.payload.noticeId); |
| | | try { |
| | | if (msg.payload.indexOf("/") === 0) { |
| | | uni.navigateTo({ |
| | | url: msg.payload, |
| | | }); |
| | | } else { |
| | | uni.navigateTo({ |
| | | url: "/" + msg.payload, |
| | | }); |
| | | } |
| | | confirmMessage(msg.payload.noticeId, 1).then(res => { |
| | | if (msg.payload.url) { |
| | | if (msg.payload.url.indexOf("/") === 0) { |
| | | uni.navigateTo({ |
| | | url: msg.payload.url, |
| | | }); |
| | | } else { |
| | | uni.navigateTo({ |
| | | url: "/" + msg.payload.url, |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | } catch (error) { |
| | | uni.showToast({ |
| | | title: "路径:" + msg.payload, |
| | |
| | | method: 'get', |
| | | params: { consigneeId } |
| | | }) |
| | | } |
| | | } |
| | | |
| | | // 标记消息为已读 |
| | | export function markAsRead(noticeId, status) { |
| | | return request({ |
| | | url: "/system/notice", |
| | | method: "put", |
| | | data: { noticeId, status }, |
| | | }); |
| | | } |
| | | |
| | | // 一键标记所有消息为已读 |
| | | export function markAllAsRead() { |
| | | return request({ |
| | | url: "/system/notice/readAll", |
| | | method: "post", |
| | | }); |
| | | } |
| | | |
| | | // 确认消息 |
| | | export function confirmMessage(noticeId, status) { |
| | | return request({ |
| | | url: "/system/notice", |
| | | method: "put", |
| | | data: { noticeId, status }, |
| | | }); |
| | | } |
| | |
| | | <u-icon name="calendar" |
| | | color="#348fe2" |
| | | size="16"></u-icon> |
| | | <text class="shift-text">白班: {{ workTimeDict.startAt }}-{{ workTimeDict.endAt }}</text> |
| | | <text class="shift-text">白班: {{ todayRecord.startAt }}-{{ todayRecord.endAt }}</text> |
| | | </view> |
| | | </view> |
| | | <!-- 打卡按钮 --> |
| | | <view class="checkin-button-container"> |
| | | <view class="checkin-button-wrapper"> |
| | | <view class="checkin-button" |
| | | :class="{ 'disabled': todayRecord.workEndAt }" |
| | | :class="{ 'disabled': todayRecord.workEndAt || noNeedCheckIn }" |
| | | @click="handleCheckInOut"> |
| | | <text class="checkin-button-text">{{ checkInOutText }}</text> |
| | | <text class="checkin-time">{{ nowTime.split(' ')[1] }}</text> |
| | |
| | | // 当前时间展示 |
| | | const nowTime = ref(""); |
| | | let timer = null; |
| | | |
| | | // 上次打卡时间 |
| | | const lastCheckInTime = ref(null); |
| | | |
| | | // 打卡冷却时间(毫秒) |
| | | const CHECKIN_COOLDOWN = 5000; |
| | | // 返回上一页 |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | |
| | | // 查询今日打卡信息 |
| | | const fetchTodayData = () => { |
| | | findTodayPersonalAttendanceRecord({}).then(res => { |
| | | todayRecord.value = res.data; |
| | | if (res.data) { |
| | | todayRecord.value = res.data; |
| | | noNeedCheckIn.value = false; |
| | | } else { |
| | | // 页面显示“无需打卡” |
| | | todayRecord.value = {}; |
| | | noNeedCheckIn.value = true; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 打卡范围状态 |
| | | const inCheckRange = ref(true); |
| | | |
| | | // 是否无需打卡 |
| | | const noNeedCheckIn = ref(false); |
| | | |
| | | // 当前位置 |
| | | const currentLocation = ref(null); |
| | |
| | | |
| | | // 打卡按钮文本 |
| | | const checkInOutText = computed(() => { |
| | | if (noNeedCheckIn.value) { |
| | | return "无需打卡"; |
| | | } |
| | | if (!todayRecord.value || !todayRecord.value.workStartAt) { |
| | | return "上班打卡"; |
| | | } |
| | |
| | | // #endif |
| | | }); |
| | | }; |
| | | const form = ref({ |
| | | longitude: "", |
| | | latitude: "", |
| | | }); |
| | | |
| | | // 获取当前位置 |
| | | const getCurrentLocation = () => { |
| | | return new Promise((resolve, reject) => { |
| | | uni.getLocation({ |
| | | type: "wgs84", |
| | | success: res => { |
| | | currentLocation.value = res; |
| | | // 模拟检查是否在打卡范围内(实际项目中应根据公司位置和允许的半径进行计算) |
| | | // 这里简单模拟为始终在范围内 |
| | | inCheckRange.value = true; |
| | | resolve(res); |
| | | }, |
| | | fail: err => { |
| | | console.error("获取位置失败:", err); |
| | | // 失败时默认允许打卡(实际项目中应根据业务需求处理) |
| | | inCheckRange.value = true; |
| | | reject(err); |
| | | }, |
| | | }); |
| | | uni.showLoading({ title: "获取位置中..." }); |
| | | |
| | | uni.getLocation({ |
| | | type: "gcj02", |
| | | success: res => { |
| | | uni.hideLoading(); |
| | | form.value.latitude = res.latitude; |
| | | form.value.longitude = res.longitude; |
| | | }, |
| | | fail: err => { |
| | | uni.hideLoading(); |
| | | console.error("获取位置失败:", err); |
| | | |
| | | // 显示错误提示并引导用户检查权限 |
| | | showToast("获取位置失败,请检查定位权限"); |
| | | |
| | | // 引导用户检查权限设置 |
| | | uni.showModal({ |
| | | title: "位置权限提示", |
| | | content: |
| | | "获取位置失败,可能是因为位置权限未开启,请在设备设置中检查并开启位置权限。", |
| | | confirmText: "知道了", |
| | | cancelText: "取消", |
| | | success: res => { |
| | | if (res.confirm) { |
| | | // 可以尝试打开设置页面(如果支持) |
| | | if (uni.openSetting) { |
| | | uni.openSetting({ |
| | | success: settingRes => { |
| | | console.log("设置结果:", settingRes); |
| | | }, |
| | | }); |
| | | } |
| | | } |
| | | }, |
| | | }); |
| | | |
| | | // 失败时显示错误信息 |
| | | form.value.visitAddress = "位置获取失败"; |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | |
| | | |
| | | // 打卡逻辑 |
| | | const handleCheckInOut = async () => { |
| | | if (noNeedCheckIn.value) { |
| | | uni.showToast({ |
| | | title: "今日无需打卡", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | if (todayRecord.value.workEndAt) { |
| | | uni.showToast({ |
| | | title: "您已经打过卡了,无需重复打卡!!!", |
| | |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // 检查是否在打卡冷却时间内 |
| | | if (lastCheckInTime.value) { |
| | | const currentTime = Date.now(); |
| | | const timeDiff = currentTime - lastCheckInTime.value; |
| | | if (timeDiff < CHECKIN_COOLDOWN) { |
| | | const remainingTime = Math.ceil((CHECKIN_COOLDOWN - timeDiff) / 1000); |
| | | uni.showToast({ |
| | | title: `请${remainingTime}秒后再试`, |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // 检查是否在打卡范围内 |
| | | if (!inCheckRange.value) { |
| | | uni.showToast({ |
| | |
| | | } |
| | | |
| | | // 调用打卡API |
| | | createPersonalAttendanceRecord({}) |
| | | createPersonalAttendanceRecord({ |
| | | ...form.value, |
| | | }) |
| | | .then(res => { |
| | | // 记录打卡时间 |
| | | lastCheckInTime.value = Date.now(); |
| | | |
| | | uni.showToast({ |
| | | title: "打卡成功!", |
| | | icon: "success", |
| | |
| | | fetchTodayData(); |
| | | }) |
| | | .catch(error => { |
| | | console.error("打卡失败:", error); |
| | | uni.showToast({ |
| | | title: error.msg || "打卡失败,请重试", |
| | | icon: "none", |
| | |
| | | |
| | | // 获取位置权限并检查位置 |
| | | try { |
| | | await getLocationPermission(); |
| | | // await getLocationPermission(); |
| | | await getCurrentLocation(); |
| | | } catch (error) { |
| | | console.error("位置权限获取失败:", error); |
| | |
| | | prop="scenario" |
| | | border-bottom> |
| | | <u-input v-model="form.scenario" |
| | | readonly="readonly" |
| | | :readonly="readonly" |
| | | placeholder="请输入适用场景" /> |
| | | </u-form-item> |
| | | <u-form-item label="解决效率" |
| | |
| | | prop="creator" |
| | | border-bottom> |
| | | <u-input v-model="form.creator" |
| | | readonly |
| | | placeholder="请选择创建人" |
| | | @click="openCreatorSheet" /> |
| | | <template v-if="!readonly" |
| | | #right> |
| | | <up-icon name="arrow-right" |
| | | @click="openCreatorSheet"></up-icon> |
| | | </template> |
| | | <!-- <u-input v-model="form.creator" |
| | | :readonly="readonly" |
| | | placeholder="请输入创建人" /> |
| | | placeholder="请输入创建人" /> --> |
| | | </u-form-item> |
| | | <u-form-item label="使用次数" |
| | | prop="usageCount" |
| | |
| | | :actions="equipmentOptions" |
| | | @select="handleEquipmentChange" |
| | | @close="showEquipmentSheet = false" /> |
| | | <up-action-sheet :show="showCreatorSheet" |
| | | :actions="creatorOptions" |
| | | @select="handleCreatorChange" |
| | | @close="showCreatorSheet = false" /> |
| | | <!-- <u-popup :show="showEquipmentSheet" |
| | | mode="bottom" |
| | | @close="showEquipmentSheet = false" |
| | |
| | | addKnowledgeBase, |
| | | updateKnowledgeBase, |
| | | } from "@/api/managementMeetings/knowledgeBase"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user"; |
| | | |
| | | const userStore = useUserStore(); |
| | | |
| | |
| | | form.value.efficiency = action.value; |
| | | statusname.value = action.name; |
| | | showStatusSheet.value = false; |
| | | }; |
| | | const showCreatorSheet = ref(false); |
| | | const creatorOptions = ref([]); |
| | | const openCreatorSheet = () => { |
| | | showCreatorSheet.value = true; |
| | | }; |
| | | const getCreatorOptions = async () => { |
| | | try { |
| | | const res = await userListNoPageByTenantId(); |
| | | if (res.code === 200) { |
| | | creatorOptions.value = res.data || []; |
| | | creatorOptions.value.forEach(item => { |
| | | item.name = item.nickName; |
| | | item.value = item.userId; |
| | | }); |
| | | } else { |
| | | showToast("获取创建人列表失败"); |
| | | } |
| | | } catch (e) { |
| | | console.error("获取创建人列表失败:", e); |
| | | showToast("获取创建人列表失败"); |
| | | } |
| | | }; |
| | | // 创建人选择 |
| | | const handleCreatorChange = val => { |
| | | form.value.creator = val.name; |
| | | }; |
| | | const equipmentname = ref(""); |
| | | // 设备配置选择 |
| | |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | getCreatorOptions(); |
| | | // 从本地存储中获取知识数据 |
| | | const knowledgeBase = uni.getStorageSync("knowledgeBase"); |
| | | if (knowledgeBase) { |
| | |
| | | </view> |
| | | <view v-else |
| | | class="no-data"> |
| | | <text>暂无会议室记录</text> |
| | | <text>暂无知识记录</text> |
| | | </view> |
| | | <!-- 浮动新增按钮 --> |
| | | <view class="fab-button" |
| | |
| | | </view> |
| | | <view v-else |
| | | class="no-data"> |
| | | <text>暂无会议室记录</text> |
| | | <text>暂无规章制度</text> |
| | | </view> |
| | | <!-- 浮动新增按钮 --> |
| | | <view class="fab-button" |
| | |
| | | </view> |
| | | <view v-else |
| | | class="no-data"> |
| | | <text>暂无会议室记录</text> |
| | | <text>暂无用印记录</text> |
| | | </view> |
| | | <!-- 浮动新增按钮 --> |
| | | <view class="fab-button" |
| | |
| | | }; |
| | | listSealApplication(params) |
| | | .then(res => { |
| | | const currentFactoryName = userStore.currentFactoryName; |
| | | if (currentFactoryName) { |
| | | visitList.value = res.data.records.filter( |
| | | item => item.department === currentFactoryName |
| | | ); |
| | | } else { |
| | | visitList.value = res.data.records; |
| | | } |
| | | // const currentFactoryName = userStore.currentFactoryName; |
| | | // if (currentFactoryName) { |
| | | // visitList.value = res.data.records.filter( |
| | | // item => item.department === currentFactoryName |
| | | // ); |
| | | // } else { |
| | | visitList.value = res.data.records; |
| | | // } |
| | | closeToast(); |
| | | }) |
| | | .catch(() => { |
| | |
| | | <text class="message-time">{{ formatTime(item.createTime) }}</text> |
| | | </view> |
| | | <text class="message-desc">{{ item.noticeContent }}</text> |
| | | <view class="message-footer"> |
| | | <view v-if="activeTab === 0" |
| | | class="message-footer"> |
| | | <text class="message-view" |
| | | @click="goToDetail(item)"> |
| | | 去查看 > |
| | | 确认消息 |
| | | </text> |
| | | </view> |
| | | </view> |
| | |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { listNotice } from "@/api/login.js"; |
| | | import { listNotice, confirmMessage } from "@/api/login.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | |
| | | // 标签页数据 |
| | |
| | | |
| | | // 跳转到详情页 |
| | | const goToDetail = item => { |
| | | if (item.appJumpPath.indexOf("/") === 0) { |
| | | uni.navigateTo({ |
| | | url: item.appJumpPath, |
| | | }); |
| | | } else { |
| | | uni.navigateTo({ |
| | | url: "/" + item.appJumpPath, |
| | | }); |
| | | } |
| | | confirmMessage(item.noticeId, 1).then(res => { |
| | | if (res.code === 200) { |
| | | // uni.showToast({ title: "确认成功", icon: "success" }); |
| | | loadMessages(false); |
| | | if (item.appJumpPath) { |
| | | if (item.appJumpPath.indexOf("/") === 0) { |
| | | uni.navigateTo({ |
| | | url: item.appJumpPath, |
| | | }); |
| | | } else { |
| | | uni.navigateTo({ |
| | | url: "/" + item.appJumpPath, |
| | | }); |
| | | } |
| | | } |
| | | } else { |
| | | uni.showToast({ title: "确认失败", icon: "none" }); |
| | | } |
| | | }); |
| | | }; |
| | | const userStore = useUserStore(); |
| | | const userId = ref(""); |
| | |
| | | registrant: "", |
| | | paymentDate: "", |
| | | registrationtDate: "", |
| | | ticketRegistrationId: "", |
| | | // ticketRegistrationId: "", |
| | | purchaseLedgerId: "", |
| | | salesLedgerProductId: "", |
| | | }); |
| | | const currentNoReceiptAmount = ref(0); |
| | | |
| | |
| | | return; |
| | | } |
| | | loading.value = true; |
| | | paymentRegistrationAdd(form.value) |
| | | paymentRegistrationAdd([form.value]) |
| | | .then(() => { |
| | | showToast("提交成功"); |
| | | onClickLeft(); |
| | |
| | | const rowStr = uni.getStorageSync("invoiceLedgerEditRow"); |
| | | const row = JSON.parse(rowStr); |
| | | form.value = { ...row }; |
| | | form.value.ticketRegistrationId = row.id; |
| | | // form.value.ticketRegistrationId = row.id; |
| | | form.value.id = null; |
| | | form.value.id = ""; |
| | | currentNoReceiptAmount.value = row.pendingTicketsTotal |
| | |
| | | form.value.registrant = userStore.nickName; |
| | | form.value.registrationtDate = getCurrentDate(); |
| | | form.value.paymentDate = getCurrentDate(); |
| | | form.value.salesLedgerProductId = row.id || ""; |
| | | |
| | | form.value.purchaseLedgerId = row.salesLedgerId || ""; |
| | | }; |
| | | // 获取当前日期并格式化为 YYYY-MM-DD |
| | | function getCurrentDate() { |
| | |
| | | </view> |
| | | </view> |
| | | <!-- 仅非“审批通过”的台账展示删除按钮 --> |
| | | <view |
| | | class="detail-row" |
| | | v-if="item.approvalStatus !== 3" |
| | | style="justify-content: flex-end; margin-top: 8px;" |
| | | > |
| | | <up-button |
| | | type="error" |
| | | size="small" |
| | | plain |
| | | @click.stop="handleDelete(item)" |
| | | > |
| | | <view class="detail-row" |
| | | v-if="item.approvalStatus !== 3" |
| | | style="justify-content: flex-end; margin-top: 8px;"> |
| | | <up-button type="error" |
| | | size="small" |
| | | plain |
| | | @click.stop="handleDelete(item)"> |
| | | 删除 |
| | | </up-button> |
| | | </view> |
| | |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { purchaseListPage, delPurchase } from "@/api/procurementManagement/procurementLedger"; |
| | | import { |
| | | purchaseListPage, |
| | | delPurchase, |
| | | } from "@/api/procurementManagement/procurementLedger"; |
| | | const userStore = useUserStore(); |
| | | const approvalStatusText = { |
| | | 1: "待审核", |
| | |
| | | try { |
| | | // 设置操作类型 |
| | | uni.setStorageSync("operationType", type); |
| | | uni.removeStorageSync("editData"); |
| | | |
| | | // 如果是查看或编辑操作 |
| | | if (type !== "add") { |
| | |
| | | // 只有在真正异常时,才在这里兜底提示 |
| | | const msg = |
| | | (error && error.msg) || |
| | | (error && error.response && error.response.data && error.response.data.msg) || |
| | | (error && |
| | | error.response && |
| | | error.response.data && |
| | | error.response.data.msg) || |
| | | (error && error.message) || |
| | | "删除失败"; |
| | | uni.showToast({ |
| | |
| | | placeholder="点击选择业务员" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showPicker = true"></up-icon> |
| | | @click=" showPicker = true"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="客户名称" |
| | |
| | | @click="openCategoryPicker(idx)" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showCategoryPicker = true"></up-icon> |
| | | @click="openCategoryPicker(idx)"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <!-- 规格型号 --> |
| | |
| | | @click="openSpecificationPicker(idx)" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showSpecificationPicker = true"></up-icon> |
| | | @click="openSpecificationPicker(idx)"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <!-- 绑定机器 --> |
| | |
| | | @click="openTaxRatePicker(idx)" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showTaxRatePicker = true"></up-icon> |
| | | @click="openTaxRatePicker(idx)"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | <!-- 含税单价 --> |
| | |
| | | @click="openInvoiceTypePicker(idx)" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showInvoiceTypePicker = true"></up-icon> |
| | | @click="openInvoiceTypePicker(idx)"></up-icon> |
| | | </template> |
| | | </up-form-item> |
| | | </view> |
| | |
| | | v-if="ledgerList.length > 0"> |
| | | <view v-for="(item, index) in ledgerList" |
| | | :key="index"> |
| | | <view class="ledger-item" |
| | | @click="handleInfo('edit', item)"> |
| | | <view class="ledger-item"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="document-icon"> |
| | |
| | | <up-divider></up-divider> |
| | | <view class="detail-buttons"> |
| | | <u-button class="detail-button" |
| | | size="small" |
| | | type="primary" |
| | | @click="openOut(item)"> |
| | | 发货状态 |
| | | </u-button> |
| | | <u-button class="detail-button" |
| | | size="small" |
| | | type="error" |
| | | plain |
| | | @click.stop="handleDelete(item)"> |
| | | 删除 |
| | | </u-button> |
| | | size="small" |
| | | type="primary" |
| | | @click.stop="handleInfo('edit', item)"> |
| | | 编辑 |
| | | </u-button> |
| | | <u-button class="detail-button" |
| | | size="small" |
| | | type="primary" |
| | | plain |
| | | @click.stop="openOut(item)"> |
| | | 发货状态 |
| | | </u-button> |
| | | <u-button class="detail-button" |
| | | size="small" |
| | | type="error" |
| | | plain |
| | | @click.stop="handleDelete(item)"> |
| | | 删除 |
| | | </u-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | const statusStr = (p.shippingStatus ?? "").toString(); |
| | | // 包含“发货”或有发货日期/车牌号视为已发货 |
| | | return ( |
| | | statusStr.includes("发货") || |
| | | !!p.shippingDate || |
| | | !!p.shippingCarNumber |
| | | statusStr.includes("发货") || !!p.shippingDate || !!p.shippingCarNumber |
| | | ); |
| | | }); |
| | | }; |
| | |
| | | try { |
| | | // 设置操作类型 |
| | | uni.setStorageSync("operationType", type); |
| | | uni.removeStorageSync("editData"); |
| | | |
| | | // 如果是查看或编辑操作 |
| | | if (type !== "add") { |
| | |
| | | // code: code, |
| | | // data: data |
| | | // }) |
| | | console.log('接收到的参数:', { |
| | | url: (config.baseUrl || baseUrl) + config.url, |
| | | code: code, |
| | | data: data |
| | | }) |
| | | if (code === 401) { |
| | | showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(res => { |
| | | if (res.confirm) { |
| | |
| | | reject('无效的会话,或者会话已过期,请重新登录。') |
| | | } else if (code === 500) { |
| | | toast(msg) |
| | | reject('500') |
| | | console.error('500错误:', data) |
| | | reject(data) |
| | | } else if (code !== 200) { |
| | | toast(msg) |
| | | reject(code) |