新增食堂巡查
This commit is contained in:
parent
87d90244aa
commit
ee5e5c335f
30
src/api/base/hcApi.ts
Normal file
30
src/api/base/hcApi.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// 食堂巡查相关API接口
|
||||||
|
import { get, post } from "@/utils/request";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询食堂巡查记录
|
||||||
|
*/
|
||||||
|
export function hcFindPageApi(params: any) {
|
||||||
|
return get("/api/hc/findPage", params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增/修改食堂巡查记录
|
||||||
|
*/
|
||||||
|
export function hcSaveApi(params: any) {
|
||||||
|
return post("/api/hc/save", params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id查询食堂巡查记录
|
||||||
|
*/
|
||||||
|
export function hcFindByIdApi(params: any) {
|
||||||
|
return get("/api/hc/findById", params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除食堂巡查记录
|
||||||
|
*/
|
||||||
|
export function hcLogicDeleteApi(params: any) {
|
||||||
|
return post("/api/hc/logicDelete", params);
|
||||||
|
}
|
||||||
@ -181,6 +181,27 @@
|
|||||||
"enablePullDownRefresh": false
|
"enablePullDownRefresh": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/view/routine/ShiTangXunCha/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "食堂巡查",
|
||||||
|
"enablePullDownRefresh": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/view/routine/ShiTangXunCha/add",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "新增食堂巡查",
|
||||||
|
"enablePullDownRefresh": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/view/routine/ShiTangXunCha/detail",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "食堂巡查详情",
|
||||||
|
"enablePullDownRefresh": false
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/view/routine/JiFenPingJia/detail",
|
"path": "pages/view/routine/JiFenPingJia/detail",
|
||||||
"style": {
|
"style": {
|
||||||
|
|||||||
@ -181,6 +181,7 @@ const sections = reactive<Section[]>([
|
|||||||
show: true,
|
show: true,
|
||||||
path: "/pages/view/routine/GongZuoLiang/index",
|
path: "/pages/view/routine/GongZuoLiang/index",
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// id: "r4",
|
// id: "r4",
|
||||||
// icon: "file-paper-2-fill",
|
// icon: "file-paper-2-fill",
|
||||||
@ -195,7 +196,13 @@ const sections = reactive<Section[]>([
|
|||||||
show: true,
|
show: true,
|
||||||
path: "/pages/view/routine/RengJiaoRengZhi/index",
|
path: "/pages/view/routine/RengJiaoRengZhi/index",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "r9",
|
||||||
|
icon: "hc-fill",
|
||||||
|
text: "食堂巡查",
|
||||||
|
show: true,
|
||||||
|
path: "/pages/view/routine/ShiTangXunCha/index",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "r6",
|
id: "r6",
|
||||||
icon: "pass-pending-fill",
|
icon: "pass-pending-fill",
|
||||||
@ -252,13 +259,13 @@ const sections = reactive<Section[]>([
|
|||||||
show: true,
|
show: true,
|
||||||
path: "/pages/view/homeSchool/parentAddressBook/index",
|
path: "/pages/view/homeSchool/parentAddressBook/index",
|
||||||
},
|
},
|
||||||
{
|
/*{
|
||||||
id: "hs4",
|
id: "hs4",
|
||||||
icon: "newspaper-fill",
|
icon: "newspaper-fill",
|
||||||
text: "通知列表",
|
text: "通知列表",
|
||||||
show: true,
|
show: true,
|
||||||
path: "/pages/view/notice/index",
|
path: "/pages/view/notice/index",
|
||||||
},
|
},*/
|
||||||
{
|
{
|
||||||
id: "hs6",
|
id: "hs6",
|
||||||
icon: "filechart2fil",
|
icon: "filechart2fil",
|
||||||
|
|||||||
@ -207,7 +207,7 @@ watch(studentList, (list) => {
|
|||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.notice-detail-page {
|
.notice-detail-page {
|
||||||
background-color: #f4f5f7;
|
background-color: #f4f5f7;
|
||||||
min-height: 100vh;
|
padding-bottom: 70px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -130,6 +130,21 @@
|
|||||||
</picker>
|
</picker>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 回执说明 -->
|
||||||
|
<view class="info-card" v-if="formData.signatureRequired">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">回执说明</text>
|
||||||
|
<uni-easyinput
|
||||||
|
type="textarea"
|
||||||
|
autoHeight
|
||||||
|
v-model="formData.mdhz"
|
||||||
|
placeholder="请输入回执说明"
|
||||||
|
:inputBorder="false"
|
||||||
|
class="receipt-input"
|
||||||
|
></uni-easyinput>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="info-card list-item-card">
|
<view class="info-card list-item-card">
|
||||||
<uni-datetime-picker type="datetime" v-model="formData.startTime">
|
<uni-datetime-picker type="datetime" v-model="formData.startTime">
|
||||||
<view class="list-item-row">
|
<view class="list-item-row">
|
||||||
@ -159,9 +174,6 @@
|
|||||||
<view class="bottom-actions">
|
<view class="bottom-actions">
|
||||||
<button class="action-btn draft-btn" @click="saveDraft">
|
<button class="action-btn draft-btn" @click="saveDraft">
|
||||||
保存草稿
|
保存草稿
|
||||||
</button>
|
|
||||||
<button class="action-btn preview-btn" @click="previewNotice">
|
|
||||||
预览
|
|
||||||
</button>
|
</button>
|
||||||
<button class="action-btn publish-btn" @click="publishNotice" :disabled="isPublishing">
|
<button class="action-btn publish-btn" @click="publishNotice" :disabled="isPublishing">
|
||||||
{{ isPublishing ? '发布中...' : '立即发布' }}
|
{{ isPublishing ? '发布中...' : '立即发布' }}
|
||||||
@ -222,7 +234,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, reactive, computed, nextTick } from "vue";
|
import { ref, reactive, computed, nextTick, watch } from "vue";
|
||||||
import { onLoad, onShow } from "@dcloudio/uni-app";
|
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||||
import CustomUpload from "/src/components/BasicUpload/CustomUpload.vue";
|
import CustomUpload from "/src/components/BasicUpload/CustomUpload.vue";
|
||||||
import BasicTree from "@/components/BasicTree/Tree.vue";
|
import BasicTree from "@/components/BasicTree/Tree.vue";
|
||||||
@ -254,6 +266,7 @@ interface FormData {
|
|||||||
signatureRequired: boolean;
|
signatureRequired: boolean;
|
||||||
startTime: string;
|
startTime: string;
|
||||||
endTime: string;
|
endTime: string;
|
||||||
|
mdhz: string; // 回执说明
|
||||||
}
|
}
|
||||||
|
|
||||||
const noticeId = ref<string | null>(null);
|
const noticeId = ref<string | null>(null);
|
||||||
@ -273,6 +286,7 @@ const formData = reactive<FormData>({
|
|||||||
signatureRequired: true,
|
signatureRequired: true,
|
||||||
startTime: "",
|
startTime: "",
|
||||||
endTime: "",
|
endTime: "",
|
||||||
|
mdhz: "", // 新增回执说明字段
|
||||||
});
|
});
|
||||||
|
|
||||||
const signatureOptions = ["启用" , "不启用"];
|
const signatureOptions = ["启用" , "不启用"];
|
||||||
@ -281,7 +295,7 @@ const signatureStatusText = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 树形数据
|
// 树形数据
|
||||||
const treeData = ref([]);
|
const treeData = ref<any[]>([]);
|
||||||
const treeRef = ref();
|
const treeRef = ref();
|
||||||
|
|
||||||
// 学生显示相关
|
// 学生显示相关
|
||||||
@ -296,14 +310,23 @@ const displayNames = computed(() => {
|
|||||||
// 添加 ref 来引用封面上传组件
|
// 添加 ref 来引用封面上传组件
|
||||||
const coverUploadRef = ref();
|
const coverUploadRef = ref();
|
||||||
|
|
||||||
|
// 统一接口返回类型声明
|
||||||
|
interface ApiResponse<T = any> {
|
||||||
|
resultCode: number;
|
||||||
|
result?: T;
|
||||||
|
resultMsg?: string;
|
||||||
|
data?: any;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
// 加载树形数据
|
// 加载树形数据
|
||||||
const loadTreeData = async () => {
|
const loadTreeData = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await findAllNjBjTree();
|
const res = await findAllNjBjTree() as ApiResponse<any[]>;
|
||||||
if (res.resultCode === 1 && res.result) {
|
if (res && res.resultCode === 1 && res.result) {
|
||||||
// 递归转换数据格式以适配 BasicTree 组件,同时保留 njmcId 字段
|
// 递归转换数据格式以适配 BasicTree 组件,同时保留 njmcId 字段
|
||||||
const convertTreeData = (items: any[]): any[] => {
|
const convertTreeData = (items: Array<{ key: string; title: string; njmcId?: string; children?: any[] }>): any[] => {
|
||||||
return items.map((item: any) => ({
|
return items.map((item) => ({
|
||||||
key: item.key,
|
key: item.key,
|
||||||
title: item.title,
|
title: item.title,
|
||||||
njmcId: item.njmcId, // 保留 njmcId 字段
|
njmcId: item.njmcId, // 保留 njmcId 字段
|
||||||
@ -355,9 +378,10 @@ const resetForm = () => {
|
|||||||
targetNjIds: [],
|
targetNjIds: [],
|
||||||
targetNjmcIds: [],
|
targetNjmcIds: [],
|
||||||
targetBjIds: [],
|
targetBjIds: [],
|
||||||
signatureRequired: false,
|
signatureRequired: true, // 修正:与初始值保持一致
|
||||||
startTime: "",
|
startTime: "",
|
||||||
endTime: "",
|
endTime: "",
|
||||||
|
mdhz: "", // 重置回执说明
|
||||||
});
|
});
|
||||||
|
|
||||||
// 重置发布状态
|
// 重置发布状态
|
||||||
@ -384,9 +408,7 @@ const resetForm = () => {
|
|||||||
const loadJlData = async (jlId: string) => {
|
const loadJlData = async (jlId: string) => {
|
||||||
try {
|
try {
|
||||||
uni.showLoading({ title: "加载数据中..." });
|
uni.showLoading({ title: "加载数据中..." });
|
||||||
|
const response = await jlFindByIdApi({ id: jlId }) as ApiResponse<any>;
|
||||||
// 调用获取接龙详情的接口
|
|
||||||
const response = await jlFindByIdApi({ id: jlId });
|
|
||||||
|
|
||||||
if (response && response.resultCode === 1 && response.result) {
|
if (response && response.resultCode === 1 && response.result) {
|
||||||
const jlData = response.result;
|
const jlData = response.result;
|
||||||
@ -399,14 +421,15 @@ const loadJlData = async (jlId: string) => {
|
|||||||
formData.startTime = jlData.jlkstime || "";
|
formData.startTime = jlData.jlkstime || "";
|
||||||
formData.endTime = jlData.jljstime || "";
|
formData.endTime = jlData.jljstime || "";
|
||||||
formData.signatureRequired = jlData.mdqz === "1";
|
formData.signatureRequired = jlData.mdqz === "1";
|
||||||
|
formData.mdhz = jlData.mdhz || ""; // 加载回执说明
|
||||||
|
|
||||||
// 处理附件
|
// 处理附件
|
||||||
if (jlData.jlfj) {
|
if (jlData.jlfj) {
|
||||||
const attachmentUrls = jlData.jlfj.split(",");
|
const attachmentUrls = jlData.jlfj.split(",");
|
||||||
formData.attachments = attachmentUrls.map(url => ({
|
formData.attachments = attachmentUrls.map((url: string) => ({
|
||||||
name: url.split("/").pop() || "附件",
|
name: url.split("/").pop() || "附件",
|
||||||
type: "file",
|
type: "file",
|
||||||
url: url
|
url: url as string
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,7 +596,7 @@ const getAttachmentIcon = (type: string): string => {
|
|||||||
const previewAttachment = (attachment: Attachment) => {
|
const previewAttachment = (attachment: Attachment) => {
|
||||||
// 如果是图片类型,可以预览
|
// 如果是图片类型,可以预览
|
||||||
if (attachment.type === "image") {
|
if (attachment.type === "image") {
|
||||||
const fullUrl = imagUrl(attachment.url);
|
const fullUrl = imagUrl(attachment.url as string);
|
||||||
uni.previewImage({
|
uni.previewImage({
|
||||||
urls: [fullUrl],
|
urls: [fullUrl],
|
||||||
current: fullUrl,
|
current: fullUrl,
|
||||||
@ -594,11 +617,11 @@ const showClassTree = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 树形选择确认
|
// 树形选择确认
|
||||||
const onTreeConfirm = async (selectedItems: any[]) => {
|
const onTreeConfirm = async (selectedItems: Array<any>) => {
|
||||||
|
|
||||||
if (selectedItems.length > 0) {
|
if (selectedItems.length > 0) {
|
||||||
// 处理多选情况
|
// 处理多选情况
|
||||||
const classNames = selectedItems.map((item) => item.title);
|
const classNames = selectedItems.map((item: any) => item.title);
|
||||||
formData.targetClass = classNames.join(", ");
|
formData.targetClass = classNames.join(", ");
|
||||||
formData.targetNames = [];
|
formData.targetNames = [];
|
||||||
formData.targetStudentIds = [];
|
formData.targetStudentIds = [];
|
||||||
@ -615,16 +638,24 @@ const onTreeConfirm = async (selectedItems: any[]) => {
|
|||||||
const bjIds: string[] = [];
|
const bjIds: string[] = [];
|
||||||
const gradeNames: string[] = []; // 用于存储年级名称,用于错误提示
|
const gradeNames: string[] = []; // 用于存储年级名称,用于错误提示
|
||||||
|
|
||||||
selectedItems.forEach((item) => {
|
selectedItems.forEach((item: any) => {
|
||||||
// 如果选择的是班级(有parents表示是班级)
|
// 如果选择的是班级(有parents表示是班级)
|
||||||
if (item.parents && item.parents.length > 0) {
|
if (item.parents && item.parents.length > 0) {
|
||||||
const parent = item.parents[0]; // 年级信息
|
const parent = item.parents[0]; // 年级信息
|
||||||
|
|
||||||
const njId = parent.key; // 年级ID:parents[0].key
|
const njId = parent.key; // 年级ID:parents[0].key
|
||||||
const njmcId = parent.njmcId; // 年级名称ID:从年级信息中获取
|
|
||||||
const bjId = item.key; // 班级ID:item.key
|
const bjId = item.key; // 班级ID:item.key
|
||||||
const gradeName = parent.title; // 年级名称
|
const gradeName = parent.title; // 年级名称
|
||||||
|
|
||||||
|
// 通过年级key从treeData中查找对应的njmcId
|
||||||
|
let njmcId: string | undefined;
|
||||||
|
for (const grade of treeData.value || []) {
|
||||||
|
if (grade.key === njId) {
|
||||||
|
njmcId = grade.njmcId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 确保年级信息完整
|
// 确保年级信息完整
|
||||||
if (njId && bjId && gradeName) {
|
if (njId && bjId && gradeName) {
|
||||||
njIds.push(njId);
|
njIds.push(njId);
|
||||||
@ -669,7 +700,7 @@ const onTreeConfirm = async (selectedItems: any[]) => {
|
|||||||
bjId: bjIds.join(","),
|
bjId: bjIds.join(","),
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await mobilejlstudentListApi(params);
|
const response = await mobilejlstudentListApi(params) as ApiResponse<any[]>;
|
||||||
|
|
||||||
if (response && response.resultCode === 1 && response.result) {
|
if (response && response.resultCode === 1 && response.result) {
|
||||||
// 提取学生姓名,尝试多个可能的字段名
|
// 提取学生姓名,尝试多个可能的字段名
|
||||||
@ -699,7 +730,6 @@ const onTreeConfirm = async (selectedItems: any[]) => {
|
|||||||
icon: "success",
|
icon: "success",
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取学生列表失败:", error);
|
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
uni.showToast({ title: "获取学生列表失败", icon: "error" });
|
uni.showToast({ title: "获取学生列表失败", icon: "error" });
|
||||||
|
|
||||||
@ -733,7 +763,16 @@ const removeStudent = (index: number) => {
|
|||||||
|
|
||||||
const handleSignatureChange = (e: any) => {
|
const handleSignatureChange = (e: any) => {
|
||||||
const index = e.detail.value;
|
const index = e.detail.value;
|
||||||
formData.signatureRequired = index == 1;
|
formData.signatureRequired = index == 0; // 修正:索引0是"启用",索引1是"不启用"
|
||||||
|
|
||||||
|
// 当启用按名单签名时,自动设置默认的回执说明
|
||||||
|
if (formData.signatureRequired) {
|
||||||
|
const title = formData.title.trim() || "通知";
|
||||||
|
formData.mdhz = `我已认真阅读《${title}》内容,加强对孩子安全教育和安全监管,对孩子的安全全面负责。`;
|
||||||
|
} else {
|
||||||
|
// 当不启用时,清空回执说明
|
||||||
|
formData.mdhz = "";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
@ -814,6 +853,7 @@ const buildJlDto = (status: string) => {
|
|||||||
jlkstime: formatDate(formData.startTime), // 接龙开始时间,格式化为 yyyy-MM-dd HH:mm:ss
|
jlkstime: formatDate(formData.startTime), // 接龙开始时间,格式化为 yyyy-MM-dd HH:mm:ss
|
||||||
jljstime: formatDate(formData.endTime), // 接龙结束时间,格式化为 yyyy-MM-dd HH:mm:ss
|
jljstime: formatDate(formData.endTime), // 接龙结束时间,格式化为 yyyy-MM-dd HH:mm:ss
|
||||||
mdqz: formData.signatureRequired ? "1" : "0", // 按名单签字:1启用,0不启用
|
mdqz: formData.signatureRequired ? "1" : "0", // 按名单签字:1启用,0不启用
|
||||||
|
mdhz: formData.mdhz || "", // 回执说明
|
||||||
njId: formData.targetNjIds.join(","), // 关联年级ID列表
|
njId: formData.targetNjIds.join(","), // 关联年级ID列表
|
||||||
njmcId: formData.targetNjmcIds.join(","), // 关联年级名称ID列表
|
njmcId: formData.targetNjmcIds.join(","), // 关联年级名称ID列表
|
||||||
bjId: formData.targetBjIds.join(","), // 关联班级ID列表
|
bjId: formData.targetBjIds.join(","), // 关联班级ID列表
|
||||||
@ -838,13 +878,13 @@ const saveDraft = async () => {
|
|||||||
const jlDto = buildJlDto("B"); // B表示暂存
|
const jlDto = buildJlDto("B"); // B表示暂存
|
||||||
|
|
||||||
// 调用后端接口
|
// 调用后端接口
|
||||||
const response = await jlSaveApi(jlDto);
|
const response = await jlSaveApi(jlDto) as ApiResponse<any>;
|
||||||
|
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
|
|
||||||
if (response && response.resultCode === 1) {
|
if (response && response.resultCode === 1) {
|
||||||
// 获取接龙ID
|
// 获取接龙ID
|
||||||
const jlId = response.result || response.data || jlDto.id;
|
const jlId = response.result || (response as any).data || jlDto.id;
|
||||||
|
|
||||||
// 更新表单中的ID
|
// 更新表单中的ID
|
||||||
if (jlId) {
|
if (jlId) {
|
||||||
@ -863,15 +903,12 @@ const saveDraft = async () => {
|
|||||||
}, 2000);
|
}, 2000);
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: response?.resultMsg || "保存草稿失败",
|
title: (response as any)?.resultMsg || "保存草稿失败",
|
||||||
icon: "error"
|
icon: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
console.error("保存草稿失败:", error);
|
|
||||||
|
|
||||||
// 处理数据验证错误
|
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: error.message,
|
title: error.message,
|
||||||
@ -886,11 +923,6 @@ const saveDraft = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const previewNotice = () => {
|
|
||||||
if (!validateForm()) return;
|
|
||||||
uni.showToast({ title: "预览功能待实现", icon: "none" });
|
|
||||||
};
|
|
||||||
|
|
||||||
const publishNotice = async () => {
|
const publishNotice = async () => {
|
||||||
if (!validateForm()) return;
|
if (!validateForm()) return;
|
||||||
|
|
||||||
@ -909,7 +941,7 @@ const publishNotice = async () => {
|
|||||||
// 如果有ID,检查接龙是否已存在且已发布
|
// 如果有ID,检查接龙是否已存在且已发布
|
||||||
if (formData.id) {
|
if (formData.id) {
|
||||||
try {
|
try {
|
||||||
const response = await jlFindByIdApi({ id: formData.id });
|
const response = await jlFindByIdApi({ id: formData.id }) as ApiResponse<any>;
|
||||||
if (response && response.resultCode === 1 && response.result) {
|
if (response && response.resultCode === 1 && response.result) {
|
||||||
const jlData = response.result;
|
const jlData = response.result;
|
||||||
if (jlData.jlStatus === 'A') {
|
if (jlData.jlStatus === 'A') {
|
||||||
@ -917,9 +949,7 @@ const publishNotice = async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {}
|
||||||
console.error("检查接龙状态失败:", error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -931,20 +961,19 @@ const publishNotice = async () => {
|
|||||||
|
|
||||||
// 调用后端接口,设置超时处理
|
// 调用后端接口,设置超时处理
|
||||||
const response = await Promise.race([
|
const response = await Promise.race([
|
||||||
jlSaveApi(jlDto),
|
jlSaveApi(jlDto) as Promise<ApiResponse<any>>,
|
||||||
new Promise((_, reject) =>
|
new Promise((_, reject) =>
|
||||||
setTimeout(() => reject(new Error("请求超时")), 300000) // 5分钟超时
|
setTimeout(() => reject(new Error("请求超时")), 300000) // 5分钟超时
|
||||||
)
|
)
|
||||||
]);
|
]) as ApiResponse<any>;
|
||||||
|
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
|
|
||||||
if (response && response.resultCode === 1) {
|
if (response && response.resultCode === 1) {
|
||||||
// 获取接龙ID,优先使用返回的ID,如果没有则使用请求中的ID
|
// 获取接龙ID,优先使用返回的ID,如果没有则使用请求中的ID
|
||||||
const jlId = response.result || response.data || jlDto.id;
|
const jlId = response.result || (response as any).data || jlDto.id;
|
||||||
|
|
||||||
if (!jlId) {
|
if (!jlId) {
|
||||||
console.error("发布成功但未获取到接龙ID");
|
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "发布成功,但获取接龙ID失败",
|
title: "发布成功,但获取接龙ID失败",
|
||||||
icon: "error"
|
icon: "error"
|
||||||
@ -971,16 +1000,16 @@ const publishNotice = async () => {
|
|||||||
});
|
});
|
||||||
}, 2000);
|
}, 2000);
|
||||||
} else {
|
} else {
|
||||||
|
// 处理业务错误
|
||||||
|
const errorMsg = (response as any)?.resultMsg || (response as any)?.message || "发布失败";
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: response?.resultMsg || "发布失败",
|
title: errorMsg,
|
||||||
icon: "error"
|
icon: "none",
|
||||||
|
duration: 3000
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
console.error("发布接龙失败:", error);
|
|
||||||
|
|
||||||
// 处理不同类型的错误
|
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
if (error.message === "请求超时") {
|
if (error.message === "请求超时") {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
@ -996,7 +1025,7 @@ const publishNotice = async () => {
|
|||||||
}
|
}
|
||||||
} else if (error && typeof error === 'object' && 'errMsg' in error) {
|
} else if (error && typeof error === 'object' && 'errMsg' in error) {
|
||||||
// uni-app 错误对象
|
// uni-app 错误对象
|
||||||
if (error.errMsg && error.errMsg.includes('timeout')) {
|
if ((error as any).errMsg && (error as any).errMsg.includes('timeout')) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "请求超时,请检查网络连接或稍后重试",
|
title: "请求超时,请检查网络连接或稍后重试",
|
||||||
icon: "none",
|
icon: "none",
|
||||||
@ -1004,7 +1033,7 @@ const publishNotice = async () => {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: error.errMsg || "发布失败,请重试",
|
title: (error as any).errMsg || "发布失败,请重试",
|
||||||
icon: "error"
|
icon: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1018,6 +1047,15 @@ const publishNotice = async () => {
|
|||||||
isPublishing.value = false;
|
isPublishing.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 监听标题变化,自动更新回执说明
|
||||||
|
watch(() => formData.title, (newTitle) => {
|
||||||
|
if (formData.signatureRequired && newTitle) {
|
||||||
|
const title = newTitle.trim() || "通知";
|
||||||
|
formData.mdhz = `我已认真阅读《${title}》内容,加强对孩子安全教育和安全监管,对孩子的安全全面负责。`;
|
||||||
|
}
|
||||||
|
}, { immediate: false });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@ -1154,6 +1192,14 @@ const publishNotice = async () => {
|
|||||||
min-height: 80px;
|
min-height: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.receipt-input ::v-deep .uni-easyinput__content-textarea {
|
||||||
|
font-size: 14px !important;
|
||||||
|
color: #606266 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
line-height: 1.5;
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
.attachments-section {
|
.attachments-section {
|
||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
.form-label {
|
.form-label {
|
||||||
|
|||||||
489
src/pages/view/routine/ShiTangXunCha/add.vue
Normal file
489
src/pages/view/routine/ShiTangXunCha/add.vue
Normal file
@ -0,0 +1,489 @@
|
|||||||
|
<!-- src/pages/view/routine/ShiTangXunCha/add.vue -->
|
||||||
|
<template>
|
||||||
|
<view class="add-page">
|
||||||
|
<view class="form-container">
|
||||||
|
<view class="form-title">新增食堂巡查</view>
|
||||||
|
|
||||||
|
<!-- 工作名称 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">工作名称 *</text>
|
||||||
|
<input
|
||||||
|
v-model="formData.gzmc"
|
||||||
|
class="form-input"
|
||||||
|
placeholder="请输入工作名称"
|
||||||
|
maxlength="100"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 工作描述 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">工作描述</text>
|
||||||
|
<textarea
|
||||||
|
v-model="formData.gzms"
|
||||||
|
class="form-textarea"
|
||||||
|
placeholder="请输入工作描述"
|
||||||
|
maxlength="500"
|
||||||
|
:show-count="true"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 上传照片 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">上传照片</text>
|
||||||
|
<view class="upload-section">
|
||||||
|
<view class="upload-list">
|
||||||
|
<view
|
||||||
|
v-for="(image, index) in imageList"
|
||||||
|
:key="index"
|
||||||
|
class="upload-item"
|
||||||
|
>
|
||||||
|
<image
|
||||||
|
:src="image.url ? imagUrl(image.url) : image.tempPath"
|
||||||
|
mode="aspectFill"
|
||||||
|
class="upload-image"
|
||||||
|
@click="previewImage(index)"
|
||||||
|
/>
|
||||||
|
<view class="upload-delete" @click="deleteImage(index)">
|
||||||
|
<text class="delete-icon">×</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="imageList.length < 9"
|
||||||
|
class="upload-add"
|
||||||
|
@click="chooseImage"
|
||||||
|
>
|
||||||
|
<text class="add-icon">+</text>
|
||||||
|
<text class="add-text">添加图片</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 提交人 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">提交人</text>
|
||||||
|
<input
|
||||||
|
:value="formData.jsxm"
|
||||||
|
class="form-input"
|
||||||
|
disabled
|
||||||
|
placeholder="自动获取当前用户"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 提交时间 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">提交时间</text>
|
||||||
|
<picker
|
||||||
|
mode="date"
|
||||||
|
:value="formData.tjtime"
|
||||||
|
@change="onTimeChange"
|
||||||
|
class="form-picker"
|
||||||
|
>
|
||||||
|
<view class="picker-display">
|
||||||
|
{{ formData.tjtime || '请选择提交时间' }}
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 提交按钮 -->
|
||||||
|
<view class="submit-section">
|
||||||
|
<u-button
|
||||||
|
text="提交"
|
||||||
|
type="primary"
|
||||||
|
:loading="submitting"
|
||||||
|
@click="handleSubmit"
|
||||||
|
class="submit-btn"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { hcSaveApi } from "@/api/base/hcApi";
|
||||||
|
import { attachmentUpload } from "@/api/system/upload";
|
||||||
|
import { useUserStore } from "@/store/modules/user";
|
||||||
|
import { showLoading, hideLoading, showToast } from "@/utils/uniapp";
|
||||||
|
import { imagUrl } from "@/utils";
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
|
||||||
|
const { getJs } = useUserStore();
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formData = ref({
|
||||||
|
gzmc: '', // 工作名称
|
||||||
|
gzms: '', // 工作描述
|
||||||
|
jsId: '', // 提交老师ID
|
||||||
|
jsxm: '', // 提交老师姓名
|
||||||
|
tjtime: '', // 提交时间
|
||||||
|
tjfj: '' // 提交附件
|
||||||
|
});
|
||||||
|
|
||||||
|
// 图片列表 - 修改为包含临时路径和服务器路径的对象
|
||||||
|
interface ImageItem {
|
||||||
|
tempPath?: string; // 临时路径(用于预览)
|
||||||
|
url?: string; // 服务器路径(上传成功后)
|
||||||
|
name?: string; // 文件名
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageList = ref<ImageItem[]>([]);
|
||||||
|
|
||||||
|
// 提交状态
|
||||||
|
const submitting = ref(false);
|
||||||
|
|
||||||
|
// 初始化数据
|
||||||
|
onMounted(() => {
|
||||||
|
const js = getJs;
|
||||||
|
formData.value.jsId = js.id;
|
||||||
|
formData.value.jsxm = js.jsxm;
|
||||||
|
|
||||||
|
// 设置默认时间为今天
|
||||||
|
const today = new Date();
|
||||||
|
formData.value.tjtime = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 选择图片
|
||||||
|
const chooseImage = () => {
|
||||||
|
uni.chooseImage({
|
||||||
|
count: 9 - imageList.value.length,
|
||||||
|
sizeType: ['compressed'],
|
||||||
|
sourceType: ['album', 'camera'],
|
||||||
|
success: async (res) => {
|
||||||
|
// 添加临时图片到列表
|
||||||
|
const newImages = res.tempFilePaths.map(path => ({
|
||||||
|
tempPath: path,
|
||||||
|
name: path.split('/').pop() || 'image.jpg'
|
||||||
|
}));
|
||||||
|
|
||||||
|
imageList.value = [...imageList.value, ...newImages];
|
||||||
|
|
||||||
|
// 自动上传图片
|
||||||
|
await uploadImages(newImages);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 上传图片到服务器
|
||||||
|
const uploadImages = async (images: ImageItem[]) => {
|
||||||
|
try {
|
||||||
|
showLoading('上传图片中...');
|
||||||
|
|
||||||
|
for (let i = 0; i < images.length; i++) {
|
||||||
|
const image = images[i];
|
||||||
|
if (image.tempPath) {
|
||||||
|
try {
|
||||||
|
// 调用上传接口
|
||||||
|
const uploadResult: any = await attachmentUpload(image.tempPath as any);
|
||||||
|
|
||||||
|
if (uploadResult && uploadResult.resultCode === 1 && uploadResult.result && uploadResult.result.length > 0) {
|
||||||
|
// 保存服务器返回的文件路径
|
||||||
|
const serverPath = uploadResult.result[0].filePath;
|
||||||
|
|
||||||
|
// 更新图片对象,保存服务器路径
|
||||||
|
const index = imageList.value.findIndex(img => img.tempPath === image.tempPath);
|
||||||
|
if (index !== -1) {
|
||||||
|
imageList.value[index].url = serverPath;
|
||||||
|
// 可以删除临时路径以节省内存
|
||||||
|
delete imageList.value[index].tempPath;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('上传响应格式异常');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('图片上传失败:', error);
|
||||||
|
showToast(`${image.name || '图片'}上传失败`, 'none');
|
||||||
|
|
||||||
|
// 从列表中移除上传失败的图片
|
||||||
|
const index = imageList.value.findIndex(img => img.tempPath === image.tempPath);
|
||||||
|
if (index !== -1) {
|
||||||
|
imageList.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hideLoading();
|
||||||
|
showToast('图片上传完成', 'success');
|
||||||
|
} catch (error) {
|
||||||
|
hideLoading();
|
||||||
|
console.error('批量上传图片失败:', error);
|
||||||
|
showToast('图片上传失败,请重试', 'none');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 预览图片
|
||||||
|
const previewImage = (index: number) => {
|
||||||
|
const urls = imageList.value.map(img =>
|
||||||
|
img.url ? imagUrl(img.url) : img.tempPath
|
||||||
|
).filter(url => url);
|
||||||
|
|
||||||
|
uni.previewImage({
|
||||||
|
urls: urls,
|
||||||
|
current: index
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除图片
|
||||||
|
const deleteImage = (index: number) => {
|
||||||
|
imageList.value.splice(index, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 时间选择
|
||||||
|
const onTimeChange = (e: any) => {
|
||||||
|
formData.value.tjtime = e.detail.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
// 验证必填字段
|
||||||
|
if (!formData.value.gzmc.trim()) {
|
||||||
|
showToast('请输入工作名称', 'none');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.value.tjtime) {
|
||||||
|
showToast('请选择提交时间', 'none');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有图片正在上传
|
||||||
|
const hasUploadingImages = imageList.value.some(img => img.tempPath && !img.url);
|
||||||
|
if (hasUploadingImages) {
|
||||||
|
showToast('请等待图片上传完成', 'none');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
submitting.value = true;
|
||||||
|
showLoading('提交中...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 处理图片路径
|
||||||
|
let tjfj = '';
|
||||||
|
const uploadedImages = imageList.value.filter(img => img.url);
|
||||||
|
if (uploadedImages.length > 0) {
|
||||||
|
// 使用服务器路径,用逗号分隔
|
||||||
|
tjfj = uploadedImages.map(img => img.url).join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitData = {
|
||||||
|
...formData.value,
|
||||||
|
tjfj
|
||||||
|
};
|
||||||
|
|
||||||
|
await hcSaveApi(submitData);
|
||||||
|
|
||||||
|
hideLoading();
|
||||||
|
showToast('提交成功', 'success');
|
||||||
|
|
||||||
|
// 返回列表页
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack();
|
||||||
|
}, 1500);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
hideLoading();
|
||||||
|
showToast('提交失败,请重试', 'none');
|
||||||
|
console.error('提交失败:', error);
|
||||||
|
} finally {
|
||||||
|
submitting.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.add-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
padding: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2c3e50;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #2c3e50;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 44px;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #495057;
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: #28a745;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100px;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #495057;
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-sizing: border-box;
|
||||||
|
resize: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: #28a745;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-picker {
|
||||||
|
.picker-display {
|
||||||
|
width: 100%;
|
||||||
|
height: 44px;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #495057;
|
||||||
|
background-color: #ffffff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-section {
|
||||||
|
.upload-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-item {
|
||||||
|
position: relative;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
|
||||||
|
.upload-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-delete {
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
right: 4px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.delete-icon {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-add {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border: 2px dashed #e9ecef;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.add-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #6c757d;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-text {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-section {
|
||||||
|
padding: 0 16px;
|
||||||
|
|
||||||
|
.submit-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
background: linear-gradient(135deg, #007aff 0%, #0056cc 100%);
|
||||||
|
border: none;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #ffffff;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 122, 255, 0.3);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 122, 255, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式优化
|
||||||
|
@media (max-width: 375px) {
|
||||||
|
.add-page {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-title {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
284
src/pages/view/routine/ShiTangXunCha/detail.vue
Normal file
284
src/pages/view/routine/ShiTangXunCha/detail.vue
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
<!-- src/pages/view/routine/ShiTangXunCha/detail.vue -->
|
||||||
|
<template>
|
||||||
|
<view class="detail-page">
|
||||||
|
<view class="detail-container">
|
||||||
|
<view class="detail-header">
|
||||||
|
<text class="detail-title">{{ detailData.gzmc }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 工作描述 -->
|
||||||
|
<view class="detail-section">
|
||||||
|
<text class="section-title">工作描述</text>
|
||||||
|
<view class="section-content">
|
||||||
|
<rich-text class="detail-text" :nodes="detailData.gzms || '暂无描述'"></rich-text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 照片展示 -->
|
||||||
|
<view v-if="imageList.length > 0" class="detail-section">
|
||||||
|
<text class="section-title">巡查照片</text>
|
||||||
|
<view class="image-grid">
|
||||||
|
<view
|
||||||
|
v-for="(image, index) in imageList"
|
||||||
|
:key="index"
|
||||||
|
class="image-item"
|
||||||
|
@click="previewImage(index)"
|
||||||
|
>
|
||||||
|
<image
|
||||||
|
:src="image"
|
||||||
|
mode="aspectFill"
|
||||||
|
class="detail-image"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 提交信息 -->
|
||||||
|
<view class="detail-section">
|
||||||
|
<text class="section-title">提交信息</text>
|
||||||
|
<view class="info-list">
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="info-label">提交人:</text>
|
||||||
|
<text class="info-value">{{ detailData.jsxm || '未知' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="info-label">提交时间:</text>
|
||||||
|
<text class="info-value">{{ formatTime(detailData.tjtime) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<view class="action-section">
|
||||||
|
<u-button
|
||||||
|
text="返回列表"
|
||||||
|
type="primary"
|
||||||
|
@click="goBack"
|
||||||
|
class="back-btn"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { hcFindByIdApi } from "@/api/base/hcApi";
|
||||||
|
import { imagUrl } from "@/utils";
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
import { onLoad } from "@dcloudio/uni-app";
|
||||||
|
|
||||||
|
interface DetailData {
|
||||||
|
id: string;
|
||||||
|
gzmc: string;
|
||||||
|
gzms: string;
|
||||||
|
jsId: string;
|
||||||
|
jsxm: string;
|
||||||
|
tjtime: string;
|
||||||
|
tjfj: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 详情数据
|
||||||
|
const detailData = ref<DetailData>({
|
||||||
|
id: '',
|
||||||
|
gzmc: '',
|
||||||
|
gzms: '',
|
||||||
|
jsId: '',
|
||||||
|
jsxm: '',
|
||||||
|
tjtime: '',
|
||||||
|
tjfj: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
// 图片列表
|
||||||
|
const imageList = ref<string[]>([]);
|
||||||
|
|
||||||
|
// 获取页面参数
|
||||||
|
onLoad((options) => {
|
||||||
|
if (options.id) {
|
||||||
|
loadDetail(options.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 加载详情数据
|
||||||
|
const loadDetail = async (id: string) => {
|
||||||
|
try {
|
||||||
|
const res = await hcFindByIdApi({ id });
|
||||||
|
if (res && res.result) {
|
||||||
|
detailData.value = res.result;
|
||||||
|
|
||||||
|
// 处理图片
|
||||||
|
if (detailData.value.tjfj) {
|
||||||
|
const images = detailData.value.tjfj.split(',').filter(img => img.trim());
|
||||||
|
imageList.value = images.map(img => imagUrl(img));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载详情失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 预览图片
|
||||||
|
const previewImage = (index: number) => {
|
||||||
|
uni.previewImage({
|
||||||
|
urls: imageList.value,
|
||||||
|
current: index
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化时间
|
||||||
|
const formatTime = (timeStr: string) => {
|
||||||
|
if (!timeStr) return "";
|
||||||
|
const date = new Date(timeStr);
|
||||||
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 返回列表
|
||||||
|
const goBack = () => {
|
||||||
|
uni.navigateBack();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.detail-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
padding: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-container {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-header {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
|
||||||
|
.detail-title {
|
||||||
|
display: block;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-time {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-section {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: block;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding-left: 8px;
|
||||||
|
border-left: 3px solid #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-content {
|
||||||
|
.detail-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #5a6c7d;
|
||||||
|
line-height: 1.6;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.image-item {
|
||||||
|
width: calc(33.33% - 6px);
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
|
||||||
|
.detail-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-list {
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #6c757d;
|
||||||
|
min-width: 80px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #2c3e50;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-section {
|
||||||
|
padding: 0 16px;
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
background: linear-gradient(135deg, #007aff 0%, #0056cc 100%);
|
||||||
|
border: none;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #ffffff;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 122, 255, 0.3);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 122, 255, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式优化
|
||||||
|
@media (max-width: 375px) {
|
||||||
|
.detail-page {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-header .detail-title {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-grid .image-item {
|
||||||
|
width: calc(50% - 4px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
477
src/pages/view/routine/ShiTangXunCha/index.vue
Normal file
477
src/pages/view/routine/ShiTangXunCha/index.vue
Normal file
@ -0,0 +1,477 @@
|
|||||||
|
<!-- src/pages/view/routine/ShiTangXunCha/index.vue -->
|
||||||
|
<template>
|
||||||
|
<view class="hc-list-page">
|
||||||
|
<!-- 查询组件 -->
|
||||||
|
<view class="query-component">
|
||||||
|
<view class="search-card">
|
||||||
|
<view class="search-item">
|
||||||
|
<picker
|
||||||
|
mode="date"
|
||||||
|
:value="searchForm.startTime"
|
||||||
|
@change="onStartTimeChange"
|
||||||
|
class="date-picker"
|
||||||
|
>
|
||||||
|
<view class="picker-text">{{ searchForm.startTime || '开始时间' }}</view>
|
||||||
|
</picker>
|
||||||
|
<text class="date-separator">~</text>
|
||||||
|
<picker
|
||||||
|
mode="date"
|
||||||
|
:value="searchForm.endTime"
|
||||||
|
@change="onEndTimeChange"
|
||||||
|
class="date-picker"
|
||||||
|
>
|
||||||
|
<view class="picker-text">{{ searchForm.endTime || '结束时间' }}</view>
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
<view class="search-actions">
|
||||||
|
<u-button
|
||||||
|
text="查询"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="handleSearch"
|
||||||
|
class="search-btn"
|
||||||
|
/>
|
||||||
|
<u-button
|
||||||
|
text="重置"
|
||||||
|
type="info"
|
||||||
|
size="small"
|
||||||
|
@click="handleReset"
|
||||||
|
class="reset-btn"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 列表组件 -->
|
||||||
|
<view class="list-component">
|
||||||
|
<scroll-view scroll-y class="list-scroll-view">
|
||||||
|
<view v-if="isLoading" class="loading-indicator">加载中...</view>
|
||||||
|
<template v-else-if="dataList.length > 0">
|
||||||
|
<view v-for="data in dataList" :key="data.id" class="hc-card" @click="goToDetail(data.id)">
|
||||||
|
<view class="card-header">
|
||||||
|
<text class="hc-title">{{ data.gzmc }}</text>
|
||||||
|
<text class="hc-time">{{ formatTime(data.tjtime) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="card-body">
|
||||||
|
<rich-text class="hc-excerpt" :nodes="data.gzms"></rich-text>
|
||||||
|
<view v-if="data.tjfj" class="image-preview">
|
||||||
|
<image
|
||||||
|
:src="getImageUrl(data.tjfj)"
|
||||||
|
mode="aspectFill"
|
||||||
|
class="preview-image"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="card-footer">
|
||||||
|
<text class="footer-item">提交人: {{ data.jsxm || '未知' }}</text>
|
||||||
|
<text class="footer-item">提交时间: {{ formatTime(data.tjtime) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<view v-else class="empty-state">暂无巡查数据</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 新增按钮 - 固定在底部 -->
|
||||||
|
<view class="add-button-fixed">
|
||||||
|
<u-button
|
||||||
|
text="新增巡查"
|
||||||
|
type="primary"
|
||||||
|
@click="goToAdd"
|
||||||
|
class="add-btn"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { hcFindPageApi } from "@/api/base/hcApi";
|
||||||
|
import { imagUrl } from "@/utils";
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
import { onShow } from "@dcloudio/uni-app";
|
||||||
|
|
||||||
|
interface HcItem {
|
||||||
|
id: string;
|
||||||
|
gzmc: string; // 工作名称
|
||||||
|
gzms: string; // 工作描述
|
||||||
|
jsId: string; // 提交老师ID
|
||||||
|
jsxm: string; // 提交老师姓名
|
||||||
|
tjtime: string; // 提交时间
|
||||||
|
tjfj: string; // 提交附件
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当月开始时间
|
||||||
|
const getCurrentMonthStart = () => {
|
||||||
|
const now = new Date();
|
||||||
|
const year = now.getFullYear();
|
||||||
|
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||||
|
return `${year}-${month}-01`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取当月结束时间
|
||||||
|
const getCurrentMonthEnd = () => {
|
||||||
|
const now = new Date();
|
||||||
|
const year = now.getFullYear();
|
||||||
|
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||||
|
const lastDay = new Date(year, now.getMonth() + 1, 0).getDate();
|
||||||
|
return `${year}-${month}-${lastDay}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = ref({
|
||||||
|
startTime: getCurrentMonthStart(),
|
||||||
|
endTime: getCurrentMonthEnd()
|
||||||
|
});
|
||||||
|
|
||||||
|
// 数据列表
|
||||||
|
const dataList = ref<HcItem[]>([]);
|
||||||
|
const isLoading = ref(false);
|
||||||
|
|
||||||
|
// 开始时间选择
|
||||||
|
const onStartTimeChange = (e: any) => {
|
||||||
|
searchForm.value.startTime = e.detail.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 结束时间选择
|
||||||
|
const onEndTimeChange = (e: any) => {
|
||||||
|
searchForm.value.endTime = e.detail.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const handleSearch = () => {
|
||||||
|
getHcList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const handleReset = () => {
|
||||||
|
searchForm.value.startTime = getCurrentMonthStart();
|
||||||
|
searchForm.value.endTime = getCurrentMonthEnd();
|
||||||
|
getHcList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取巡查列表
|
||||||
|
const getHcList = async () => {
|
||||||
|
isLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await hcFindPageApi({
|
||||||
|
startTime: searchForm.value.startTime,
|
||||||
|
endTime: searchForm.value.endTime,
|
||||||
|
page: 1,
|
||||||
|
rows: 100
|
||||||
|
});
|
||||||
|
dataList.value = res.rows || [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取巡查列表失败:', error);
|
||||||
|
dataList.value = [];
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转到详情页
|
||||||
|
const goToDetail = (hcId: string) => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/view/routine/ShiTangXunCha/detail?id=${hcId}`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转到新增页
|
||||||
|
const goToAdd = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: "/pages/view/routine/ShiTangXunCha/add",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化时间
|
||||||
|
const formatTime = (timeStr: string) => {
|
||||||
|
if (!timeStr) return "";
|
||||||
|
const date = new Date(timeStr);
|
||||||
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(
|
||||||
|
2,
|
||||||
|
"0"
|
||||||
|
)}-${String(date.getDate()).padStart(2, "0")}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取图片URL
|
||||||
|
const getImageUrl = (imagePath: string) => {
|
||||||
|
if (!imagePath) return "";
|
||||||
|
// 如果路径包含逗号,说明有多张图片,取第一张
|
||||||
|
const firstImage = imagePath.split(',')[0];
|
||||||
|
return imagUrl(firstImage);
|
||||||
|
};
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
getHcList();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 页面加载时也调用一次
|
||||||
|
onMounted(() => {
|
||||||
|
getHcList();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.hc-list-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询组件
|
||||||
|
.query-component {
|
||||||
|
padding: 12px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-card {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.date-picker {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-text {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #495057;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-separator {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #6c757d;
|
||||||
|
margin: 0 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.search-btn, .reset-btn {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表组件
|
||||||
|
.list-component {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 12px;
|
||||||
|
padding-bottom: 80px; // 为底部按钮留出空间
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-scroll-view {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-indicator,
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
color: #999;
|
||||||
|
padding: 30px 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hc-card {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 4px;
|
||||||
|
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.hc-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2c3e50;
|
||||||
|
flex: 1;
|
||||||
|
line-height: 1.4;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hc-time {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #6c757d;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.hc-excerpt {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #5a6c7d;
|
||||||
|
line-height: 1.6;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
word-break: break-word;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview {
|
||||||
|
.preview-image {
|
||||||
|
width: 80px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px 16px;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
|
||||||
|
.footer-item {
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #7f8c8d;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 180px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
background-color: #bdc3c7;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增按钮 - 固定在底部
|
||||||
|
.add-button-fixed {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 10;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
|
||||||
|
.add-btn {
|
||||||
|
width: 100%;
|
||||||
|
background: linear-gradient(135deg, #007aff 0%, #0056cc 100%);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 122, 255, 0.3);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式优化
|
||||||
|
@media (max-width: 375px) {
|
||||||
|
.query-component {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hc-card {
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header .hc-title {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer .footer-item {
|
||||||
|
max-width: 150px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载动画
|
||||||
|
.hc-card {
|
||||||
|
animation: fadeInUp 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
BIN
src/static/base/home/hc-fill.png
Normal file
BIN
src/static/base/home/hc-fill.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
Loading…
x
Reference in New Issue
Block a user