From 4dca88c6433e3b3e370dce4ba139a3fd0287c020 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期一, 02 二月 2026 16:32:48 +0800
Subject: [PATCH] 安全规程与资质管理、危险源台账模块开发
---
src/api/safeProduction/safeQualifications.js | 61 +
src/pages/index.vue | 45 +
src/pages/safeProduction/hazardSourceLedger/detail.vue | 431 +++++++++++++
src/pages.json | 44 +
src/pages/safeProduction/hazardSourceLedger/index.vue | 291 +++++++++
src/api/safeProduction/hazardSourceLedger.js | 36 +
src/pages/safeProduction/safeQualifications/index.vue | 263 ++++++++
src/pages/safeProduction/safeQualifications/view.vue | 159 +++++
src/pages/safeProduction/hazardSourceLedger/view.vue | 187 ++++++
src/pages/safeProduction/safeQualifications/detail.vue | 317 ++++++++++
10 files changed, 1,832 insertions(+), 2 deletions(-)
diff --git a/src/api/safeProduction/hazardSourceLedger.js b/src/api/safeProduction/hazardSourceLedger.js
new file mode 100644
index 0000000..546415c
--- /dev/null
+++ b/src/api/safeProduction/hazardSourceLedger.js
@@ -0,0 +1,36 @@
+// 鍙戣揣鍙拌处椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ
+export function safeHazardListPage(query) {
+ return request({
+ url: "/safeHazard/page",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏂板鍗遍櫓婧愬彴璐�
+export function safeHazardAdd(query) {
+ return request({
+ url: '/safeHazard',
+ method: 'post',
+ data: query
+ })
+}
+// 淇敼鍗遍櫓婧愬彴璐�
+export function safeHazardUpdate(query) {
+ return request({
+ url: '/safeHazard',
+ method: 'put',
+ data: query
+ })
+}
+// 鍒犻櫎鍗遍櫓婧愬彴璐�
+export function safeHazardDel(ids) {
+ return request({
+ url: '/safeHazard/' + ids,
+ method: 'delete',
+ data: ids
+ })
+}
\ No newline at end of file
diff --git a/src/api/safeProduction/safeQualifications.js b/src/api/safeProduction/safeQualifications.js
new file mode 100644
index 0000000..bde2443
--- /dev/null
+++ b/src/api/safeProduction/safeQualifications.js
@@ -0,0 +1,61 @@
+// 鍙戣揣鍙拌处椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ
+export function qualificationsListPage(query) {
+ return request({
+ url: "/safeCertification/page",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏂板瀹夊叏瑙勭▼涓庤祫璐ㄧ鐞�
+export function safeCertificationAdd(query) {
+ return request({
+ url: '/safeCertification',
+ method: 'post',
+ data: query
+ })
+}
+// 淇敼瀹夊叏瑙勭▼涓庤祫璐ㄧ鐞�
+export function safeCertificationUpdate(query) {
+ return request({
+ url: '/safeCertification',
+ method: 'put',
+ data: query
+ })
+}
+// 鍒犻櫎瀹夊叏瑙勭▼涓庤祫璐ㄧ鐞�
+export function safeCertificationDel(ids) {
+ return request({
+ url: '/safeCertification/' + ids,
+ method: 'delete',
+ data: ids
+ })
+}
+
+// 鏌ヨ闄勪欢鍒楄〃
+export function fileListPage(query) {
+ return request({
+ url: "/safeCertificationFile/listPage",
+ method: "get",
+ params: query,
+ });
+}
+// 娣诲姞闄勪欢
+export function safeCertificationFileAdd(query) {
+ return request({
+ url: '/safeCertificationFile/add',
+ method: 'post',
+ data: query
+ })
+}
+// 鍒犻櫎闄勪欢
+export function safeCertificationFileDel(ids) {
+ return request({
+ url: '/safeCertificationFile/del',
+ method: 'delete',
+ data: ids
+ })
+}
\ No newline at end of file
diff --git a/src/pages.json b/src/pages.json
index e93e39d..02997dd 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -667,7 +667,49 @@
"navigationBarTitleText": "鑷畾涔夊嚭搴�",
"navigationStyle": "custom"
}
- }
+ },
+ {
+ "path": "pages/safeProduction/safeQualifications/index",
+ "style": {
+ "navigationBarTitleText": "瑙勭▼璧勮川",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/safeProduction/safeQualifications/detail",
+ "style": {
+ "navigationBarTitleText": "瑙勭▼璧勮川璇︽儏",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/safeProduction/safeQualifications/view",
+ "style": {
+ "navigationBarTitleText": "瑙勭▼璧勮川璇︽儏",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/safeProduction/hazardSourceLedger/index",
+ "style": {
+ "navigationBarTitleText": "鍗遍櫓婧愬彴璐�",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/safeProduction/hazardSourceLedger/detail",
+ "style": {
+ "navigationBarTitleText": "鍗遍櫓婧愯鎯�",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/safeProduction/hazardSourceLedger/view",
+ "style": {
+ "navigationBarTitleText": "鍗遍櫓婧愯鎯�",
+ "navigationStyle": "custom"
+ }
+ },
],
"subPackages": [
{
diff --git a/src/pages/index.vue b/src/pages/index.vue
index 8a6647f..6be8435 100644
--- a/src/pages/index.vue
+++ b/src/pages/index.vue
@@ -109,6 +109,30 @@
</up-grid>
</view>
</view>
+ <!-- 瀹夊叏鐢熶骇妯″潡 -->
+ <view class="common-module collaboration-module">
+ <view class="module-header">
+ <view class="module-title-container">
+ <text class="module-title">瀹夊叏鐢熶骇</text>
+ </view>
+ </view>
+ <view class="module-content">
+ <up-grid :border="false"
+ col="4">
+ <up-grid-item v-for="(item, index) in safetyItems"
+ :key="index"
+ @click="handleCommonItemClick(item)">
+ <view class="icon-container"
+ :style="{ background: item.bgColor }">
+ <up-icon :name="item.icon"
+ :size="58"
+ color="#ffffff"></up-icon>
+ </view>
+ <text class="item-label">{{item.label}}</text>
+ </up-grid-item>
+ </up-grid>
+ </view>
+ </view>
<!-- 鐢熶骇绠℃帶妯″潡 -->
<!-- <view class="common-module production-module">-->
<!-- <view class="module-header">-->
@@ -275,7 +299,16 @@
label: "渚涘簲鍟嗗線鏉�",
},
]);
-
+ const safetyItems = reactive([
+ {
+ icon: "/static/images/icon/caigoutaizhang@2x.png",
+ label: "瑙勭▼璧勮川",
+ },
+ {
+ icon: "/static/images/icon/caigoutaizhang@2x.png",
+ label: "鍗遍櫓婧愮鐞�",
+ },
+ ]);
// 鍗忓悓鍔炲叕鍔熻兘鏁版嵁
const collaborationItems = reactive([
{
@@ -637,6 +670,16 @@
url: "/pages/equipmentManagement/verification/index",
});
break;
+ case "瑙勭▼璧勮川":
+ uni.navigateTo({
+ url: "/pages/safeProduction/safeQualifications/index",
+ });
+ break;
+ case "鍗遍櫓婧愮鐞�":
+ uni.navigateTo({
+ url: "/pages/safeProduction/hazardSourceLedger/index",
+ });
+ break;
default:
uni.showToast({
title: `鐐瑰嚮浜�${item.label}`,
diff --git a/src/pages/safeProduction/hazardSourceLedger/detail.vue b/src/pages/safeProduction/hazardSourceLedger/detail.vue
new file mode 100644
index 0000000..d9ffde2
--- /dev/null
+++ b/src/pages/safeProduction/hazardSourceLedger/detail.vue
@@ -0,0 +1,431 @@
+<template>
+ <view class="hazard-source-detail">
+ <PageHeader :title="isEdit ? '缂栬緫鍗遍櫓婧�' : '鏂板鍗遍櫓婧�'"
+ @back="goBack" />
+ <u-form @submit="handleSubmit"
+ ref="formRef"
+ label-width="110">
+ <!-- 鍗遍櫓婧愪俊鎭� -->
+ <u-cell-group title="鍗遍櫓婧愪俊鎭�">
+ <u-form-item label="鍗遍櫓婧愬悕绉�"
+ prop="name"
+ required
+ border-bottom>
+ <u-input v-model="form.name"
+ placeholder="璇疯緭鍏ュ嵄闄╂簮鍚嶇О" />
+ </u-form-item>
+ <u-form-item label="鍗遍櫓婧愮紪鐮�"
+ prop="code"
+ required
+ border-bottom>
+ <u-input v-model="form.code"
+ placeholder="璇疯緭鍏ュ嵄闄╂簮缂栫爜" />
+ </u-form-item>
+ <u-form-item label="鍗遍櫓婧愮被鍨�"
+ prop="type"
+ required
+ border-bottom>
+ <u-input v-model="typeName"
+ placeholder="璇烽�夋嫨鍗遍櫓婧愮被鍨�"
+ @click="showTypeSheet"
+ readonly />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showTypeSheet"></up-icon>
+ </template>
+ </u-form-item>
+ <u-form-item label="椋庨櫓绛夌骇"
+ prop="riskLevel"
+ required
+ border-bottom>
+ <u-input v-model="riskLevelName"
+ placeholder="璇烽�夋嫨椋庨櫓绛夌骇"
+ @click="showRiskLevelSheet"
+ readonly />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showRiskLevelSheet"></up-icon>
+ </template>
+ </u-form-item>
+ <u-form-item label="鎵�鍦ㄤ綅缃�"
+ prop="location"
+ required
+ border-bottom>
+ <u-input v-model="form.location"
+ placeholder="璇疯緭鍏ユ墍鍦ㄤ綅缃�" />
+ </u-form-item>
+ <u-form-item label="绠℃帶鎺柦"
+ prop="controlMeasures"
+ required
+ border-bottom>
+ <u-textarea v-model="form.controlMeasures"
+ placeholder="璇疯緭鍏ョ鎺ф帾鏂�"
+ :maxlength="200"
+ count
+ :autoHeight="true" />
+ </u-form-item>
+ <u-form-item label="搴撳瓨鏁伴噺"
+ prop="stockQty"
+ border-bottom>
+ <u-input v-model="form.stockQty"
+ type="number"
+ min="0"
+ @blur="validateStockQty"
+ placeholder="璇疯緭鍏ュ簱瀛樻暟閲�" />
+ </u-form-item>
+ <u-form-item label="绠℃帶璐d换浜�"
+ prop="principalUser"
+ required
+ border-bottom>
+ <u-input v-model="form.principalUser"
+ placeholder="璇烽�夋嫨绠℃帶璐d换浜�"
+ @click="showPrincipalSheet"
+ readonly />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showPrincipalSheet"></up-icon>
+ </template>
+ </u-form-item>
+ <u-form-item label="璐d换浜鸿仈绯荤數璇�"
+ prop="principalMobile"
+ required
+ border-bottom>
+ <u-input v-model="form.principalMobile"
+ placeholder="璇疯緭鍏ヨ矗浠讳汉鑱旂郴鐢佃瘽" />
+ </u-form-item>
+ <u-form-item label="瑙勬牸 / 椋庨櫓鎻忚堪"
+ prop="specInfo"
+ border-bottom>
+ <u-textarea v-model="form.specInfo"
+ placeholder="璇疯緭鍏ヨ鏍� / 椋庨櫓鎻忚堪"
+ :maxlength="200"
+ count
+ :autoHeight="true" />
+ </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-action-sheet :show="typeSheetVisible"
+ :actions="typeOptions"
+ @select="handleTypeSelect"
+ title="閫夋嫨鍗遍櫓婧愮被鍨�" />
+ <!-- 椋庨櫓绛夌骇閫夋嫨鍣� -->
+ <up-action-sheet :show="riskLevelSheetVisible"
+ :actions="riskLevelOptions"
+ @select="handleRiskLevelSelect"
+ title="閫夋嫨椋庨櫓绛夌骇" />
+ <!-- 绠℃帶璐d换浜洪�夋嫨鍣� -->
+ <up-action-sheet :show="principalSheetVisible"
+ :actions="principalOptions"
+ @select="handlePrincipalSelect"
+ title="閫夋嫨绠℃帶璐d换浜�" />
+ </view>
+</template>
+
+<script setup>
+ // 鏇挎崲 toast 鏂规硶
+ defineOptions({ name: "hazard-source-detail" });
+ const showToast = message => {
+ uni.showToast({
+ title: message,
+ icon: "none",
+ });
+ };
+
+ import { ref, onMounted, nextTick } from "vue";
+ import PageHeader from "@/components/PageHeader.vue";
+ import {
+ safeHazardAdd,
+ safeHazardUpdate,
+ } from "@/api/safeProduction/hazardSourceLedger";
+ import { userListNoPageByTenantId } from "@/api/system/user";
+ import useUserStore from "@/store/modules/user";
+ import { useDict } from "@/utils/dict";
+ import { onLoad } from "@dcloudio/uni-app";
+
+ // 鑾峰彇瀛楀吀鏁版嵁
+ const { hazard_source_type } = useDict("hazard_source_type");
+
+ const userStore = useUserStore();
+
+ // 琛ㄥ崟鏁版嵁
+ const form = ref({
+ name: "",
+ code: "",
+ type: "",
+ riskLevel: "",
+ location: "",
+ controlMeasures: "",
+ stockQty: "",
+ principalUser: "",
+ principalUserId: "",
+ principalMobile: "",
+ specInfo: "",
+ });
+
+ // 椤甸潰鐘舵��
+ const loading = ref(false);
+ const formRef = ref(null);
+ const isEdit = ref(false);
+
+ // 鍗遍櫓婧愮被鍨嬮�夋嫨鍣�
+ const typeSheetVisible = ref(false);
+ const typeName = ref("");
+ const typeOptions = ref([]);
+ const showTypeSheet = () => {
+ typeSheetVisible.value = true;
+ };
+ const handleTypeSelect = item => {
+ form.value.type = item.value;
+ typeName.value = item.name;
+ typeSheetVisible.value = false;
+ };
+ const validateStockQty = val => {
+ let numVal = Number(val);
+ if (isNaN(numVal)) {
+ numVal = 0;
+ }
+ if (numVal < 0) {
+ showToast("搴撳瓨鏁伴噺涓嶈兘灏忎簬0");
+ numVal = 0;
+ }
+ // 浣跨敤 nextTick 纭繚瑙嗗浘鏇存柊
+ nextTick(() => {
+ form.value.stockQty = numVal;
+ });
+ };
+
+ // 椋庨櫓绛夌骇閫夋嫨鍣�
+ const riskLevelSheetVisible = ref(false);
+ const riskLevelName = ref("");
+ const riskLevelOptions = ref([]);
+ const showRiskLevelSheet = () => {
+ riskLevelSheetVisible.value = true;
+ };
+ const handleRiskLevelSelect = item => {
+ form.value.riskLevel = item.value;
+ riskLevelName.value = item.name;
+ riskLevelSheetVisible.value = false;
+ };
+
+ // 绠℃帶璐d换浜洪�夋嫨鍣�
+ const principalSheetVisible = ref(false);
+ const userList = ref([]);
+ const principalOptions = ref([]);
+ const showPrincipalSheet = () => {
+ if (principalOptions.value.length === 0) {
+ getUserList();
+ } else {
+ principalSheetVisible.value = true;
+ }
+ };
+ const handlePrincipalSelect = item => {
+ console.log(item, "item");
+ form.value.principalUser = item.name;
+ form.value.principalUserId = item.value;
+ form.value.principalMobile = item.phonenumber;
+ principalSheetVisible.value = false;
+ };
+
+ // 鑾峰彇鐢ㄦ埛鍒楄〃
+ const getUserList = () => {
+ userListNoPageByTenantId().then(res => {
+ if (res.code === 200) {
+ userList.value = res.data;
+ principalOptions.value = res.data.map(user => ({
+ value: user.userId,
+ name: user.nickName,
+ phonenumber: user.phonenumber,
+ }));
+ principalSheetVisible.value = true;
+ }
+ });
+ };
+
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ // 杩斿洖鏃舵竻闄ゆ湰鍦板瓨鍌ㄧ殑鏁版嵁
+ uni.removeStorageSync("hazardSourceLedger");
+ uni.navigateBack();
+ };
+
+ // 鎻愪氦琛ㄥ崟
+ const handleSubmit = async () => {
+ if (!form.value.name) {
+ showToast("璇疯緭鍏ュ嵄闄╂簮鍚嶇О");
+ return;
+ }
+
+ if (!form.value.code) {
+ showToast("璇疯緭鍏ュ嵄闄╂簮缂栫爜");
+ return;
+ }
+
+ if (!form.value.type) {
+ showToast("璇烽�夋嫨鍗遍櫓婧愮被鍨�");
+ return;
+ }
+
+ if (!form.value.riskLevel) {
+ showToast("璇烽�夋嫨椋庨櫓绛夌骇");
+ return;
+ }
+
+ if (!form.value.location) {
+ showToast("璇疯緭鍏ユ墍鍦ㄤ綅缃�");
+ return;
+ }
+
+ if (!form.value.controlMeasures) {
+ showToast("璇疯緭鍏ョ鎺ф帾鏂�");
+ return;
+ }
+
+ if (!form.value.principalUser) {
+ showToast("璇疯緭鍏ョ鎺ц矗浠讳汉");
+ return;
+ }
+
+ if (!form.value.principalMobile) {
+ showToast("璇疯緭鍏ヨ矗浠讳汉鑱旂郴鐢佃瘽");
+ 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];
+ });
+ console.log("submitData", submitData);
+ if (isEdit.value) {
+ const { code } = await safeHazardUpdate(submitData);
+ if (code === 200) {
+ showToast("淇敼鎴愬姛");
+ setTimeout(() => {
+ goBack();
+ }, 500);
+ } else {
+ loading.value = false;
+ showToast("淇敼澶辫触锛岃閲嶈瘯");
+ }
+ } else {
+ const { code } = await safeHazardAdd(submitData);
+ if (code === 200) {
+ showToast("鏂板鎴愬姛");
+ setTimeout(() => {
+ goBack();
+ }, 500);
+ } else {
+ loading.value = false;
+ showToast("鏂板澶辫触锛岃閲嶈瘯");
+ }
+ }
+ } catch (e) {
+ loading.value = false;
+ console.error("鎻愪氦澶辫触:", e);
+ showToast("鎻愪氦澶辫触锛岃閲嶈瘯");
+ }
+ };
+
+ onLoad(() => {
+ // 缂栬緫鍗遍櫓婧愭椂锛屼粠鏈湴瀛樺偍鑾峰彇鏁版嵁
+ const hazardSource = uni.getStorageSync("hazardSourceLedger");
+ console.log("hazardSource", hazardSource);
+ if (hazardSource.id) {
+ form.value = hazardSource;
+ isEdit.value = true;
+ console.log("form.value", form.value);
+ } else {
+ isEdit.value = false;
+ }
+ });
+
+ onMounted(() => {
+ // 鍒濆鍖栭�夐」鏁版嵁
+ typeOptions.value = hazard_source_type.value.map(item => ({
+ value: item.value,
+ name: item.label,
+ }));
+ riskLevelOptions.value = [
+ { value: "浣庨闄�", name: "浣庨闄�" },
+ { value: "涓�鑸闄�", name: "涓�鑸闄�" },
+ { value: "杈冨ぇ椋庨櫓", name: "杈冨ぇ椋庨櫓" },
+ { value: "閲嶅ぇ椋庨櫓", name: "閲嶅ぇ椋庨櫓" },
+ ];
+
+ // 璁剧疆宸查�夊�肩殑鏄剧ず鏂囨湰
+ if (form.value.type) {
+ typeName.value =
+ typeOptions.value.find(item => item.value == form.value.type)?.name || "";
+ }
+ if (form.value.riskLevel) {
+ riskLevelName.value =
+ riskLevelOptions.value.find(item => item.value == form.value.riskLevel)
+ ?.name || "";
+ }
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/static/scss/form-common.scss";
+ .client-visit {
+ min-height: 100vh;
+ background: #f8f9fa;
+ padding-bottom: 5rem;
+ }
+
+ .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 2.5rem 2.5rem 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 2.5rem 2.5rem 2.5rem;
+ }
+
+ .location-icon {
+ color: #1989fa;
+ font-size: 1.2rem;
+ }
+</style>
\ No newline at end of file
diff --git a/src/pages/safeProduction/hazardSourceLedger/index.vue b/src/pages/safeProduction/hazardSourceLedger/index.vue
new file mode 100644
index 0000000..494e3b4
--- /dev/null
+++ b/src/pages/safeProduction/hazardSourceLedger/index.vue
@@ -0,0 +1,291 @@
+<template>
+ <view class="sales-accoun">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="鍗遍櫓婧愬彴璐�"
+ @back="goBack" />
+ <!-- 鎼滅储鍜岀瓫閫夊尯鍩� -->
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input class="search-text"
+ placeholder="璇疯緭鍏ュ嵄闄╂簮鍚嶇О"
+ v-model="customerName"
+ @blur="getList"
+ clearable />
+ </view>
+ <view class="filter-button"
+ @click="getList">
+ <u-icon name="search"
+ size="24"
+ color="#999"></u-icon>
+ </view>
+ </view>
+ </view>
+ <!-- 鎷滆璁板綍鍒楄〃 -->
+ <view class="ledger-list"
+ v-if="visitList.length > 0">
+ <view v-for="(item, index) in visitList"
+ :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.name }}</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.code || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鍗遍櫓婧愮被鍨�</text>
+ <text class="detail-value">{{ hazard_source_type.find(i => i.value === item.type)?.label || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">椋庨櫓绛夌骇</text>
+ <u-tag :type="getRiskLevelType(item.riskLevel)">
+ {{ item.riskLevel || '-' }}
+ </u-tag>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鎵�鍦ㄤ綅缃�</text>
+ <text class="detail-value">{{ item.location || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">绠℃帶鎺柦</text>
+ <text class="detail-value">{{ item.controlMeasures || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">搴撳瓨鏁伴噺</text>
+ <text class="detail-value">{{ item.stockQty || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">绠℃帶璐d换浜�</text>
+ <text class="detail-value">{{ item.principalUser || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">璐d换浜鸿仈绯荤數璇�</text>
+ <text class="detail-value">{{ item.principalMobile || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">瑙勬牸 / 椋庨櫓鎻忚堪</text>
+ <text class="detail-value">{{ item.specInfo || '-' }}</text>
+ </view>
+ </view>
+ <!-- 鎸夐挳鍖哄煙 -->
+ <view class="action-buttons">
+ <u-button type="info"
+ size="small"
+ class="action-btn"
+ @click="viewDetail(item)">
+ 鏌ョ湅璇︽儏
+ </u-button>
+ <u-button type="primary"
+ size="small"
+ class="action-btn"
+ @click="editVisit(item)">
+ 缂栬緫
+ </u-button>
+ <u-button type="error"
+ size="small"
+ class="action-btn"
+ @click="deleteVisit(item)">
+ 鍒犻櫎
+ </u-button>
+ </view>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="no-data">
+ <text>鏆傛棤鎷滆璁板綍</text>
+ </view>
+ <!-- 娴姩鏂板鎸夐挳 -->
+ <view class="fab-button"
+ @click="addVisit">
+ <up-icon name="plus"
+ size="24"
+ color="#ffffff"></up-icon>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { ref, onMounted } from "vue";
+
+ import { onShow } from "@dcloudio/uni-app";
+ import PageHeader from "@/components/PageHeader.vue";
+ import { delCustomer } from "@/api/cooperativeOffice/clientVisit";
+ import {
+ qualificationsListPage,
+ safeCertificationDel,
+ } from "@/api/safeProduction/safeQualifications";
+ import {
+ safeHazardListPage,
+ safeHazardDel,
+ } from "@/api/safeProduction/hazardSourceLedger";
+ import useUserStore from "@/store/modules/user";
+ import { useDict } from "@/utils/dict";
+ // 鏇挎崲 toast 鏂规硶
+ defineOptions({ name: "client-visit-index" });
+ const showToast = message => {
+ uni.showToast({
+ title: message,
+ icon: "none",
+ });
+ };
+ const getRiskLevelType = riskLevel => {
+ const typeMap = {
+ 浣庨闄�: "info",
+ 涓�鑸闄�: "info",
+ 杈冨ぇ椋庨櫓: "warning",
+ 閲嶅ぇ椋庨櫓: "error",
+ };
+ return typeMap[riskLevel] || "info";
+ };
+
+ import dayjs from "dayjs";
+
+ const userStore = useUserStore();
+
+ // 鎼滅储鍏抽敭璇�
+ const customerName = ref("");
+
+ // 鎷滆璁板綍鏁版嵁
+ const visitList = ref([]);
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ // 鏌ヨ鍒楄〃
+ const getList = () => {
+ showLoadingToast("鍔犺浇涓�...");
+ const params = {
+ current: -1,
+ size: -1,
+ name: customerName.value,
+ };
+ safeHazardListPage(params)
+ .then(res => {
+ visitList.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("hazardSourceLedger", {});
+ uni.navigateTo({
+ url: "/pages/safeProduction/hazardSourceLedger/detail",
+ });
+ };
+ // 缂栬緫鍗遍櫓婧�
+ const editVisit = item => {
+ uni.setStorageSync("hazardSourceLedger", item);
+ uni.navigateTo({
+ url: "/pages/safeProduction/hazardSourceLedger/detail",
+ });
+ };
+ // 鍒犻櫎鍗遍櫓婧�
+ const deleteVisit = item => {
+ uni.showModal({
+ title: "鍒犻櫎纭",
+ content: `纭畾瑕佸垹闄よ鍗遍櫓婧愬悧锛焋,
+ success: res => {
+ if (res.confirm) {
+ deleteClientVisit(item.id);
+ }
+ },
+ });
+ };
+ const { hazard_source_type } = useDict("hazard_source_type");
+ const { risk_level } = useDict("risk_level");
+
+ // 鍒犻櫎鍗遍櫓婧愯褰�
+ const deleteClientVisit = id => {
+ showLoadingToast("鍒犻櫎涓�...");
+ safeHazardDel([id])
+ .then(() => {
+ closeToast();
+ showToast("鍒犻櫎鎴愬姛");
+ getList();
+ })
+ .catch(() => {
+ closeToast();
+ showToast("鍒犻櫎澶辫触");
+ });
+ };
+ // 鏌ョ湅璇︽儏
+ const viewDetail = item => {
+ uni.setStorageSync("hazardSourceLedger", item);
+ uni.navigateTo({
+ url: "/pages/safeProduction/hazardSourceLedger/view",
+ });
+ };
+
+ onMounted(() => {
+ 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;
+ }
+
+ // 鐗瑰畾鐨勫浘鏍囨牱寮�
+ .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); // 淇濇寔椤甸潰鐗规湁鐨勯槾褰辨晥鏋�
+ }
+</style>
+
diff --git a/src/pages/safeProduction/hazardSourceLedger/view.vue b/src/pages/safeProduction/hazardSourceLedger/view.vue
new file mode 100644
index 0000000..441aeb6
--- /dev/null
+++ b/src/pages/safeProduction/hazardSourceLedger/view.vue
@@ -0,0 +1,187 @@
+<template>
+ <view class="hazard-source-detail">
+ <PageHeader title="鍗遍櫓婧愯鎯�"
+ @back="goBack" />
+ <!-- 鍐呭瀹瑰櫒 -->
+ <view class="content-container">
+ <!-- 鍗遍櫓婧愪俊鎭� -->
+ <view class="section">
+ <view class="section-title">鍗遍櫓婧愪俊鎭�</view>
+ <view class="info-item">
+ <text class="info-label">鍗遍櫓婧愬悕绉�</text>
+ <text class="info-value">{{ form.name || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鍗遍櫓婧愮紪鐮�</text>
+ <text class="info-value">{{ form.code || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鍗遍櫓婧愮被鍨�</text>
+ <text class="info-value">{{ hazard_source_type.find(item => item.value === form.type)?.label || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">椋庨櫓绛夌骇</text>
+ <text class="info-value"><u-tag :type="getRiskLevelType(form.riskLevel)">
+ {{ form.riskLevel || '-' }}
+ </u-tag></text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鎵�鍦ㄤ綅缃�</text>
+ <text class="info-value">{{ form.location || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">绠℃帶鎺柦</text>
+ <text class="info-value">{{ form.controlMeasures || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">搴撳瓨鏁伴噺</text>
+ <text class="info-value">{{ form.stockQty || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">绠℃帶璐d换浜�</text>
+ <text class="info-value">{{ form.principalUser || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">璐d换浜鸿仈绯荤數璇�</text>
+ <text class="info-value">{{ form.principalMobile || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">瑙勬牸 / 椋庨櫓鎻忚堪</text>
+ <text class="info-value">{{ form.specInfo || '-' }}</text>
+ </view>
+ </view>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ // 鏇挎崲 toast 鏂规硶
+ const showToast = message => {
+ uni.showToast({
+ title: message,
+ icon: "none",
+ });
+ };
+
+ import { ref, onMounted } from "vue";
+ import PageHeader from "@/components/PageHeader.vue";
+ import useUserStore from "@/store/modules/user";
+ import { useDict } from "@/utils/dict";
+
+ const userStore = useUserStore();
+
+ // 鑾峰彇瀛楀吀鏁版嵁
+ const { hazard_source_type } = useDict("hazard_source_type");
+ const { risk_level } = useDict("risk_level");
+
+ // 琛ㄥ崟鏁版嵁
+ const form = ref({
+ name: "",
+ code: "",
+ type: "",
+ riskLevel: "",
+ location: "",
+ controlMeasures: "",
+ stockQty: "",
+ principalUser: "",
+ principalMobile: "",
+ specInfo: "",
+ });
+
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ // 杩斿洖鏃舵竻闄ゆ湰鍦板瓨鍌ㄧ殑鏁版嵁
+ uni.removeStorageSync("hazardSourceLedger");
+ uni.navigateBack();
+ };
+ const getRiskLevelType = riskLevel => {
+ const typeMap = {
+ 浣庨闄�: "info",
+ 涓�鑸闄�: "info",
+ 杈冨ぇ椋庨櫓: "warning",
+ 閲嶅ぇ椋庨櫓: "error",
+ };
+ return typeMap[riskLevel] || "info";
+ };
+
+ // 鍒濆鍖栭〉闈㈡暟鎹�
+ const initPageData = () => {
+ // 浠庢湰鍦板瓨鍌ㄨ幏鍙栧嵄闄╂簮璇︽儏
+ const row = uni.getStorageSync("hazardSourceLedger");
+ if (row) {
+ form.value = { ...row };
+ } else {
+ showToast("鏆傛棤鍗遍櫓婧愭暟鎹�");
+ }
+ };
+
+ onMounted(() => {
+ initPageData();
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/static/scss/form-common.scss";
+
+ .client-visit-detail {
+ min-height: 100vh;
+ background-color: #f8f9fa;
+ }
+
+ .content-container {
+ padding: 16px;
+ }
+
+ .section {
+ background-color: #ffffff;
+ border-radius: 12px;
+ margin-bottom: 16px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+ }
+
+ .section-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333333;
+ padding: 16px 16px 12px;
+ border-bottom: 1px solid #f0f0f0;
+ }
+
+ .info-item {
+ display: flex;
+ padding: 14px 16px;
+ border-bottom: 1px solid #f8f8f8;
+ align-items: flex-start;
+ }
+
+ .info-item:last-child {
+ border-bottom: none;
+ }
+
+ .info-label {
+ font-size: 14px;
+ color: #666666;
+ min-width: 80px;
+ flex-shrink: 0;
+ line-height: 22px;
+ }
+
+ .info-value {
+ font-size: 14px;
+ color: #333333;
+ flex: 1;
+ line-height: 22px;
+ text-align: right;
+ }
+
+ .multi-line {
+ text-align: left;
+ word-break: break-all;
+ line-height: 1.6;
+ }
+
+ .remark-item {
+ padding-bottom: 16px;
+ }
+</style>
\ No newline at end of file
diff --git a/src/pages/safeProduction/safeQualifications/detail.vue b/src/pages/safeProduction/safeQualifications/detail.vue
new file mode 100644
index 0000000..affa43e
--- /dev/null
+++ b/src/pages/safeProduction/safeQualifications/detail.vue
@@ -0,0 +1,317 @@
+<template>
+ <view class="safe-qualifications-detail">
+ <PageHeader :title="isEdit ? '缂栬緫瑙勭▼璧勮川' : '鏂板瑙勭▼璧勮川'"
+ @back="goBack" />
+ <u-form @submit="handleSubmit"
+ ref="formRef"
+ label-width="110">
+ <!-- 瑙勭▼璧勮川淇℃伅 -->
+ <u-cell-group title="瑙勭▼璧勮川淇℃伅">
+ <u-form-item label="瑙勭▼璧勮川鍚嶇О"
+ prop="name"
+ required
+ border-bottom>
+ <u-input v-model="form.name"
+ placeholder="璇疯緭鍏ヨ绋嬭祫璐ㄥ悕绉�" />
+ </u-form-item>
+ <u-form-item label="瑙勭▼璧勮川缂栧彿"
+ prop="code"
+ required
+ border-bottom>
+ <u-input v-model="form.code"
+ placeholder="璇疯緭鍏ヨ绋嬭祫璐ㄧ紪鍙�" />
+ </u-form-item>
+ <u-form-item label="瑙勭▼璧勮川绫诲瀷"
+ prop="type"
+ required
+ border-bottom>
+ <u-input v-model="typeName"
+ placeholder="璇烽�夋嫨瑙勭▼璧勮川绫诲瀷"
+ @click="showTypeSheet"
+ readonly />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showTypeSheet"></up-icon>
+ </template>
+ </u-form-item>
+ <u-form-item label="鐗堟湰鍙�"
+ prop="version"
+ required
+ border-bottom>
+ <u-input v-model="form.version"
+ placeholder="璇疯緭鍏ョ増鏈彿" />
+ </u-form-item>
+ <u-form-item label="鏈夋晥鏈�"
+ prop="effectiveTime"
+ required
+ border-bottom>
+ <u-input v-model="form.effectiveTime"
+ placeholder="璇烽�夋嫨鏈夋晥鏈�"
+ @click="showTimePicker" />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showTimePicker"></up-icon>
+ </template>
+ </u-form-item>
+ <u-form-item label="澶囨敞"
+ prop="remark"
+ border-bottom>
+ <u-textarea v-model="form.remark"
+ placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�"
+ :maxlength="200"
+ count
+ :autoHeight="true" />
+ </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="showTime"
+ v-model="currentTime"
+ @confirm="onTimeConfirm"
+ @cancel="showTime = false"
+ mode="date" />
+ <!-- 瑙勭▼璧勮川绫诲瀷閫夋嫨鍣� -->
+ <up-action-sheet :show="typeSheetVisible"
+ :actions="typeOptions"
+ @select="handleTypeSelect"
+ title="閫夋嫨瑙勭▼璧勮川绫诲瀷" />
+ </view>
+</template>
+
+<script setup>
+ // 鏇挎崲 toast 鏂规硶
+ defineOptions({ name: "safe-qualifications-detail" });
+ const showToast = message => {
+ uni.showToast({
+ title: message,
+ icon: "none",
+ });
+ };
+
+ import { ref, onMounted } from "vue";
+ import PageHeader from "@/components/PageHeader.vue";
+ import {
+ safeCertificationAdd,
+ safeCertificationUpdate,
+ } from "@/api/safeProduction/safeQualifications";
+ import useUserStore from "@/store/modules/user";
+ import { useDict } from "@/utils/dict";
+ import dayjs from "dayjs";
+ import { onLoad } from "@dcloudio/uni-app";
+
+ // 鑾峰彇瑙勭▼璧勮川绫诲瀷瀛楀吀
+ const { type_qualification } = useDict("type_qualification");
+
+ const userStore = useUserStore();
+
+ // 琛ㄥ崟鏁版嵁
+ const form = ref({
+ name: "",
+ code: "",
+ type: "",
+ version: "",
+ remark: "",
+ effectiveTime: "",
+ });
+
+ // 椤甸潰鐘舵��
+ const loading = ref(false);
+ const formRef = ref(null);
+ const isEdit = ref(false);
+
+ // 鏃堕棿鐩稿叧
+ const currentTime = ref(Date.now());
+ const showTime = ref(false);
+
+ // 瑙勭▼璧勮川绫诲瀷閫夋嫨鍣�
+ const typeSheetVisible = ref(false);
+ const showTypeSheet = () => {
+ typeSheetVisible.value = true;
+ };
+ const typeName = ref("");
+ const handleTypeSelect = item => {
+ form.value.type = item.value;
+ typeName.value = item.name;
+ typeSheetVisible.value = false;
+ };
+
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ // 杩斿洖鏃舵竻闄ゆ湰鍦板瓨鍌ㄧ殑鏁版嵁
+ uni.removeStorageSync("safeQualifications");
+ uni.navigateBack();
+ };
+
+ // 鏄剧ず鏃堕棿閫夋嫨鍣�
+ const showTimePicker = () => {
+ showTime.value = true;
+ };
+
+ // 纭鏃堕棿閫夋嫨
+ const onTimeConfirm = e => {
+ form.value.effectiveTime = dayjs(e.value).format("YYYY-MM-DD");
+ currentTime.value = e.value;
+ showTime.value = false;
+ };
+
+ // 鎻愪氦琛ㄥ崟
+ const handleSubmit = async () => {
+ if (!form.value.name) {
+ showToast("璇疯緭鍏ヨ绋嬭祫璐ㄥ悕绉�");
+ return;
+ }
+
+ if (!form.value.code) {
+ showToast("璇疯緭鍏ヨ绋嬭祫璐ㄧ紪鍙�");
+ return;
+ }
+
+ if (!form.value.type) {
+ showToast("璇疯緭鍏ヨ绋嬭祫璐ㄧ被鍨�");
+ return;
+ }
+
+ if (!form.value.version) {
+ showToast("璇疯緭鍏ョ増鏈彿");
+ return;
+ }
+
+ if (!form.value.effectiveTime) {
+ showToast("璇烽�夋嫨鏈夋晥鏈�");
+ 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];
+ });
+ console.log("submitData", submitData);
+ if (isEdit.value) {
+ const { code } = await safeCertificationUpdate(submitData);
+ if (code === 200) {
+ showToast("淇敼鎴愬姛");
+ setTimeout(() => {
+ goBack();
+ }, 500);
+ } else {
+ loading.value = false;
+ showToast("淇敼澶辫触锛岃閲嶈瘯");
+ }
+ } else {
+ const { code } = await safeCertificationAdd(submitData);
+ if (code === 200) {
+ showToast("鏂板鎴愬姛");
+ setTimeout(() => {
+ goBack();
+ }, 500);
+ } else {
+ loading.value = false;
+ showToast("鏂板澶辫触锛岃閲嶈瘯");
+ }
+ }
+ } catch (e) {
+ loading.value = false;
+ console.error("鎻愪氦澶辫触:", e);
+ showToast("鎻愪氦澶辫触锛岃閲嶈瘯");
+ }
+ };
+
+ onLoad(() => {
+ // 缂栬緫瑙勭▼璧勮川鏃讹紝浠庢湰鍦板瓨鍌ㄨ幏鍙栨暟鎹�
+ const qualification = uni.getStorageSync("safeQualifications");
+ if (qualification) {
+ form.value = qualification;
+ isEdit.value = true;
+
+ console.log("form.value", form.value);
+ } else {
+ isEdit.value = false;
+ }
+ });
+
+ // 鍒濆鍖栭〉闈㈡暟鎹�
+ const initPageData = () => {
+ // 璁剧疆榛樿鏈夋晥鏈熶负褰撳墠鏃堕棿
+ if (!isEdit.value) {
+ form.value.effectiveTime = dayjs().format("YYYY-MM-DD");
+ currentTime.value = Date.now();
+ }
+ };
+ const typeOptions = ref([]);
+ onMounted(() => {
+ initPageData();
+ typeOptions.value = type_qualification.value.map(item => ({
+ value: item.value,
+ name: item.label,
+ }));
+ if (form.value.type) {
+ typeName.value =
+ typeOptions.value.find(item => item.value == form.value.type)?.name || "";
+ }
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/static/scss/form-common.scss";
+ .client-visit {
+ min-height: 100vh;
+ background: #f8f9fa;
+ padding-bottom: 5rem;
+ }
+
+ .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 2.5rem 2.5rem 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 2.5rem 2.5rem 2.5rem;
+ }
+
+ .location-icon {
+ color: #1989fa;
+ font-size: 1.2rem;
+ }
+</style>
\ No newline at end of file
diff --git a/src/pages/safeProduction/safeQualifications/index.vue b/src/pages/safeProduction/safeQualifications/index.vue
new file mode 100644
index 0000000..4014791
--- /dev/null
+++ b/src/pages/safeProduction/safeQualifications/index.vue
@@ -0,0 +1,263 @@
+<template>
+ <view class="sales-accoun">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="瑙勭▼璧勮川"
+ @back="goBack" />
+ <!-- 鎼滅储鍜岀瓫閫夊尯鍩� -->
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input class="search-text"
+ placeholder="璇疯緭鍏ヨ绋嬭祫璐ㄥ悕绉�"
+ v-model="customerName"
+ @blur="getList"
+ clearable />
+ </view>
+ <view class="filter-button"
+ @click="getList">
+ <u-icon name="search"
+ size="24"
+ color="#999"></u-icon>
+ </view>
+ </view>
+ </view>
+ <!-- 鎷滆璁板綍鍒楄〃 -->
+ <view class="ledger-list"
+ v-if="visitList.length > 0">
+ <view v-for="(item, index) in visitList"
+ :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.name }}</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.code || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">瑙勭▼璧勮川绫诲瀷</text>
+ <text class="detail-value">{{ type_qualification.find(itemItem => itemItem.value === item.type)?.label || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鐗堟湰鍙�</text>
+ <text class="detail-value">{{ item.version || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">澶囨敞</text>
+ <text class="detail-value">{{ item.remark || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鏈夋晥鏈�</text>
+ <text class="detail-value">{{ item.effectiveTime || '-' }}</text>
+ </view>
+ </view>
+ <!-- 鎸夐挳鍖哄煙 -->
+ <view class="action-buttons">
+ <u-button type="info"
+ size="small"
+ class="action-btn"
+ @click="viewDetail(item)">
+ 鏌ョ湅璇︽儏
+ </u-button>
+ <u-button type="primary"
+ size="small"
+ class="action-btn"
+ @click="editVisit(item)">
+ 缂栬緫
+ </u-button>
+ <u-button type="error"
+ size="small"
+ class="action-btn"
+ @click="deleteVisit(item)">
+ 鍒犻櫎
+ </u-button>
+ </view>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="no-data">
+ <text>鏆傛棤鎷滆璁板綍</text>
+ </view>
+ <!-- 娴姩鏂板鎸夐挳 -->
+ <view class="fab-button"
+ @click="addVisit">
+ <up-icon name="plus"
+ size="24"
+ color="#ffffff"></up-icon>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { ref, onMounted } from "vue";
+ import { onShow } from "@dcloudio/uni-app";
+ import PageHeader from "@/components/PageHeader.vue";
+ import { delCustomer } from "@/api/cooperativeOffice/clientVisit";
+ import {
+ qualificationsListPage,
+ safeCertificationAdd,
+ safeCertificationUpdate,
+ safeCertificationDel,
+ fileListPage,
+ safeCertificationFileAdd,
+ safeCertificationFileDel,
+ } from "@/api/safeProduction/safeQualifications";
+
+ import useUserStore from "@/store/modules/user";
+ import { useDict } from "@/utils/dict";
+ // 鏇挎崲 toast 鏂规硶
+ defineOptions({ name: "client-visit-index" });
+ const showToast = message => {
+ uni.showToast({
+ title: message,
+ icon: "none",
+ });
+ };
+
+ import dayjs from "dayjs";
+
+ const userStore = useUserStore();
+
+ // 鎼滅储鍏抽敭璇�
+ const customerName = ref("");
+
+ // 鎷滆璁板綍鏁版嵁
+ const visitList = ref([]);
+ const { type_qualification } = useDict("type_qualification");
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ // 鏌ヨ鍒楄〃
+ const getList = () => {
+ showLoadingToast("鍔犺浇涓�...");
+ const params = {
+ current: -1,
+ size: -1,
+ name: customerName.value,
+ };
+ qualificationsListPage(params)
+ .then(res => {
+ visitList.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("safeQualifications", {});
+ uni.navigateTo({
+ url: "/pages/safeProduction/safeQualifications/detail",
+ });
+ };
+ // 缂栬緫瑙勭▼璧勮川
+ const editVisit = item => {
+ uni.setStorageSync("safeQualifications", item);
+ uni.navigateTo({
+ url: "/pages/safeProduction/safeQualifications/detail",
+ });
+ };
+ // 鍒犻櫎瑙勭▼璧勮川
+ const deleteVisit = item => {
+ uni.showModal({
+ title: "鍒犻櫎纭",
+ content: `纭畾瑕佸垹闄よ瑙勭▼璧勮川鍚楋紵`,
+ success: res => {
+ if (res.confirm) {
+ deleteClientVisit(item.id);
+ }
+ },
+ });
+ };
+ // 鍒犻櫎瑙勭▼璧勮川璁板綍
+ const deleteClientVisit = id => {
+ showLoadingToast("鍒犻櫎涓�...");
+ safeCertificationDel([id])
+ .then(() => {
+ closeToast();
+ showToast("鍒犻櫎鎴愬姛");
+ getList();
+ })
+ .catch(() => {
+ closeToast();
+ showToast("鍒犻櫎澶辫触");
+ });
+ };
+ // 鏌ョ湅璇︽儏
+ const viewDetail = item => {
+ uni.setStorageSync("safeQualifications", item);
+ uni.navigateTo({
+ url: "/pages/safeProduction/safeQualifications/view",
+ });
+ };
+
+ onMounted(() => {
+ 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;
+ }
+
+ // 鐗瑰畾鐨勫浘鏍囨牱寮�
+ .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); // 淇濇寔椤甸潰鐗规湁鐨勯槾褰辨晥鏋�
+ }
+</style>
+
diff --git a/src/pages/safeProduction/safeQualifications/view.vue b/src/pages/safeProduction/safeQualifications/view.vue
new file mode 100644
index 0000000..66501e0
--- /dev/null
+++ b/src/pages/safeProduction/safeQualifications/view.vue
@@ -0,0 +1,159 @@
+<template>
+ <view class="safe-qualifications-detail">
+ <PageHeader title="瑙勭▼璧勮川璇︽儏"
+ @back="goBack" />
+ <!-- 鍐呭瀹瑰櫒 -->
+ <view class="content-container">
+ <!-- 瑙勭▼璧勮川淇℃伅 -->
+ <view class="section">
+ <view class="section-title">瑙勭▼璧勮川淇℃伅</view>
+ <view class="info-item">
+ <text class="info-label">瑙勭▼璧勮川鍚嶇О</text>
+ <text class="info-value">{{ form.name || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">瑙勭▼璧勮川缂栧彿</text>
+ <text class="info-value">{{ form.code || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">瑙勭▼璧勮川绫诲瀷</text>
+ <text class="info-value">{{ type_qualification.find(item => item.value === form.type)?.label || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鐗堟湰鍙�</text>
+ <text class="info-value">{{ form.version || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鏈夋晥鏈�</text>
+ <text class="info-value">{{ form.effectiveTime || '-' }}</text>
+ </view>
+ </view>
+ <!-- 澶囨敞淇℃伅 -->
+ <view class="section">
+ <view class="section-title">澶囨敞淇℃伅</view>
+ <view class="info-item remark-item">
+ <text class="info-label">澶囨敞</text>
+ <text class="info-value multi-line">{{ form.remark || '-' }}</text>
+ </view>
+ </view>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ // 鏇挎崲 toast 鏂规硶
+ const showToast = message => {
+ uni.showToast({
+ title: message,
+ icon: "none",
+ });
+ };
+
+ import { ref, onMounted } from "vue";
+ import PageHeader from "@/components/PageHeader.vue";
+ import useUserStore from "@/store/modules/user";
+ import { useDict } from "@/utils/dict";
+
+ const userStore = useUserStore();
+
+ // 瑙勭▼璧勮川绫诲瀷瀛楀吀
+ const { type_qualification } = useDict("type_qualification");
+
+ // 琛ㄥ崟鏁版嵁
+ const form = ref({
+ name: "",
+ code: "",
+ type: "",
+ version: "",
+ remark: "",
+ effectiveTime: "",
+ });
+
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ // 杩斿洖鏃舵竻闄ゆ湰鍦板瓨鍌ㄧ殑鏁版嵁
+ uni.removeStorageSync("safeQualifications");
+ uni.navigateBack();
+ };
+
+ // 鍒濆鍖栭〉闈㈡暟鎹�
+ const initPageData = () => {
+ // 浠庢湰鍦板瓨鍌ㄨ幏鍙栬绋嬭祫璐ㄨ鎯�
+ const row = uni.getStorageSync("safeQualifications");
+ if (row) {
+ form.value = { ...row };
+ } else {
+ showToast("鏆傛棤瑙勭▼璧勮川鏁版嵁");
+ }
+ };
+
+ onMounted(() => {
+ initPageData();
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/static/scss/form-common.scss";
+
+ .client-visit-detail {
+ min-height: 100vh;
+ background-color: #f8f9fa;
+ }
+
+ .content-container {
+ padding: 16px;
+ }
+
+ .section {
+ background-color: #ffffff;
+ border-radius: 12px;
+ margin-bottom: 16px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+ }
+
+ .section-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333333;
+ padding: 16px 16px 12px;
+ border-bottom: 1px solid #f0f0f0;
+ }
+
+ .info-item {
+ display: flex;
+ padding: 14px 16px;
+ border-bottom: 1px solid #f8f8f8;
+ align-items: flex-start;
+ }
+
+ .info-item:last-child {
+ border-bottom: none;
+ }
+
+ .info-label {
+ font-size: 14px;
+ color: #666666;
+ min-width: 80px;
+ flex-shrink: 0;
+ line-height: 22px;
+ }
+
+ .info-value {
+ font-size: 14px;
+ color: #333333;
+ flex: 1;
+ line-height: 22px;
+ text-align: right;
+ }
+
+ .multi-line {
+ text-align: left;
+ word-break: break-all;
+ line-height: 1.6;
+ }
+
+ .remark-item {
+ padding-bottom: 16px;
+ }
+</style>
\ No newline at end of file
--
Gitblit v1.9.3