Merge remote-tracking branch 'origin/dev_new' into dev_new
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // å页æ¥è¯¢ |
| | | export function safeTrainingListPage(query) { |
| | | return request({ |
| | | url: "/safeTraining/page", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | |
| | | // æ°å¢å®å
¨å¹è®è¯ä¼° |
| | | export function safeTrainingAdd(query) { |
| | | return request({ |
| | | url: '/safeTraining', |
| | | method: 'post', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | // ä¿®æ¹å®å
¨å¹è®è¯ä¼° |
| | | export function safeTrainingUpdate(query) { |
| | | return request({ |
| | | url: '/safeTraining', |
| | | method: 'put', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | // å é¤å®å
¨å¹è®è¯ä¼° |
| | | export function safeTrainingDel(ids) { |
| | | return request({ |
| | | url: '/safeTraining/' + ids, |
| | | method: 'delete', |
| | | data: ids |
| | | }) |
| | | } |
| | | |
| | | // å¯¼åº |
| | | export function safeTrainingExport(query) { |
| | | return request({ |
| | | url: '/safeTraining/export', |
| | | method: 'post', |
| | | data: query, |
| | | responseType: 'blob' |
| | | }) |
| | | } |
| | | |
| | | // æ¥è¯¢éä»¶å表 |
| | | export function safeTrainingFileListPage(query) { |
| | | return request({ |
| | | url: "/safeTrainingFile/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // æ·»å éä»¶ |
| | | export function safeTrainingFileAdd(query) { |
| | | return request({ |
| | | url: '/safeTrainingFile/add', |
| | | method: 'post', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | // å é¤éä»¶ |
| | | export function safeTrainingFileDel(ids) { |
| | | return request({ |
| | | url: '/safeTrainingFile/del', |
| | | method: 'delete', |
| | | data: ids |
| | | }) |
| | | } |
| | | |
| | | // ç¾å° |
| | | export function safeTrainingSign(query) { |
| | | return request({ |
| | | url: '/safeTraining/sign', |
| | | method: 'post', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | // æ¥è¯¢è¯¦æ
|
| | | export function safeTrainingGet(query) { |
| | | return request({ |
| | | url: '/safeTraining/getSafeTraining', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // æäº¤ |
| | | export function safeTrainingSave(query) { |
| | | return request({ |
| | | url: '/safeTraining/saveSafeTraining', |
| | | method: 'post', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | export function safeTrainingDetailListPage(query) { |
| | | return request({ |
| | | url: "/safeTrainingDetails/page", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // å¯¼åº |
| | | export function safeTrainingDetailExport(query) { |
| | | return request({ |
| | | url: '/safeTrainingDetails/export', |
| | | method: 'post', |
| | | data: query, |
| | | responseType: 'blob' |
| | | }) |
| | | } |
| | |
| | | "navigationBarTitleText": "åºæ¥é¢æ¡è¯¦æ
", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/safeProduction/safetyTrainingAssessment/index", |
| | | "style": { |
| | | "navigationBarTitleText": "å®å
¨å¹è®è¯ä¼°", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/safeProduction/safetyTrainingAssessment/detail", |
| | | "style": { |
| | | "navigationBarTitleText": "å¹è®è¯¦æ
", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/safeProduction/safetyTrainingAssessment/view", |
| | | "style": { |
| | | "navigationBarTitleText": "å¹è®è¯¦æ
", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/safeProduction/safetyTrainingAssessment/fileList", |
| | | "style": { |
| | | "navigationBarTitleText": "å¹è®éä»¶", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/safeProduction/safetyTrainingAssessment/resultDetail", |
| | | "style": { |
| | | "navigationBarTitleText": "ç»ææç»", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/safeProduction/safetyTrainingAssessment/record", |
| | | "style": { |
| | | "navigationBarTitleText": "å¹è®è®°å½", |
| | | "navigationStyle": "custom" |
| | | } |
| | | } |
| | | ], |
| | | "subPackages": [ |
| | |
| | | icon: "/static/images/icon/guzhangfenxi@2x.png", |
| | | label: "äºæ
䏿¥", |
| | | }, |
| | | { |
| | | icon: "/static/images/icon/guzhangfenxi@2x.png", |
| | | label: "å®å
¨å¹è®", |
| | | }, |
| | | ]); |
| | | // åååå
¬åè½æ°æ® |
| | | const collaborationItems = reactive([ |
| | |
| | | url: "/pages/safeProduction/accidentReportingRecord/index", |
| | | }); |
| | | break; |
| | | case "å®å
¨å¹è®": |
| | | uni.navigateTo({ |
| | | url: "/pages/safeProduction/safetyTrainingAssessment/index", |
| | | }); |
| | | break; |
| | | |
| | | default: |
| | | uni.showToast({ |
| | | title: `ç¹å»äº${item.label}`, |
| | |
| | | <view class="topbox"> |
| | | <view class="boxItem"> |
| | | <view class="boxItem-num"> |
| | | {{stats.total}} |
| | | {{stats.total ? stats.total : 0}} |
| | | </view> |
| | | <view class="boxItem-title"> |
| | | æ»ä¼è®®æ° |
| | |
| | | </view> |
| | | <view class="boxItem"> |
| | | <view class="boxItem-num"> |
| | | {{stats.underWay}} |
| | | {{stats.underWay ? stats.underWay : 0}} |
| | | </view> |
| | | <view class="boxItem-title"> |
| | | è¿è¡ä¸ |
| | |
| | | </view> |
| | | <view class="boxItem"> |
| | | <view class="boxItem-num"> |
| | | {{stats.completed}} |
| | | {{stats.completed ? stats.completed : 0}} |
| | | </view> |
| | | <view class="boxItem-title"> |
| | | 已宿 |
| | |
| | | </view> |
| | | <view class="boxItem"> |
| | | <view class="boxItem-num"> |
| | | {{stats.toStart}} |
| | | {{stats.toStart ? stats.toStart : 0}} |
| | | </view> |
| | | <view class="boxItem-title"> |
| | | å³å°å¼å§ |
| | |
| | | <text class="detail-label">åºä»éé¢(å
)</text> |
| | | <text class="detail-value danger">{{ formatAmount(item.payableAmount) }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åçæ¥æ</text> |
| | | <text class="detail-value">{{ item.paymentDate }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="danger-investigation-detail"> |
| | | <PageHeader :title="isEdit ? 'ç¼è¾å¹è®' : 'æ°å¢å¹è®'" |
| | | @back="goBack" /> |
| | | <u-form @submit="handleSubmit" |
| | | ref="formRef" |
| | | label-width="110"> |
| | | <!-- å¹è®ä¿¡æ¯ --> |
| | | <u-cell-group title="å¹è®ä¿¡æ¯"> |
| | | <u-form-item label="课ç¨ç¼å·" |
| | | prop="courseCode" |
| | | border-bottom> |
| | | <u-input v-model="form.courseCode" |
| | | placeholder="ç³»ç»èªå¨çæ" |
| | | readonly /> |
| | | </u-form-item> |
| | | <u-form-item label="å¹è®æ¥æ" |
| | | prop="trainingDate" |
| | | required |
| | | border-bottom> |
| | | <u-input v-model="form.trainingDate" |
| | | placeholder="è¯·éæ©å¹è®æ¥æ" |
| | | @click="showTrainingDatePicker" |
| | | readonly /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showTrainingDatePicker"></up-icon> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item label="å¼å§æ¶é´" |
| | | prop="openingTime" |
| | | required |
| | | border-bottom> |
| | | <u-input v-model="form.openingTime" |
| | | placeholder="è¯·éæ©å¼å§æ¶é´" |
| | | @click="showOpeningTimePicker" |
| | | readonly /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showOpeningTimePicker"></up-icon> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item label="ç»ææ¶é´" |
| | | prop="endTime" |
| | | required |
| | | border-bottom> |
| | | <u-input v-model="form.endTime" |
| | | placeholder="è¯·éæ©ç»ææ¶é´" |
| | | @click="showEndTimePicker" |
| | | readonly /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showEndTimePicker"></up-icon> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item label="å¹è®ç®æ " |
| | | prop="trainingObjectives" |
| | | border-bottom> |
| | | <u-input v-model="form.trainingObjectives" |
| | | placeholder="请è¾å
¥å¹è®ç®æ " /> |
| | | </u-form-item> |
| | | <u-form-item label="åå 对象" |
| | | prop="participants" |
| | | border-bottom> |
| | | <u-input v-model="form.participants" |
| | | placeholder="请è¾å
¥åå 对象" /> |
| | | </u-form-item> |
| | | <u-form-item label="å¹è®å
容" |
| | | prop="trainingContent" |
| | | required |
| | | border-bottom> |
| | | <u-textarea v-model="form.trainingContent" |
| | | placeholder="请è¾å
¥å¹è®å
容" |
| | | :maxlength="200" |
| | | count |
| | | :autoHeight="true" /> |
| | | </u-form-item> |
| | | <u-form-item label="å¹è®è®²å¸" |
| | | prop="trainingLecturer" |
| | | required |
| | | border-bottom> |
| | | <u-input v-model="form.trainingLecturer" |
| | | placeholder="请è¾å
¥å¹è®è®²å¸" /> |
| | | </u-form-item> |
| | | <u-form-item label="项ç®å¦å" |
| | | prop="projectCredits" |
| | | border-bottom> |
| | | <u-input v-model="form.projectCredits" |
| | | placeholder="请è¾å
¥é¡¹ç®å¦å" |
| | | type="number" /> |
| | | </u-form-item> |
| | | <u-form-item label="å¹è®æ¹å¼" |
| | | prop="trainingMode" |
| | | border-bottom> |
| | | <u-input v-model="trainingModeName" |
| | | placeholder="è¯·éæ©å¹è®æ¹å¼" |
| | | @click="showTrainingModeSheet" |
| | | readonly /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showTrainingModeSheet"></up-icon> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item label="å¹è®å°ç¹" |
| | | prop="placeTraining" |
| | | border-bottom> |
| | | <u-input v-model="form.placeTraining" |
| | | placeholder="请è¾å
¥å¹è®å°ç¹" /> |
| | | </u-form-item> |
| | | <u-form-item label="课æ¶" |
| | | prop="classHour" |
| | | required |
| | | border-bottom> |
| | | <u-input v-model="form.classHour" |
| | | placeholder="请è¾å
¥è¯¾æ¶" |
| | | type="number" /> |
| | | </u-form-item> |
| | | </u-cell-group> |
| | | <!-- æäº¤æé® --> |
| | | <view class="footer-btns"> |
| | | <u-button class="cancel-btn" |
| | | @click="goBack">åæ¶</u-button> |
| | | <u-button class="sign-btn" |
| | | type="primary" |
| | | @click="handleSubmit" |
| | | :loading="loading">{{ isEdit ? 'ä¿åä¿®æ¹' : 'æäº¤' }}</u-button> |
| | | </view> |
| | | </u-form> |
| | | <!-- æ¶é´éæ©å¨ --> |
| | | <up-datetime-picker :show="trainingDateVisible" |
| | | mode="date" |
| | | v-model="nowDate" |
| | | @confirm="handleTrainingDateConfirm" |
| | | @cancel="trainingDateVisible = false" |
| | | title="éæ©å¹è®æ¥æ" /> |
| | | <u-datetime-picker :show="openingTimeVisible" |
| | | mode="time" |
| | | @confirm="handleOpeningTimeConfirm" |
| | | @cancel="openingTimeVisible = false" |
| | | title="éæ©å¼å§æ¶é´" /> |
| | | <u-datetime-picker :show="endTimeVisible" |
| | | mode="time" |
| | | @confirm="handleEndTimeConfirm" |
| | | @cancel="endTimeVisible = false" |
| | | title="éæ©ç»ææ¶é´" /> |
| | | <!-- å¹è®æ¹å¼éæ©å¨ --> |
| | | <up-action-sheet :show="trainingModeSheetVisible" |
| | | :actions="trainingModeOptions" |
| | | @select="handleTrainingModeSelect" |
| | | @close="trainingModeSheetVisible = false" |
| | | title="éæ©å¹è®æ¹å¼" /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | // æ¿æ¢ toast æ¹æ³ |
| | | defineOptions({ name: "danger-investigation-detail" }); |
| | | const showToast = message => { |
| | | uni.showToast({ title: message, icon: "none" }); |
| | | }; |
| | | |
| | | import { ref, onMounted } from "vue"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { |
| | | safeTrainingAdd, |
| | | safeTrainingUpdate, |
| | | } from "@/api/safeProduction/safetyTrainingAssessment"; |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | import { useDict } from "@/utils/dict"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | // è·ååå
¸æ°æ® |
| | | const { safe_training_methods } = useDict("safe_training_methods"); |
| | | |
| | | // è¡¨åæ°æ® |
| | | const form = ref({ |
| | | courseCode: "", // 课ç¨ç¼å· |
| | | trainingDate: "", // å¹è®æ¥æ |
| | | openingTime: "", // å¼å§æ¶é´ |
| | | endTime: "", // ç»ææ¶é´ |
| | | trainingObjectives: "", // å¹è®ç®æ |
| | | participants: "", // åå 对象 |
| | | trainingContent: "", // å¹è®å
容 |
| | | trainingLecturer: "", // å¹è®è®²å¸ |
| | | projectCredits: "", // 项ç®å¦å |
| | | trainingMode: "", // å¹è®æ¹å¼ |
| | | placeTraining: "", // å¹è®å°ç¹ |
| | | classHour: "", // è¯¾æ¶ |
| | | }); |
| | | |
| | | // 页é¢ç¶æ |
| | | const loading = ref(false); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | |
| | | // å¹è®æ¹å¼éæ©å¨ |
| | | const trainingModeSheetVisible = ref(false); |
| | | const trainingModeName = ref(""); |
| | | const trainingModeOptions = ref([]); |
| | | |
| | | // æ¶é´éæ©å¨ |
| | | const trainingDateVisible = ref(false); |
| | | const openingTimeVisible = ref(false); |
| | | const endTimeVisible = ref(false); |
| | | const nowDate = ref(new Date()); |
| | | |
| | | const showTrainingDatePicker = () => { |
| | | trainingDateVisible.value = true; |
| | | }; |
| | | |
| | | const showOpeningTimePicker = () => { |
| | | openingTimeVisible.value = true; |
| | | }; |
| | | |
| | | const showEndTimePicker = () => { |
| | | endTimeVisible.value = true; |
| | | }; |
| | | |
| | | const handleTrainingDateConfirm = e => { |
| | | form.value.trainingDate = dayjs(e.value).format("YYYY-MM-DD"); |
| | | nowDate.value = e.value; |
| | | trainingDateVisible.value = false; |
| | | }; |
| | | |
| | | const handleOpeningTimeConfirm = e => { |
| | | console.log(e); |
| | | form.value.openingTime = e.value; |
| | | openingTimeVisible.value = false; |
| | | }; |
| | | |
| | | const handleEndTimeConfirm = e => { |
| | | form.value.endTime = e.value; |
| | | endTimeVisible.value = false; |
| | | }; |
| | | |
| | | // æ¾ç¤ºå¹è®æ¹å¼éæ©å¨ |
| | | const showTrainingModeSheet = () => { |
| | | trainingModeSheetVisible.value = true; |
| | | }; |
| | | |
| | | // å¤çå¹è®æ¹å¼éæ© |
| | | const handleTrainingModeSelect = item => { |
| | | form.value.trainingMode = item.value; |
| | | trainingModeName.value = item.name; |
| | | trainingModeSheetVisible.value = false; |
| | | }; |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.removeStorageSync("safetyTraining"); |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const handleSubmit = async () => { |
| | | if (!form.value.trainingDate) { |
| | | showToast("è¯·éæ©å¹è®æ¥æ"); |
| | | return; |
| | | } |
| | | |
| | | if (!form.value.openingTime) { |
| | | showToast("è¯·éæ©å¼å§æ¶é´"); |
| | | return; |
| | | } |
| | | |
| | | if (!form.value.endTime) { |
| | | showToast("è¯·éæ©ç»ææ¶é´"); |
| | | return; |
| | | } |
| | | if (!form.value.trainingContent) { |
| | | showToast("请è¾å
¥å¹è®å
容"); |
| | | return; |
| | | } |
| | | |
| | | if (!form.value.trainingLecturer) { |
| | | showToast("请è¾å
¥å¹è®è®²å¸"); |
| | | return; |
| | | } |
| | | |
| | | if (!form.value.classHour) { |
| | | showToast("请è¾å
¥è¯¾æ¶"); |
| | | return; |
| | | } |
| | | if ( |
| | | form.value.projectCredits && |
| | | (isNaN(Number(form.value.projectCredits)) || |
| | | Number(form.value.projectCredits) <= 0) |
| | | ) { |
| | | showToast("å¦åå¿
é¡»æ¯å¤§äº0çæ°å"); |
| | | return; |
| | | } |
| | | form.value.openingTime = form.value.openingTime + ":00"; |
| | | form.value.endTime = form.value.endTime + ":00"; |
| | | |
| | | if ( |
| | | form.value.classHour && |
| | | (isNaN(Number(form.value.classHour)) || Number(form.value.classHour) <= 0) |
| | | ) { |
| | | showToast("课æ¶å¿
é¡»æ¯å¤§äº0çæ°å"); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | loading.value = true; |
| | | |
| | | // 使ç¨å®å
¨æµ
æ·è´ |
| | | const source = |
| | | form.value && typeof form.value === "object" ? form.value : {}; |
| | | const submitData = {}; |
| | | Object.keys(source).forEach(k => { |
| | | submitData[k] = source[k]; |
| | | }); |
| | | |
| | | if (isEdit.value) { |
| | | const { code } = await safeTrainingAdd(submitData); |
| | | if (code === 200) { |
| | | showToast("ä¿®æ¹æå"); |
| | | setTimeout(() => { |
| | | goBack(); |
| | | }, 500); |
| | | } else { |
| | | loading.value = false; |
| | | showToast("ä¿®æ¹å¤±è´¥ï¼è¯·éè¯"); |
| | | } |
| | | } else { |
| | | const { code } = await safeTrainingAdd(submitData); |
| | | if (code === 200) { |
| | | showToast("æ°å¢æå"); |
| | | setTimeout(() => { |
| | | goBack(); |
| | | }, 500); |
| | | } else { |
| | | loading.value = false; |
| | | showToast("æ°å¢å¤±è´¥ï¼è¯·éè¯"); |
| | | } |
| | | } |
| | | } catch (e) { |
| | | loading.value = false; |
| | | console.error("æäº¤å¤±è´¥:", e); |
| | | showToast("æäº¤å¤±è´¥ï¼è¯·éè¯"); |
| | | } |
| | | }; |
| | | |
| | | onLoad(() => { |
| | | // ç¼è¾å¹è®æ¶ï¼ä»æ¬å°åå¨è·åæ°æ® |
| | | const safetyTraining = uni.getStorageSync("safetyTraining"); |
| | | if (safetyTraining.id) { |
| | | form.value = safetyTraining; |
| | | nowDate.value = dayjs(form.value.trainingDate).toDate(); |
| | | form.value.openingTime = form.value.openingTime |
| | | ? form.value.openingTime.slice(0, 5) |
| | | : ""; |
| | | form.value.endTime = form.value.endTime |
| | | ? form.value.endTime.slice(0, 5) |
| | | : ""; |
| | | isEdit.value = true; |
| | | } else { |
| | | isEdit.value = false; |
| | | // é»è®¤å¹è®æ¥æä¸ºä»å¤© |
| | | form.value.trainingDate = dayjs().format("YYYY-MM-DD"); |
| | | } |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | // åå§åå¹è®æ¹å¼é项 |
| | | if (safe_training_methods && Array.isArray(safe_training_methods.value)) { |
| | | trainingModeOptions.value = |
| | | safe_training_methods.value.map(item => ({ |
| | | value: item.value, |
| | | name: item.label, |
| | | })) || []; |
| | | } else { |
| | | trainingModeOptions.value = []; |
| | | } |
| | | |
| | | // 设置已éå¼çæ¾ç¤ºææ¬ |
| | | if (form.value.trainingMode) { |
| | | const modeItem = trainingModeOptions.value.find( |
| | | item => String(item.value) === String(form.value.trainingMode) |
| | | ); |
| | | trainingModeName.value = modeItem ? modeItem.name : ""; |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "@/static/scss/form-common.scss"; |
| | | |
| | | .danger-investigation-detail { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 100px; |
| | | } |
| | | |
| | | .footer-btns { |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: #fff; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 0.75rem 0; |
| | | box-shadow: 0 -0.125rem 0.5rem rgba(0, 0, 0, 0.05); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .cancel-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #666; |
| | | background: #f5f5f5; |
| | | border: 1px solid #ddd; |
| | | width: 45%; |
| | | height: 2.5rem; |
| | | border-radius: 2.5rem; |
| | | } |
| | | |
| | | .sign-btn { |
| | | font-weight: 500; |
| | | font-size: 1rem; |
| | | color: #fff; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | border: none; |
| | | width: 45%; |
| | | height: 2.5rem; |
| | | border-radius: 2.5rem; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="file-list-page"> |
| | | <!-- 页é¢å¤´é¨ --> |
| | | <PageHeader title="é件管ç" |
| | | @back="goBack" /> |
| | | <!-- éä»¶å表 --> |
| | | <view class="file-list-container"> |
| | | <view v-if="fileList.length > 0" |
| | | class="file-list"> |
| | | <view v-for="(file, index) in fileList" |
| | | :key="file.id || index" |
| | | class="file-item"> |
| | | <!-- æä»¶å¾æ --> |
| | | <!-- <view class="file-icon" |
| | | :class="getFileIconClass(file.fileType)"> |
| | | <up-icon :name="getFileIcon(file.fileType)" |
| | | size="24" |
| | | color="#ffffff" /> |
| | | </view> --> |
| | | <!-- æä»¶ä¿¡æ¯ --> |
| | | <view class="file-info"> |
| | | <text class="file-name">{{ file.name }}</text> |
| | | <!-- <text class="file-meta">{{ formatFileSize(file.fileSize) }} · {{ file.uploadTime || file.createTime }}</text> --> |
| | | </view> |
| | | <!-- æä½æé® --> |
| | | <view class="file-actions"> |
| | | <!-- <u-button size="small" |
| | | type="primary" |
| | | plain |
| | | @click="previewFile(file)">é¢è§</u-button> --> |
| | | <u-button size="small" |
| | | type="info" |
| | | plain |
| | | @click="downloadFile(file)">ä¸è½½å¹¶é¢è§</u-button> |
| | | <u-button size="small" |
| | | type="error" |
| | | plain |
| | | @click="confirmDelete(file, index)">å é¤</u-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- ç©ºç¶æ --> |
| | | <view v-else |
| | | class="empty-state"> |
| | | <up-icon name="document" |
| | | size="64" |
| | | color="#c0c4cc" /> |
| | | <text class="empty-text">ææ éä»¶</text> |
| | | </view> |
| | | </view> |
| | | <!-- <a rel="nofollow" |
| | | id="downloadLink" |
| | | href="#" |
| | | style="display:none;">ä¸è½½ææ¬æä»¶</a> --> |
| | | <!-- ä¸ä¼ æé® --> |
| | | <view class="upload-button" |
| | | @click="chooseFile"> |
| | | <up-icon name="plus" |
| | | size="24" |
| | | color="#ffffff" /> |
| | | <text class="upload-text">ä¸ä¼ éä»¶</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from "vue"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import config from "@/config"; |
| | | import { getToken } from "@/utils/auth"; |
| | | // import { saveAs } from "file-saver"; |
| | | import { |
| | | listRuleFiles, |
| | | delRuleFile, |
| | | } from "@/api/managementMeetings/rulesRegulationsManagement"; |
| | | import { |
| | | safeTrainingFileListPage, |
| | | safeTrainingFileAdd, |
| | | safeTrainingFileDel, |
| | | } from "@/api/safeProduction/safetyTrainingAssessment"; |
| | | import { blobValidate } from "@/utils/ruoyi"; |
| | | |
| | | // éä»¶å表 |
| | | const fileList = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | // const request = axios.create({ |
| | | // baseURL: "URL.com", |
| | | // adapter: axiosAdapterUniapp, |
| | | // }); |
| | | // è·åæä»¶å¾æ |
| | | const getFileIcon = fileType => { |
| | | const iconMap = { |
| | | doc: "document", |
| | | docx: "document", |
| | | xls: "grid", |
| | | xlsx: "grid", |
| | | pdf: "document", |
| | | ppt: "copy", |
| | | pptx: "copy", |
| | | txt: "document", |
| | | jpg: "image", |
| | | jpeg: "image", |
| | | png: "image", |
| | | gif: "image", |
| | | zip: "folder", |
| | | rar: "folder", |
| | | }; |
| | | return iconMap[fileType.toLowerCase()] || "document"; |
| | | }; |
| | | |
| | | // è·åæä»¶å¾æ æ ·å¼ç±» |
| | | const getFileIconClass = fileType => { |
| | | const colorMap = { |
| | | doc: "blue", |
| | | docx: "blue", |
| | | xls: "green", |
| | | xlsx: "green", |
| | | pdf: "red", |
| | | ppt: "orange", |
| | | pptx: "orange", |
| | | txt: "gray", |
| | | jpg: "purple", |
| | | jpeg: "purple", |
| | | png: "purple", |
| | | gif: "purple", |
| | | zip: "yellow", |
| | | rar: "yellow", |
| | | }; |
| | | return colorMap[fileType.toLowerCase()] || "gray"; |
| | | }; |
| | | |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | | const formatFileSize = bytes => { |
| | | if (bytes === 0) return "0 B"; |
| | | const k = 1024; |
| | | const sizes = ["B", "KB", "MB", "GB"]; |
| | | const i = Math.floor(Math.log(bytes) / Math.log(k)); |
| | | return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; |
| | | }; |
| | | |
| | | // éæ©æä»¶ |
| | | const chooseFile = () => { |
| | | uni.chooseImage({ |
| | | count: 9, |
| | | sizeType: ["original", "compressed"], |
| | | sourceType: ["album", "camera"], |
| | | success: res => { |
| | | console.log(res, "éæ©å¾çæå"); |
| | | uploadFiles(res.tempFiles); |
| | | }, |
| | | fail: err => { |
| | | console.error("éæ©å¾ç失败:", err); |
| | | showToast("éæ©æä»¶å¤±è´¥"); |
| | | }, |
| | | }); |
| | | // uni.chooseFile({ |
| | | // count: 9, |
| | | // extension: [ |
| | | // ".doc", |
| | | // ".docx", |
| | | // ".xls", |
| | | // ".xlsx", |
| | | // ".pdf", |
| | | // ".ppt", |
| | | // ".pptx", |
| | | // ".txt", |
| | | // ".jpg", |
| | | // ".jpeg", |
| | | // ".png", |
| | | // ".gif", |
| | | // ".zip", |
| | | // ".rar", |
| | | // ], |
| | | // success: res => { |
| | | // console.log(res, "éæ©æä»¶æå"); |
| | | // uploadFiles(res.tempFiles); |
| | | // }, |
| | | // fail: err => { |
| | | // showToast("éæ©æä»¶å¤±è´¥"); |
| | | // }, |
| | | // }); |
| | | }; |
| | | |
| | | // ä¸ä¼ æä»¶ |
| | | const uploadFiles = tempFiles => { |
| | | console.log(tempFiles, "ä¸ä¼ æä»¶1"); |
| | | tempFiles.forEach((tempFile, index) => { |
| | | // æ¾ç¤ºä¸ä¼ ä¸æç¤º |
| | | uni.showLoading({ |
| | | title: "ä¸ä¼ ä¸...", |
| | | mask: true, |
| | | }); |
| | | console.log(tempFile, "ä¸ä¼ æä»¶2"); |
| | | // 1. ç´æ¥ä½¿ç¨ uni.uploadFile ä¸ä¼ æä»¶ |
| | | uni.uploadFile({ |
| | | url: config.baseUrl + "/file/upload", |
| | | filePath: tempFile.path, |
| | | name: "file", |
| | | header: { |
| | | Authorization: "Bearer " + getToken(), |
| | | }, |
| | | success: uploadRes => { |
| | | uni.hideLoading(); |
| | | console.log(uploadRes, "ä¸ä¼ æä»¶3"); |
| | | |
| | | try { |
| | | const res = JSON.parse(uploadRes.data); |
| | | console.log(res, "ä¸ä¼ æä»¶4"); |
| | | if (res.code === 200) { |
| | | // 2. æåæä»¶ä¿¡æ¯ |
| | | const fileName = tempFile.name |
| | | ? tempFile.name |
| | | : tempFile.path.split("/").pop(); |
| | | // const fileType = fileName.split(".").pop(); |
| | | // 3. æé ä¿åæä»¶ä¿¡æ¯çåæ° |
| | | const saveData = { |
| | | name: fileName, |
| | | safeTrainingId: rulesRegulationsManagementId.value, |
| | | url: res.data.tempPath || "", |
| | | }; |
| | | console.log(saveData, "ä¿åæä»¶ä¿¡æ¯åæ°"); |
| | | // 4. è°ç¨ addRuleFile æ¥å£ä¿åæä»¶ä¿¡æ¯ |
| | | safeTrainingFileAdd(saveData) |
| | | .then(addRes => { |
| | | if (addRes.code === 200) { |
| | | // 5. æ·»å å°æä»¶å表 |
| | | const newFile = { |
| | | ...addRes.data, |
| | | uploadTime: new Date().toLocaleString(), |
| | | }; |
| | | // fileList.value.push(newFile); |
| | | getFileList(); |
| | | showToast("ä¸ä¼ æå"); |
| | | } else { |
| | | showToast("ä¿åæä»¶ä¿¡æ¯å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | console.error("ä¿åæä»¶ä¿¡æ¯å¤±è´¥:", err); |
| | | showToast("ä¿åæä»¶ä¿¡æ¯å¤±è´¥"); |
| | | }); |
| | | } else { |
| | | showToast("æä»¶ä¸ä¼ 失败"); |
| | | } |
| | | } catch (e) { |
| | | console.error("è§£æä¸ä¼ ç»æå¤±è´¥:", e); |
| | | showToast("ä¸ä¼ 失败"); |
| | | } |
| | | }, |
| | | fail: err => { |
| | | uni.hideLoading(); |
| | | console.error("ä¸ä¼ 失败:", err); |
| | | showToast("ä¸ä¼ 失败"); |
| | | }, |
| | | }); |
| | | }); |
| | | }; |
| | | // ä¸è½½æä»¶ |
| | | const downloadFile = file => { |
| | | var url = |
| | | config.baseUrl + |
| | | "/common/download?fileName=" + |
| | | encodeURIComponent(file.url) + |
| | | "&delete=true"; |
| | | console.log(url, "url"); |
| | | |
| | | uni |
| | | .downloadFile({ |
| | | url: url, |
| | | responseType: "blob", |
| | | header: { Authorization: "Bearer " + getToken() }, |
| | | }) |
| | | .then(res => { |
| | | console.log(res, "ä¸è½½æä»¶"); |
| | | let osType = uni.getStorageSync("deviceInfo").osName; |
| | | let filePath = res.tempFilePath; |
| | | if (osType === "ios") { |
| | | uni.openDocument({ |
| | | filePath: filePath, |
| | | showMenu: true, |
| | | success: res => { |
| | | resolve(res); |
| | | }, |
| | | fail: err => { |
| | | console.log("uni.openDocument--fail"); |
| | | reject(err); |
| | | }, |
| | | }); |
| | | } else { |
| | | uni.saveFile({ |
| | | tempFilePath: filePath, |
| | | success: fileRes => { |
| | | uni.showToast({ |
| | | icon: "none", |
| | | mask: true, |
| | | title: |
| | | "æä»¶å·²ä¿åï¼Android/data/uni.UNI720216F/apps/__UNI__720216F/" + |
| | | fileRes.savedFilePath, //ä¿åè·¯å¾ |
| | | duration: 3000, |
| | | }); |
| | | setTimeout(() => { |
| | | //æå¼ææ¡£æ¥ç |
| | | uni.openDocument({ |
| | | filePath: fileRes.savedFilePath, |
| | | success: function (res) { |
| | | resolve(fileRes); |
| | | }, |
| | | }); |
| | | }, 3000); |
| | | }, |
| | | fail: err => { |
| | | console.log("uni.save--fail"); |
| | | reject(err); |
| | | }, |
| | | }); |
| | | } |
| | | // const isBlob = blobValidate(res.data); |
| | | // if (isBlob) { |
| | | // const blob = new Blob([res.data], { type: "text/plain" }); |
| | | // const url = URL.createObjectURL(blob); |
| | | // const downloadLink = document.getElementById("downloadLink"); |
| | | // downloadLink.href = url; |
| | | // downloadLink.download = file.name; |
| | | // downloadLink.click(); |
| | | // showToast("ä¸è½½æå"); |
| | | // } else { |
| | | // showToast("ä¸è½½å¤±è´¥"); |
| | | // } |
| | | }) |
| | | .catch(err => { |
| | | console.error("ä¸è½½å¤±è´¥:", err); |
| | | showToast("ä¸è½½å¤±è´¥"); |
| | | }); |
| | | }; |
| | | |
| | | // 确认å é¤ |
| | | const confirmDelete = (file, index) => { |
| | | uni.showModal({ |
| | | title: "å é¤ç¡®è®¤", |
| | | content: `ç¡®å®è¦å é¤éä»¶ "${file.name}" åï¼`, |
| | | success: res => { |
| | | if (res.confirm) { |
| | | deleteFile(file.id, index); |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // å 餿件 |
| | | const deleteFile = (fileId, index) => { |
| | | uni.showLoading({ |
| | | title: "å é¤ä¸...", |
| | | mask: true, |
| | | }); |
| | | |
| | | safeTrainingFileDel([fileId]) |
| | | .then(res => { |
| | | uni.hideLoading(); |
| | | if (res.code === 200) { |
| | | // fileList.value.splice(index, 1); |
| | | getFileList(); |
| | | showToast("å 餿å"); |
| | | } else { |
| | | showToast("å é¤å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | uni.hideLoading(); |
| | | showToast("å é¤å¤±è´¥"); |
| | | }); |
| | | }; |
| | | |
| | | // æ¾ç¤ºæç¤º |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | const rulesRegulationsManagementId = ref(""); |
| | | // 页é¢å è½½æ¶ |
| | | onMounted(() => { |
| | | rulesRegulationsManagementId.value = uni.getStorageSync( |
| | | "safetyTrainingFileId" |
| | | ); |
| | | // ä» API è·åéä»¶å表 |
| | | getFileList(); |
| | | // 仿¬å°åå¨è·å rulesRegulationsManagementId |
| | | }); |
| | | |
| | | // è·åéä»¶å表 |
| | | const getFileList = () => { |
| | | uni.showLoading({ |
| | | title: "å è½½ä¸...", |
| | | mask: true, |
| | | }); |
| | | |
| | | safeTrainingFileListPage({ |
| | | safeTrainingId: rulesRegulationsManagementId.value, |
| | | current: -1, |
| | | size: -1, |
| | | }) |
| | | .then(res => { |
| | | uni.hideLoading(); |
| | | if (res.code === 200) { |
| | | fileList.value = res.data.records || []; |
| | | } else { |
| | | showToast("è·åéä»¶å表失败"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | uni.hideLoading(); |
| | | showToast("è·åéä»¶å表失败"); |
| | | }); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "../../../styles/sales-common.scss"; |
| | | |
| | | .file-list-page { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 100rpx; |
| | | } |
| | | |
| | | .file-list-container { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .file-list { |
| | | background: #ffffff; |
| | | border-radius: 8rpx; |
| | | overflow: hidden; |
| | | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .file-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 20rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | } |
| | | |
| | | .file-icon { |
| | | width: 56rpx; |
| | | height: 56rpx; |
| | | border-radius: 8rpx; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | margin-right: 20rpx; |
| | | |
| | | &.blue { |
| | | background: #409eff; |
| | | } |
| | | |
| | | &.green { |
| | | background: #67c23a; |
| | | } |
| | | |
| | | &.red { |
| | | background: #f56c6c; |
| | | } |
| | | |
| | | &.orange { |
| | | background: #e6a23c; |
| | | } |
| | | |
| | | &.gray { |
| | | background: #909399; |
| | | } |
| | | |
| | | &.purple { |
| | | background: #909399; |
| | | } |
| | | |
| | | &.yellow { |
| | | background: #e6a23c; |
| | | } |
| | | } |
| | | |
| | | .file-info { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .file-name { |
| | | display: block; |
| | | font-size: 16px; |
| | | color: #303133; |
| | | margin-bottom: 8rpx; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .file-meta { |
| | | display: block; |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .file-actions { |
| | | display: flex; |
| | | gap: 12rpx; |
| | | } |
| | | |
| | | .empty-state { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 100rpx 0; |
| | | background: #ffffff; |
| | | border-radius: 8rpx; |
| | | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .empty-text { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | margin-top: 20rpx; |
| | | } |
| | | |
| | | .upload-button { |
| | | position: fixed; |
| | | bottom: 40rpx; |
| | | right: 40rpx; |
| | | width: 130rpx; |
| | | height: 130rpx; |
| | | border-radius: 50%; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | box-shadow: 0 4rpx 16rpx rgba(102, 126, 234, 0.4); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .upload-text { |
| | | font-size: 10px; |
| | | color: #ffffff; |
| | | margin-top: 4rpx; |
| | | } |
| | | |
| | | .upload-progress { |
| | | padding: 40rpx 0; |
| | | } |
| | | |
| | | .upload-progress-text { |
| | | display: block; |
| | | text-align: center; |
| | | margin-top: 20rpx; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="sales-accoun"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="å®å
¨å¹è®è¯ä¼°" |
| | | @back="goBack" /> |
| | | <!-- æç´¢åçéåºå --> |
| | | <view class="search-section"> |
| | | <view class="search-bar"> |
| | | <view @click="selectDate" |
| | | class="search-input"> |
| | | <view class="search-text">{{ searchKeyword? searchKeyword : 'è¯·éæ©å¹è®æ¥æ' }}</view> |
| | | </view> |
| | | <view class="filter-button" |
| | | @click="clearDate"> |
| | | <u-icon name="close-circle" |
| | | size="24" |
| | | color="#999"></u-icon> |
| | | </view> |
| | | </view> |
| | | <!-- å¹è®è®°å½æé® --> |
| | | <view class="record-button"> |
| | | <u-button type="info" |
| | | @click="viewTrainingRecord">å¹è®è®°å½</u-button> |
| | | </view> |
| | | </view> |
| | | <!-- æ ç¾é¡µ --> |
| | | <view class="tabs-section"> |
| | | <up-tabs v-model="searchForm.state" |
| | | :list="tabList" |
| | | itemStyle="width: 33%;height: 80rpx;" |
| | | @change="tabhandleQuery"> |
| | | </up-tabs> |
| | | </view> |
| | | <!-- å¹è®è®°å½å表 --> |
| | | <view class="ledger-list" |
| | | v-if="trainingList.length > 0"> |
| | | <view v-for="(item, index) in trainingList" |
| | | :key="index"> |
| | | <view class="ledger-item"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" |
| | | size="16" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-id">课ç¨ç¼å·ï¼{{ item.courseCode }}</text> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">课ç¨ç¼å·</text> |
| | | <text class="detail-value">{{ item.courseCode || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å¹è®æ¥æ</text> |
| | | <text class="detail-value">{{ item.trainingDate || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å¼å§æ¶é´</text> |
| | | <text class="detail-value">{{ item.openingTime || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç»ææ¶é´</text> |
| | | <text class="detail-value">{{ item.endTime || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å¹è®ç®æ </text> |
| | | <text class="detail-value">{{ item.trainingObjectives || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åå 对象</text> |
| | | <text class="detail-value">{{ item.participants || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å¹è®å
容</text> |
| | | <text class="detail-value">{{ item.trainingContent || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å¹è®è®²å¸</text> |
| | | <text class="detail-value">{{ item.trainingLecturer || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">项ç®å¦å</text> |
| | | <text class="detail-value">{{ item.projectCredits || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å¹è®æ¹å¼</text> |
| | | <text class="detail-value">{{ getTrainingModeLabel(item.trainingMode) || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å¹è®å°ç¹</text> |
| | | <text class="detail-value">{{ item.placeTraining || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">课æ¶</text> |
| | | <text class="detail-value">{{ item.classHour || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <!-- æé®åºå --> |
| | | <view class="action-buttons"> |
| | | <u-button type="primary" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.state !== 0" |
| | | @click="editVisit(item)"> |
| | | ç¼è¾ |
| | | </u-button> |
| | | <u-button type="info" |
| | | size="small" |
| | | class="action-btn" |
| | | @click="viewFileList(item)"> |
| | | éä»¶ |
| | | </u-button> |
| | | <u-button type="error" |
| | | size="small" |
| | | class="action-btn" |
| | | @click="deleteVisit(item)"> |
| | | å é¤ |
| | | </u-button> |
| | | </view> |
| | | <view class="action-buttons"> |
| | | <u-button type="warning" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.state !== 1" |
| | | @click="signIn(item)"> |
| | | ç¾å° |
| | | </u-button> |
| | | <u-button type="success" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.state === 0" |
| | | @click="viewResultDetail(item)"> |
| | | ç»ææç» |
| | | </u-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else |
| | | class="no-data"> |
| | | <text>ææ å¹è®è®°å½</text> |
| | | </view> |
| | | <up-datetime-picker :show="trainingDateVisible" |
| | | mode="date" |
| | | @confirm="handleDateConfirm" |
| | | @cancel="handleDateCancel" |
| | | title="éæ©å¹è®æ¥æ" /> |
| | | <!-- æµ®å¨æ°å¢æé® --> |
| | | <view class="fab-button" |
| | | @click="addVisit"> |
| | | <up-icon name="plus" |
| | | size="24" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, reactive } from "vue"; |
| | | |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { |
| | | safeTrainingListPage, |
| | | safeTrainingDel, |
| | | safeTrainingSign, |
| | | safeTrainingGet, |
| | | } from "@/api/safeProduction/safetyTrainingAssessment"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { useDict } from "@/utils/dict"; |
| | | import dayjs from "dayjs"; |
| | | // æ¿æ¢ toast æ¹æ³ |
| | | defineOptions({ name: "safety-training-index" }); |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | const userStore = useUserStore(); |
| | | |
| | | // è·ååå
¸æ°æ® |
| | | const { safe_training_methods } = useDict("safe_training_methods"); |
| | | |
| | | // æç´¢å
³é®è¯ |
| | | const searchKeyword = ref(""); |
| | | // æ¥æéæ©å¨ç¶æ |
| | | const trainingDateVisible = ref(false); |
| | | |
| | | const tabList = reactive([ |
| | | { name: "æªå¼å§", value: 0 }, |
| | | { name: "è¿è¡ä¸", value: 1 }, |
| | | { name: "å·²ç»æ", value: 2 }, |
| | | ]); |
| | | // æç´¢è¡¨å |
| | | const searchForm = ref({ |
| | | state: 0, // é»è®¤æ¾ç¤ºå·²ç»æ |
| | | trainingDate: "", |
| | | }); |
| | | const tabhandleQuery = val => { |
| | | searchForm.value.state = val.value; |
| | | getList(); |
| | | }; |
| | | const getTrainingModeLabel = mode => { |
| | | if (!safe_training_methods || !Array.isArray(safe_training_methods.value)) { |
| | | return mode || "-"; |
| | | } |
| | | const dictItem = safe_training_methods.value.find( |
| | | item => String(item.value) === String(mode) |
| | | ); |
| | | return dictItem ? dictItem.label : mode || "-"; |
| | | }; |
| | | |
| | | // å¹è®è®°å½æ°æ® |
| | | const trainingList = ref([]); |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | const viewFileList = item => { |
| | | uni.setStorageSync("safetyTrainingFileId", item.id); |
| | | uni.navigateTo({ |
| | | url: "/pages/safeProduction/safetyTrainingAssessment/fileList", |
| | | }); |
| | | }; |
| | | const currentUserId = ref(""); |
| | | // ç¾å°åè½ |
| | | const signIn = item => { |
| | | uni.showModal({ |
| | | title: "æç¤º", |
| | | content: "确认ç¾å°åï¼", |
| | | success: function (res) { |
| | | if (res.confirm) { |
| | | safeTrainingSign({ |
| | | safeTrainingId: item.id, |
| | | userId: currentUserId.value, |
| | | }) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | uni.showToast({ title: "ç¾å°æå", icon: "success" }); |
| | | setTimeout(() => {}, 1000); |
| | | } else { |
| | | uni.showToast({ title: res.msg || "ç¾å°å¤±è´¥", icon: "none" }); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | uni.showToast({ title: "ç¾å°å¤±è´¥ï¼è¯·éè¯", icon: "none" }); |
| | | }); |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // æ¥çç»ææç» |
| | | const viewResultDetail = item => { |
| | | uni.setStorageSync("safetyTrainingResultId", item.id); |
| | | uni.setStorageSync("safetyTrainingResultNums", item.nums); |
| | | |
| | | uni.navigateTo({ |
| | | url: "/pages/safeProduction/safetyTrainingAssessment/resultDetail", |
| | | }); |
| | | }; |
| | | |
| | | // æ¥çå¹è®è®°å½ |
| | | const viewTrainingRecord = () => { |
| | | uni.navigateTo({ |
| | | url: "/pages/safeProduction/safetyTrainingAssessment/record", |
| | | }); |
| | | }; |
| | | // æ¸
餿¥æéæ© |
| | | const clearDate = () => { |
| | | searchKeyword.value = ""; |
| | | searchForm.value.trainingDate = ""; |
| | | getList(); |
| | | }; |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const selectDate = () => { |
| | | trainingDateVisible.value = true; |
| | | }; |
| | | |
| | | // å¤çæ¥æéæ©ç¡®è®¤ |
| | | const handleDateConfirm = e => { |
| | | searchKeyword.value = dayjs(e.value).format("YYYY-MM-DD"); |
| | | searchForm.value.trainingDate = dayjs(e.value).format("YYYY-MM-DD"); |
| | | trainingDateVisible.value = false; |
| | | getList(); |
| | | }; |
| | | |
| | | // å¤çæ¥æéæ©åæ¶ |
| | | const handleDateCancel = () => { |
| | | trainingDateVisible.value = false; |
| | | }; |
| | | |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | showLoadingToast("å è½½ä¸..."); |
| | | const params = { |
| | | current: -1, |
| | | size: -1, |
| | | trainingDate: searchForm.value.trainingDate, |
| | | state: searchForm.value.state, |
| | | }; |
| | | safeTrainingListPage(params) |
| | | .then(res => { |
| | | trainingList.value = res.records || res.data?.records || []; |
| | | closeToast(); |
| | | }) |
| | | .catch(() => { |
| | | closeToast(); |
| | | showToast("è·åæ°æ®å¤±è´¥"); |
| | | }); |
| | | }; |
| | | |
| | | // æ¾ç¤ºå è½½æç¤º |
| | | const showLoadingToast = message => { |
| | | uni.showLoading({ |
| | | title: message, |
| | | mask: true, |
| | | }); |
| | | }; |
| | | |
| | | // å
³éæç¤º |
| | | const closeToast = () => { |
| | | uni.hideLoading(); |
| | | }; |
| | | |
| | | // æ°å¢å¹è® |
| | | const addVisit = () => { |
| | | uni.setStorageSync("safetyTraining", {}); |
| | | uni.navigateTo({ |
| | | url: "/pages/safeProduction/safetyTrainingAssessment/detail", |
| | | }); |
| | | }; |
| | | // ç¼è¾å¹è® |
| | | const editVisit = item => { |
| | | uni.setStorageSync("safetyTraining", item); |
| | | uni.navigateTo({ |
| | | url: "/pages/safeProduction/safetyTrainingAssessment/detail", |
| | | }); |
| | | }; |
| | | // å é¤å¹è® |
| | | const deleteVisit = item => { |
| | | uni.showModal({ |
| | | title: "å é¤ç¡®è®¤", |
| | | content: `ç¡®å®è¦å é¤è¯¥å¹è®è®°å½åï¼`, |
| | | success: res => { |
| | | if (res.confirm) { |
| | | deleteClientVisit(item.id); |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | // å é¤å¹è®è®°å½ |
| | | const deleteClientVisit = id => { |
| | | showLoadingToast("å é¤ä¸..."); |
| | | safeTrainingDel([id]) |
| | | .then(() => { |
| | | closeToast(); |
| | | showToast("å 餿å"); |
| | | getList(); |
| | | }) |
| | | .catch(() => { |
| | | closeToast(); |
| | | showToast("å é¤å¤±è´¥"); |
| | | }); |
| | | }; |
| | | // æ¥ç详æ
|
| | | const viewDetail = item => { |
| | | uni.setStorageSync("safetyTraining", item); |
| | | uni.navigateTo({ |
| | | url: "/pages/safeProduction/safetyTrainingAssessment/view", |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | userStore.getInfo().then(res => { |
| | | currentUserId.value = res.user.userId; |
| | | }); |
| | | // currentUserId |
| | | getList(); |
| | | }); |
| | | |
| | | onShow(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "../../../styles/sales-common.scss"; |
| | | |
| | | // 页é¢ç¹å®çæ ·å¼è¦ç |
| | | .sales-accoun { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | position: relative; |
| | | padding-bottom: 80px; |
| | | } |
| | | |
| | | // å¹è®è®°å½æé® |
| | | .record-button { |
| | | margin-top: 10px; |
| | | text-align: center; |
| | | } |
| | | |
| | | // ç¹å®ç徿 æ ·å¼ |
| | | .document-icon { |
| | | background: #667eea; // ä¿æé¡µé¢ç¹æçèæ¯è² |
| | | } |
| | | |
| | | // ç¹ææ ·å¼ |
| | | .visit-status { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .detail-value { |
| | | word-break: break-all; // ä¿ç页é¢ç¹æçææ¬æ¢è¡æ ·å¼ |
| | | } |
| | | |
| | | // ç¹å®çæµ®å¨æé®æ ·å¼ |
| | | .fab-button { |
| | | background: #667eea; // ä¿æé¡µé¢ç¹æçèæ¯è² |
| | | box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3); // ä¿æé¡µé¢ç¹æçé´å½±ææ |
| | | } |
| | | .action-buttons { |
| | | gap: 4px; |
| | | } |
| | | .action-buttons { |
| | | padding: 0 0 10rpx 0; |
| | | } |
| | | |
| | | .tabs-section { |
| | | background: #fff; |
| | | margin-bottom: 1rem; |
| | | box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.05); |
| | | } |
| | | .search-text { |
| | | // font-size: 24rpx; |
| | | color: #a6a6a6; |
| | | height: 70rpx; |
| | | line-height: 70rpx; |
| | | margin-left: 20rpx; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="training-record"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="å¹è®è®°å½" |
| | | @back="goBack" /> |
| | | <!-- æç´¢åçéåºå --> |
| | | <view class="search-section"> |
| | | <view class="search-bar"> |
| | | <view class="search-input"> |
| | | <up-input class="search-text" |
| | | placeholder="人ååç§°æç´¢" |
| | | v-model="searchForm.searchText" |
| | | @change="searchName" |
| | | clearable /> |
| | | </view> |
| | | <view class="filter-button" |
| | | @click="searchName"> |
| | | <u-icon name="search" |
| | | size="24" |
| | | color="#999"></u-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- 人åå¡çå表 --> |
| | | <view class="user-card-list" |
| | | v-if="userList.length > 0"> |
| | | <view v-for="(user, index) in userList" |
| | | :key="index" |
| | | class="user-card"> |
| | | <!-- å¡çå¤´é¨ --> |
| | | <view class="card-header" |
| | | @click="toggleUserCard(index)"> |
| | | <view class="header-left"> |
| | | <text class="user-name">{{ user.nickName }}</text> |
| | | <text class="user-dept">æå±ï¼{{ user.deptNames || '-' }}</text> |
| | | <text class="user-dept">èç³»æ¹å¼ï¼{{ user.phonenumber || '-' }}</text> |
| | | <!-- å¹è®ç»è®¡ä¿¡æ¯ --> |
| | | </view> |
| | | <u-icon :name="expandedUsers[index] ? 'arrow-up' : 'arrow-down'" |
| | | size="20" |
| | | color="#999"></u-icon> |
| | | </view> |
| | | <!-- å¡çå
å®¹ï¼æå é¨åï¼ --> |
| | | <view class="card-content" |
| | | v-if="expandedUsers[index]"> |
| | | <!-- 年份çé --> |
| | | <view class="year-filter-section"> |
| | | <!-- <text class="filter-label">年份çé</text> --> |
| | | <view class="year-options"> |
| | | <u-tag v-for="year in yearOptions" |
| | | :key="year" |
| | | :text="year" |
| | | :type="userYearFilters[user.userId] === year.toString() ? 'primary' : 'info'" |
| | | @click="() => { |
| | | userYearFilters[user.userId] = year.toString(); |
| | | filterUserCourses(user.userId); |
| | | }" |
| | | :class="{ active: userYearFilters[user.userId] === year.toString() }" |
| | | style="margin-right: 8px; margin-bottom: 8px;"></u-tag> |
| | | </view> |
| | | </view> |
| | | <!-- å¹è®è¯¾ç¨å表 --> |
| | | <view class="course-list" |
| | | v-if="userCourses[user.userId] && userCourses[user.userId].length > 0"> |
| | | <view class="user-stats" |
| | | v-if="userStats[user.userId]"> |
| | | <text class="stat-item">å¹è®æ¬¡æ°: {{ userStats[user.userId].total }}</text> |
| | | <text class="stat-item success">åæ ¼: {{ userStats[user.userId].qualified }}</text> |
| | | <text class="stat-item danger">ä¸åæ ¼: {{ userStats[user.userId].unqualified }}</text> |
| | | </view> |
| | | <view v-for="(course, courseIndex) in userCourses[user.userId]" |
| | | :key="courseIndex"> |
| | | <view class="course-item" |
| | | v-if="userYearFilters[user.userId] === 'å
¨é¨' || course.trainingDate.includes(userYearFilters[user.userId])"> |
| | | <view class="course-header"> |
| | | <text class="course-date">{{ course.trainingDate || '-' }}</text> |
| | | <u-tag :type="course.examinationResults === 'åæ ¼' ? 'success' : 'error'"> |
| | | {{ course.examinationResults || '-' }} |
| | | </u-tag> |
| | | </view> |
| | | <view class="course-info"> |
| | | <text class="info-label">å¹è®å
容ï¼</text> |
| | | <text class="info-value">{{ course.trainingContent || '-' }}</text> |
| | | </view> |
| | | <view class="course-info"> |
| | | <text class="info-label">å¹è®è¯¾æ¶ï¼</text> |
| | | <text class="info-value">{{ course.classHour || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- çéåæ æ°æ® --> |
| | | <view v-if="userYearFilters[user.userId] === 'å
¨é¨' ? userCourses[user.userId].length === 0 : userCourses[user.userId].filter(c => c.trainingDate.includes(userYearFilters[user.userId])).length === 0" |
| | | class="empty-course"> |
| | | <text class="empty-text">{{ userYearFilters[user.userId] === 'å
¨é¨' ? 'ææ å¹è®è®°å½' : 'è¯¥å¹´ä»½ææ å¹è®è®°å½' }}</text> |
| | | </view> |
| | | </view> |
| | | <!-- ç©ºç¶æ --> |
| | | <view v-else |
| | | class="empty-course"> |
| | | <text class="empty-text">ææ å¹è®è®°å½</text> |
| | | </view> |
| | | </view> |
| | | <!-- å¯¼åºæé® --> |
| | | <!-- <view class="course-export"> |
| | | <u-button type="primary" |
| | | size="small" |
| | | @click="exportUserRecord(user.userId)">导åºè®°å½</u-button> |
| | | </view> --> |
| | | </view> |
| | | </view> |
| | | <view v-else |
| | | class="empty-state"> |
| | | <up-icon name="people" |
| | | size="64" |
| | | color="#c0c4cc"></up-icon> |
| | | <text class="empty-text">ææ äººåæ°æ®</text> |
| | | </view> |
| | | <!-- ç©ºç¶æ --> |
| | | <!-- å¹´ä»½éæ©å¨ --> |
| | | <up-datetime-picker :show="yearPickerVisible" |
| | | mode="year" |
| | | @confirm="handleYearConfirm" |
| | | @cancel="yearPickerVisible = false" |
| | | title="鿩年份" /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, reactive } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { safeTrainingDetailListPage } from "@/api/safeProduction/safetyTrainingAssessment"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import config from "@/config"; |
| | | |
| | | // 页é¢ç¶æ |
| | | const userList = ref([]); |
| | | const userCourses = ref({}); // å卿¯ä¸ªç¨æ·çå¹è®è¯¾ç¨ |
| | | const userStats = ref({}); // å卿¯ä¸ªç¨æ·çå¹è®ç»è®¡ä¿¡æ¯ |
| | | const expandedUsers = ref([]); // æ§å¶ç¨æ·å¡çå±å¼ç¶æ |
| | | const loading = ref(false); |
| | | const courseLoading = ref({}); // æ§å¶æ¯ä¸ªç¨æ·ç课ç¨å è½½ç¶æ |
| | | const userYearFilters = ref({}); // å卿¯ä¸ªç¨æ·ç年份ç鿡件 |
| | | const yearOptions = ref([]); // 年份é项ï¼ä»å¹´åè¿å»ä¸å¹´ï¼ |
| | | |
| | | // æç´¢è¡¨å |
| | | const searchForm = reactive({ |
| | | searchText: "", |
| | | invoiceDate: "", |
| | | }); |
| | | |
| | | // 年份鿩å¨ç¶æ |
| | | const yearPickerVisible = ref(false); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // çæå¹´ä»½é项 |
| | | const generateYearOptions = () => { |
| | | const currentYear = new Date().getFullYear(); |
| | | const options = []; |
| | | // æ·»å "å
¨é¨"é项 |
| | | options.push("å
¨é¨"); |
| | | for (let i = 0; i < 4; i++) { |
| | | options.push(currentYear - i); |
| | | } |
| | | yearOptions.value = options; |
| | | }; |
| | | |
| | | // æç´¢äººååç§° |
| | | const searchName = () => { |
| | | getUserList(); |
| | | }; |
| | | |
| | | // æ¾ç¤ºå¹´ä»½éæ©å¨ |
| | | const showYearPicker = () => { |
| | | yearPickerVisible.value = true; |
| | | }; |
| | | |
| | | // å¤çå¹´ä»½éæ©ç¡®è®¤ |
| | | const handleYearConfirm = e => { |
| | | searchForm.invoiceDate = e.value; |
| | | yearPickerVisible.value = false; |
| | | }; |
| | | |
| | | // æå¹´ä»½æç´¢ |
| | | const searchDate = () => { |
| | | // éåææå±å¼çç¨æ·å¡çï¼éæ°å è½½å¹è®è®°å½ |
| | | userList.value.forEach((user, index) => { |
| | | if (expandedUsers.value[index]) { |
| | | getUserCourses(user.userId, index); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // è·å人åå表 |
| | | const getUserList = () => { |
| | | loading.value = true; |
| | | userListNoPage() |
| | | .then(res => { |
| | | loading.value = false; |
| | | if (res.data && res.data.length > 0) { |
| | | let users = res.data; |
| | | // 妿ææç´¢å
³é®è¯ï¼è¿è¡çé |
| | | if (searchForm.searchText) { |
| | | users = users.filter(user => |
| | | user.nickName.includes(searchForm.searchText) |
| | | ); |
| | | } |
| | | userList.value = users; |
| | | // åå§åææå¡ç为æ¶èµ·ç¶æ |
| | | expandedUsers.value = new Array(userList.value.length).fill(false); |
| | | } else { |
| | | userList.value = []; |
| | | expandedUsers.value = []; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | loading.value = false; |
| | | uni.showToast({ title: "è·å人åå表失败", icon: "none" }); |
| | | }); |
| | | }; |
| | | |
| | | // åæ¢ç¨æ·å¡çå±å¼ç¶æ |
| | | const toggleUserCard = index => { |
| | | const user = userList.value[index]; |
| | | expandedUsers.value[index] = !expandedUsers.value[index]; |
| | | |
| | | // 妿å±å¼å¡çï¼å è½½å¹è®è®°å½ |
| | | if (expandedUsers.value[index]) { |
| | | // åå§å年份çéæ¡ä»¶ä¸º"å
¨é¨" |
| | | userYearFilters.value[user.userId] = "å
¨é¨"; |
| | | getUserCourses(user.userId, index); |
| | | } |
| | | }; |
| | | |
| | | // çéç¨æ·å¹è®è¯¾ç¨ |
| | | const filterUserCourses = userId => { |
| | | if (!userYearFilters.value[userId]) return; |
| | | console.log("userYearFilters", userYearFilters.value); |
| | | const year = userYearFilters.value[userId]; |
| | | const allCourses = userCourses.value[userId] || []; |
| | | |
| | | // çéæå®å¹´ä»½çå¹è®è®°å½ |
| | | let filteredCourses = allCourses; |
| | | if (year !== "å
¨é¨") { |
| | | filteredCourses = allCourses.filter( |
| | | course => course.trainingDate && course.trainingDate.includes(year) |
| | | ); |
| | | } |
| | | console.log("filteredCourses", filteredCourses); |
| | | |
| | | // æ´æ°ç»è®¡ä¿¡æ¯ |
| | | const total = filteredCourses.length; |
| | | const qualified = filteredCourses.filter( |
| | | course => course.examinationResults === "åæ ¼" |
| | | ).length; |
| | | const unqualified = filteredCourses.filter( |
| | | course => course.examinationResults === "ä¸åæ ¼" |
| | | ).length; |
| | | userStats.value[userId] = { |
| | | total, |
| | | qualified, |
| | | unqualified, |
| | | }; |
| | | }; |
| | | |
| | | // è·åç¨æ·å¹è®è¯¾ç¨ |
| | | const getUserCourses = (userId, index) => { |
| | | courseLoading.value[userId] = true; |
| | | |
| | | const params = { |
| | | userId, |
| | | // 妿æå¹´ä»½çéï¼æ·»å 年份忰 |
| | | // è¿ééè¦æ ¹æ®å端æ¥å£çå®é
åæ°åè¿è¡è°æ´ |
| | | }; |
| | | |
| | | safeTrainingDetailListPage(params) |
| | | .then(res => { |
| | | courseLoading.value[userId] = false; |
| | | if (res.data && res.data.records) { |
| | | let courses = res.data.records; |
| | | // 妿æå¹´ä»½çéï¼è¿è¡çé |
| | | if (searchForm.invoiceDate) { |
| | | const year = searchForm.invoiceDate.substring(0, 4); |
| | | courses = courses.filter( |
| | | course => course.trainingDate && course.trainingDate.includes(year) |
| | | ); |
| | | } |
| | | userCourses.value[userId] = courses; |
| | | |
| | | // 计ç®å¹è®ç»è®¡ä¿¡æ¯ |
| | | const total = courses.length; |
| | | const qualified = courses.filter( |
| | | course => course.examinationResults === "åæ ¼" |
| | | ).length; |
| | | const unqualified = courses.filter( |
| | | course => course.examinationResults === "ä¸åæ ¼" |
| | | ).length; |
| | | |
| | | userStats.value[userId] = { |
| | | total, |
| | | qualified, |
| | | unqualified, |
| | | }; |
| | | } else { |
| | | userCourses.value[userId] = []; |
| | | userStats.value[userId] = { |
| | | total: 0, |
| | | qualified: 0, |
| | | unqualified: 0, |
| | | }; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | courseLoading.value[userId] = false; |
| | | uni.showToast({ title: "è·åå¹è®è®°å½å¤±è´¥", icon: "none" }); |
| | | }); |
| | | }; |
| | | |
| | | // 页é¢å è½½ |
| | | onMounted(() => { |
| | | // çæå¹´ä»½é项 |
| | | generateYearOptions(); |
| | | getUserList(); |
| | | }); |
| | | |
| | | // 页颿¾ç¤ºæ¶å·æ° |
| | | onShow(() => { |
| | | // å¯ä»¥å¨è¿éæ·»å å·æ°é»è¾ |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "../../../styles/sales-common.scss"; |
| | | .training-record { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 20px; |
| | | } |
| | | |
| | | // å¹´ä»½éæ©å¨ |
| | | .year-picker { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 10px 15px; |
| | | background: #f8f9fa; |
| | | border-radius: 4px; |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .picker-text { |
| | | font-size: 14px; |
| | | color: #333; |
| | | } |
| | | |
| | | // 人åå¡çå表 |
| | | .user-card-list { |
| | | padding: 10px; |
| | | } |
| | | |
| | | // 人åå¡ç |
| | | .user-card { |
| | | background: #fff; |
| | | border-radius: 8px; |
| | | margin-bottom: 12px; |
| | | box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08); |
| | | overflow: hidden; |
| | | border: 1px solid #e8e8e8; |
| | | } |
| | | |
| | | // å¡çå¤´é¨ |
| | | .card-header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 16px; |
| | | background: #f5f7fa; |
| | | border-bottom: 1px solid #e8e8e8; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .header-left { |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .user-name { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .user-dept { |
| | | font-size: 14px; |
| | | color: #666; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | // å¹è®ç»è®¡ä¿¡æ¯ |
| | | .user-stats { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 10px; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .stat-item { |
| | | font-size: 12px; |
| | | color: #555; |
| | | padding: 5px 10px; |
| | | background: #f0f2f5; |
| | | border-radius: 4px; |
| | | border: 1px solid #e0e0e0; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .stat-item.success { |
| | | background: #e6f7ff; |
| | | color: #1890ff; |
| | | border-color: #91d5ff; |
| | | } |
| | | |
| | | .stat-item.danger { |
| | | background: #fff1f0; |
| | | color: #ff4d4f; |
| | | border-color: #ffccc7; |
| | | } |
| | | |
| | | // 年份çéåºå |
| | | .year-filter-section { |
| | | margin-bottom: 16px; |
| | | padding-bottom: 12px; |
| | | border-bottom: 1px solid #e8e8e8; |
| | | } |
| | | |
| | | .filter-label { |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #333; |
| | | margin-bottom: 8px; |
| | | display: block; |
| | | } |
| | | |
| | | .year-options { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | // å¡çå
容 |
| | | .card-content { |
| | | padding: 16px; |
| | | } |
| | | |
| | | // å¹è®è¯¾ç¨å表 |
| | | .course-list { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | // 课ç¨é¡¹ |
| | | .course-item { |
| | | background: #fafafa; |
| | | border-radius: 6px; |
| | | padding: 14px; |
| | | margin-bottom: 12px; |
| | | border: 1px solid #e8e8e8; |
| | | } |
| | | |
| | | // 课ç¨å¤´é¨ |
| | | .course-header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 12px; |
| | | padding-bottom: 8px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .course-date { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | // 课ç¨ä¿¡æ¯ |
| | | .course-info { |
| | | display: flex; |
| | | margin-bottom: 8px; |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | .info-label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | width: 80px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .info-value { |
| | | font-size: 14px; |
| | | color: #333; |
| | | flex: 1; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | // 课ç¨å¯¼åºæé® |
| | | .course-export { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | // ç©ºç¶æ |
| | | .empty-state { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 60px 0; |
| | | } |
| | | |
| | | .empty-course { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 30px 0; |
| | | color: #999; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .empty-text { |
| | | font-size: 14px; |
| | | color: #999; |
| | | margin-top: 16px; |
| | | } |
| | | :deep(.u-tag--info) { |
| | | background-color: #c1c3c8; |
| | | border-width: 1px; |
| | | border-color: #c1c3c8; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="result-detail"> |
| | | <!-- 页é¢å¤´é¨ --> |
| | | <PageHeader title="ç»ææç»" |
| | | @back="goBack" /> |
| | | <!-- å
容åºå --> |
| | | <view class="content"> |
| | | <!-- 课ç¨è¯¦æ
--> |
| | | <view class="section"> |
| | | <view class="section-title">课ç¨è¯¦æ
</view> |
| | | <view class="info-list"> |
| | | <view class="info-item"> |
| | | <text class="info-label">课ç¨ç¼å·</text> |
| | | <text class="info-value">{{ currentTraining.courseCode || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å¹è®å
容</text> |
| | | <text class="info-value">{{ currentTraining.trainingContent || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">ç¶æ</text> |
| | | <text class="info-value"> |
| | | <u-tag :type="currentTraining.state === 0 ? 'success' : (currentTraining.state === 1 ? 'warning' : 'info')"> |
| | | {{ currentTraining.state === 0 ? 'æªå¼å§' : (currentTraining.state === 1 ? 'è¿è¡ä¸' : 'å·²ç»æ') }} |
| | | </u-tag> |
| | | </text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å¹è®è®²å¸</text> |
| | | <text class="info-value">{{ currentTraining.trainingLecturer || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å¹è®å¼å§æ¶é´</text> |
| | | <text class="info-value">{{ currentTraining.trainingDate + ' ' + currentTraining.openingTime || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å¹è®ç»ææ¶é´</text> |
| | | <text class="info-value">{{ currentTraining.trainingDate + ' ' + currentTraining.endTime || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å¹è®ç®æ </text> |
| | | <text class="info-value">{{ currentTraining.trainingObjectives || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">åå 对象</text> |
| | | <text class="info-value">{{ currentTraining.participants || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å¹è®æ¹å¼</text> |
| | | <text class="info-value">{{ getTrainingModeLabel(currentTraining.trainingMode) || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å¹è®å°ç¹</text> |
| | | <text class="info-value">{{ currentTraining.placeTraining || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">课æ¶</text> |
| | | <text class="info-value">{{ currentTraining.classHour || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">课ç¨å¦å</text> |
| | | <text class="info-value">{{ currentTraining.projectCredits || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">æ¥å人æ°</text> |
| | | <text class="info-value">{{ currentTraining.nums || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- 课ç¨è¯ä»· --> |
| | | <view class="section"> |
| | | <view class="section-title">课ç¨è¯ä»·</view> |
| | | <u-form ref="formRef" |
| | | label-width="90" |
| | | :model="endform"> |
| | | <u-form-item label="è¯ä»·äºº"> |
| | | <u-input v-model="endform.assessmentUserName" |
| | | disabled |
| | | placeholder="è¯·éæ©è¯ä»·äºº" /> |
| | | </u-form-item> |
| | | <u-form-item label="è¯ä»·æ¶é´"> |
| | | <u-input v-model="endform.assessmentDate" |
| | | disabled |
| | | placeholder="è¯·éæ©è¯ä»·æ¶é´" /> |
| | | </u-form-item> |
| | | <u-form-item label="èæ ¸æ¹å¼"> |
| | | <u-input v-model="endform.assessmentMethod" |
| | | placeholder="请è¾å
¥èæ ¸æ¹å¼" /> |
| | | </u-form-item> |
| | | <u-form-item label="综åè¯ä»·"> |
| | | <u-input v-model="endform.comprehensiveAssessment" |
| | | placeholder="请è¾å
¥æ¬æ¬¡è¯¾ç¨ç»¼åè¯ä»·" /> |
| | | </u-form-item> |
| | | <u-form-item label="å¹è®æè¦"> |
| | | <u-textarea v-model="endform.trainingAbstract" |
| | | :rows="4" |
| | | placeholder="请è¾å
¥å¹è®æè¦" /> |
| | | </u-form-item> |
| | | </u-form> |
| | | </view> |
| | | <!-- èæ ¸å表 --> |
| | | <view class="section"> |
| | | <view class="section-title">èæ ¸å表</view> |
| | | <view class="assessment-list"> |
| | | <view v-for="(item, index) in endform.safeTrainingDetailsDtoList" |
| | | :key="index" |
| | | class="assessment-item"> |
| | | <view class="assessment-info"> |
| | | <view class="info-row"> |
| | | <text class="label">å§åï¼</text> |
| | | <text class="value">{{ item.nickName || '-' }}</text> |
| | | </view> |
| | | <view class="info-row"> |
| | | <text class="label">çµè¯å·ç ï¼</text> |
| | | <text class="value">{{ item.phonenumber || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="assessment-result"> |
| | | <text class="result-label">èæ ¸ç»æï¼</text> |
| | | <u-radio-group v-model="endform.safeTrainingDetailsDtoList[index].examinationResults" |
| | | :key="index" |
| | | size="default"> |
| | | <u-radio label="åæ ¼" |
| | | name="åæ ¼"></u-radio> |
| | | <u-radio label="ä¸åæ ¼" |
| | | name="ä¸åæ ¼"></u-radio> |
| | | </u-radio-group> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- æäº¤æé® --> |
| | | <view class="submit-btn"> |
| | | <u-button type="primary" |
| | | @click="submitForm" |
| | | :loading="loading">æäº¤</u-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from "vue"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | import { useDict } from "@/utils/dict"; |
| | | import dayjs from "dayjs"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { |
| | | safeTrainingGet, |
| | | safeTrainingSave, |
| | | } from "@/api/safeProduction/safetyTrainingAssessment"; |
| | | |
| | | // è·ååå
¸æ°æ® |
| | | const { safe_training_methods } = useDict("safe_training_methods"); |
| | | |
| | | // 页é¢ç¶æ |
| | | const loading = ref(false); |
| | | const currentTraining = ref({}); |
| | | const endform = ref({ |
| | | assessmentUserId: "", |
| | | assessmentUserName: "", |
| | | assessmentMethod: "", |
| | | assessmentDate: "", |
| | | comprehensiveAssessment: "", |
| | | trainingAbstract: "", |
| | | safeTrainingFileList: [], |
| | | safeTrainingDetailsDtoList: [], |
| | | }); |
| | | |
| | | // è·åå¹è®æ¹å¼æ ç¾ |
| | | const getTrainingModeLabel = val => { |
| | | if (!safe_training_methods || !Array.isArray(safe_training_methods.value)) { |
| | | return val; |
| | | } |
| | | const item = safe_training_methods.value.find( |
| | | i => String(i.value) === String(val) |
| | | ); |
| | | return item ? item.label : val; |
| | | }; |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | // éªè¯èæ ¸ç»æ |
| | | for (let i = 0; i < endform.value.safeTrainingDetailsDtoList.length; i++) { |
| | | const item = endform.value.safeTrainingDetailsDtoList[i]; |
| | | if (!item.examinationResults) { |
| | | uni.showToast({ |
| | | title: `è¯·éæ©${item.nickName}çèæ ¸ç»æ`, |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | loading.value = true; |
| | | safeTrainingSave(endform.value) |
| | | .then(res => { |
| | | loading.value = false; |
| | | if (res.code === 200) { |
| | | uni.showToast({ title: "æäº¤æå", icon: "success" }); |
| | | setTimeout(() => { |
| | | goBack(); |
| | | }, 500); |
| | | } else { |
| | | uni.showToast({ title: res.msg || "æäº¤å¤±è´¥", icon: "none" }); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | loading.value = false; |
| | | uni.showToast({ title: "æäº¤å¤±è´¥ï¼è¯·éè¯", icon: "none" }); |
| | | }); |
| | | }; |
| | | |
| | | // 页é¢å è½½ |
| | | onLoad(() => { |
| | | const trainingId = uni.getStorageSync("safetyTrainingResultId"); |
| | | const trainingNums = uni.getStorageSync("safetyTrainingResultNums"); |
| | | |
| | | if (trainingId) { |
| | | getTrainingDetail(trainingId, trainingNums); |
| | | } |
| | | }); |
| | | const userStore = useUserStore(); |
| | | const getuserInfo = () => { |
| | | const userInfo = { |
| | | id: "", |
| | | nickName: "", |
| | | }; |
| | | userStore.getInfo().then(res => { |
| | | userInfo.id = res.user.userId; |
| | | userInfo.nickName = res.user.nickName; |
| | | endform.value.assessmentUserName = res.user.nickName; |
| | | endform.value.assessmentUserId = res.user.userId; |
| | | }); |
| | | return userInfo; |
| | | }; |
| | | |
| | | // è·åå¹è®è¯¦æ
|
| | | const getTrainingDetail = (id, trainingNums) => { |
| | | loading.value = true; |
| | | safeTrainingGet({ id }) |
| | | .then(res => { |
| | | loading.value = false; |
| | | if (res.code === 200) { |
| | | currentTraining.value = res.data; |
| | | currentTraining.value.nums = trainingNums; |
| | | |
| | | endform.value = { ...res.data }; |
| | | // 设置é»è®¤å¼ |
| | | if (!endform.value.assessmentUserId) { |
| | | getuserInfo(); |
| | | } |
| | | |
| | | endform.value.assessmentDate = endform.value.assessmentDate |
| | | ? dayjs(endform.value.assessmentDate).format("YYYY-MM-DD") |
| | | : dayjs().format("YYYY-MM-DD"); |
| | | endform.value.safeTrainingDetailsDtoList = |
| | | endform.value.safeTrainingDetailsDtoList || []; |
| | | } else { |
| | | uni.showToast({ title: "è·å详æ
失败", icon: "none" }); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | loading.value = false; |
| | | uni.showToast({ title: "è·å详æ
失败", icon: "none" }); |
| | | }); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .result-detail { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | } |
| | | |
| | | .content { |
| | | padding: 16px; |
| | | } |
| | | |
| | | .section { |
| | | background: #fff; |
| | | border-radius: 8px; |
| | | padding: 16px; |
| | | margin-bottom: 16px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .section-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | margin-bottom: 16px; |
| | | color: #333; |
| | | } |
| | | |
| | | .info-list { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .info-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | padding-bottom: 12px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .info-item:last-child { |
| | | border-bottom: none; |
| | | padding-bottom: 0; |
| | | } |
| | | |
| | | .info-label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | width: 100px; |
| | | } |
| | | |
| | | .info-value { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | color: #333; |
| | | text-align: right; |
| | | } |
| | | |
| | | .assessment-list { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .assessment-item { |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | padding: 16px; |
| | | border: 1px solid #e8e8e8; |
| | | } |
| | | |
| | | .assessment-info { |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .info-row { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .info-row:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | min-width: 80px; |
| | | } |
| | | |
| | | .value { |
| | | font-size: 14px; |
| | | color: #333; |
| | | flex: 1; |
| | | } |
| | | |
| | | .assessment-result { |
| | | display: flex; |
| | | align-items: center; |
| | | padding-top: 8px; |
| | | border-top: 1px solid #e8e8e8; |
| | | } |
| | | |
| | | .result-label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | min-width: 80px; |
| | | } |
| | | |
| | | .submit-btn { |
| | | margin-top: 24px; |
| | | margin-bottom: 32px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="danger-investigation-view"> |
| | | <PageHeader title="å¹è®è¯¦æ
" |
| | | @back="goBack" /> |
| | | <!-- å
å®¹å®¹å¨ --> |
| | | <view class="detail-content"> |
| | | <!-- å¹è®ä¿¡æ¯ --> |
| | | <view class="info-section"> |
| | | <!-- <view class="section-title">å¹è®ä¿¡æ¯</view> --> |
| | | <view class="info-grid"> |
| | | <view class="info-item"> |
| | | <text class="info-label">课ç¨ç¼å·</text> |
| | | <text class="info-value">{{ form.courseCode || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å¹è®æ¥æ</text> |
| | | <text class="info-value">{{ form.trainingDate || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å¼å§æ¶é´</text> |
| | | <text class="info-value">{{ form.openingTime || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">ç»ææ¶é´</text> |
| | | <text class="info-value">{{ form.endTime || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å¹è®ç®æ </text> |
| | | <text class="info-value">{{ form.trainingObjectives || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">åå 对象</text> |
| | | <text class="info-value">{{ form.participants || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å¹è®å
容</text> |
| | | <text class="info-value">{{ form.trainingContent || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å¹è®è®²å¸</text> |
| | | <text class="info-value">{{ form.trainingLecturer || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">项ç®å¦å</text> |
| | | <text class="info-value">{{ form.projectCredits || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å¹è®æ¹å¼</text> |
| | | <text class="info-value">{{ getTrainingModeLabel(form.trainingMode) || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å¹è®å°ç¹</text> |
| | | <text class="info-value">{{ form.placeTraining || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">课æ¶</text> |
| | | <text class="info-value">{{ form.classHour || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from "vue"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | import { useDict } from "@/utils/dict"; |
| | | // æ¿æ¢ toast æ¹æ³ |
| | | defineOptions({ name: "safety-training-view" }); |
| | | const showToast = message => { |
| | | uni.showToast({ title: message, icon: "none" }); |
| | | }; |
| | | |
| | | // è·ååå
¸æ°æ® |
| | | const { safe_training_methods } = useDict("safe_training_methods"); |
| | | |
| | | // è·åå¹è®æ¹å¼æ ç¾ |
| | | const getTrainingModeLabel = val => { |
| | | if (!safe_training_methods || !Array.isArray(safe_training_methods.value)) { |
| | | return val; |
| | | } |
| | | const item = safe_training_methods.value.find( |
| | | i => String(i.value) === String(val) |
| | | ); |
| | | return item ? item.label : val; |
| | | }; |
| | | |
| | | // å¹è®ä¿¡æ¯ |
| | | const form = ref({}); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | onLoad(() => { |
| | | // 仿¬å°åå¨è·åå¹è®ä¿¡æ¯ |
| | | const safetyTraining = uni.getStorageSync("safetyTraining"); |
| | | if (safetyTraining) { |
| | | form.value = safetyTraining; |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "../../../styles/sales-common.scss"; |
| | | |
| | | .danger-investigation-view { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 2rem; |
| | | } |
| | | |
| | | .detail-content { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .info-section { |
| | | background: #ffffff; |
| | | border-radius: 12px; |
| | | padding: 24px; |
| | | margin-bottom: 24px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .section-title { |
| | | padding: 1rem; |
| | | font-size: 1rem; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | background: #f5f5f5; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | } |
| | | |
| | | .info-content { |
| | | padding: 1rem; |
| | | } |
| | | .info-grid { |
| | | display: grid; |
| | | grid-template-columns: 1fr 1fr; |
| | | gap: 20px; |
| | | } |
| | | .info-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | } |
| | | .info-item:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .info-label { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .info-value { |
| | | font-size: 14px; |
| | | color: #303133; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .description-content { |
| | | padding: 1rem; |
| | | font-size: 0.875rem; |
| | | color: #303133; |
| | | line-height: 1.5; |
| | | } |
| | | </style> |
| | |
| | | <text class="detail-label">åºæ¶éé¢(å
)</text> |
| | | <text class="detail-value danger">{{ formatAmount(item.unReceiptPaymentAmount) }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åçæ¥æ</text> |
| | | <text class="detail-value">{{ item.receiptPaymentDate }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |