|
|
@@ -6,7 +6,7 @@
|
|
|
<div class="hero">
|
|
|
<div class="hero-l">
|
|
|
<div class="hero__text">{{ t("workExecute.surveyEntryHint") }}</div>
|
|
|
- <div class="hero__text">{{ t("workExecute.surveyEntryHint2") }}</div>
|
|
|
+ <div class="hero__text">{{ t("workExecute.surveyEntryHint2") }}<span class="text-tip">(可多选)</span></div>
|
|
|
</div>
|
|
|
<img class="hero__illust" src="@/assets/img/agricultural/enter.png" alt="" />
|
|
|
</div>
|
|
|
@@ -23,22 +23,27 @@
|
|
|
<div class="search-bar__btn" @click="handleSearch">{{ t("workExecute.surveySearch") }}</div>
|
|
|
</div>
|
|
|
|
|
|
- <div class="category-panel">
|
|
|
- <div class="category-panel__title">{{ t("workExecute.surveySelectTitle") }}<span class="category-panel__hint">({{ t("workExecute.surveySelectHint") }})</span></div>
|
|
|
- <div class="category-grid" v-loading="loading">
|
|
|
- <div
|
|
|
- v-for="item in displayList"
|
|
|
- :key="item.id"
|
|
|
- class="category-tag"
|
|
|
- :class="{ selected: selectedIds.has(String(item.id)) }"
|
|
|
- @click="toggleSelect(item)"
|
|
|
- >
|
|
|
- <span class="category-tag__text van-ellipsis">{{ item.name }}</span>
|
|
|
+ <div class="category-panel" v-loading="loading">
|
|
|
+ <div v-for="group in displayGroups" :key="group.name" class="category-card">
|
|
|
+ <div class="section-title">
|
|
|
+ <span class="title-icon"></span>
|
|
|
+ <span>{{ group.name }}</span>
|
|
|
</div>
|
|
|
- <div v-if="!loading && !displayList.length" class="empty-tip">
|
|
|
- {{ t("workExecute.surveyNoMatch") }}
|
|
|
+ <div class="tag-group">
|
|
|
+ <div
|
|
|
+ v-for="item in group.items"
|
|
|
+ :key="item.id"
|
|
|
+ class="tag-item"
|
|
|
+ :class="{ selected: selectedIds.has(String(item.id)) }"
|
|
|
+ @click="toggleSelect(item)"
|
|
|
+ >
|
|
|
+ <span class="text">{{ item.name }}</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <div v-if="!loading && !displayGroups.length" class="empty-tip">
|
|
|
+ {{ t("workExecute.surveyNoMatch") }}
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -62,55 +67,99 @@ const router = useRouter();
|
|
|
const SESSION_KEY = "SURVEY_SELECTED_WORK_TYPES";
|
|
|
|
|
|
const loading = ref(false);
|
|
|
-const categoryList = ref([]);
|
|
|
+const categoryGroups = ref([]);
|
|
|
const searchKeyword = ref("");
|
|
|
const appliedKeyword = ref("");
|
|
|
const selectedIds = reactive(new Set());
|
|
|
|
|
|
-const DEFAULT_CATEGORIES = [
|
|
|
- "施肥管理",
|
|
|
- "病虫害防治",
|
|
|
- "灌溉补水",
|
|
|
- "修剪整枝",
|
|
|
- "疏花疏果",
|
|
|
- "土壤改良",
|
|
|
- "除草清园",
|
|
|
- "采收作业",
|
|
|
- "套袋护果",
|
|
|
- "授粉辅助",
|
|
|
- "果园巡查",
|
|
|
- "气象防护",
|
|
|
- "营养诊断",
|
|
|
- "物候监测",
|
|
|
- "农机作业",
|
|
|
- "药肥配送",
|
|
|
- "无人机植保",
|
|
|
- "产后处理",
|
|
|
- "仓储管理",
|
|
|
- "其他农事",
|
|
|
- "标准施肥",
|
|
|
- "标准防治",
|
|
|
- "标准调节",
|
|
|
- "机动农事",
|
|
|
- "预警响应",
|
|
|
- "恢复农事",
|
|
|
- "异常处置",
|
|
|
-].map((name, index) => ({ id: index + 1, name }));
|
|
|
-
|
|
|
-const displayList = computed(() => {
|
|
|
+const DEFAULT_GROUPS = [
|
|
|
+ {
|
|
|
+ name: "建园与土壤管理",
|
|
|
+ items: [
|
|
|
+ { id: 101, name: "整地类" },
|
|
|
+ { id: 102, name: "除草" },
|
|
|
+ { id: 103, name: "清园" },
|
|
|
+ { id: 104, name: "清沟修渠" },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "肥水管理",
|
|
|
+ items: [
|
|
|
+ { id: 201, name: "根部肥" },
|
|
|
+ { id: 202, name: "根部灌水" },
|
|
|
+ { id: 203, name: "根部灌药" },
|
|
|
+ { id: 204, name: "飞防叶面肥" },
|
|
|
+ { id: 205, name: "叶面喷水" },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "打药管理",
|
|
|
+ items: [
|
|
|
+ { id: 301, name: "飞防打药" },
|
|
|
+ { id: 302, name: "内膛打药" },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "树体管理",
|
|
|
+ items: [
|
|
|
+ { id: 401, name: "整形修剪" },
|
|
|
+ { id: 402, name: "嫁接" },
|
|
|
+ { id: 403, name: "环割环剥" },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "花果管理",
|
|
|
+ items: [
|
|
|
+ { id: 501, name: "疏花疏果" },
|
|
|
+ { id: 502, name: "果实套袋" },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+];
|
|
|
+
|
|
|
+const displayGroups = computed(() => {
|
|
|
const keyword = (appliedKeyword.value || "").trim();
|
|
|
- if (!keyword) return categoryList.value;
|
|
|
- return categoryList.value.filter((item) => item.name.includes(keyword));
|
|
|
+ if (!keyword) return categoryGroups.value;
|
|
|
+ return categoryGroups.value
|
|
|
+ .map((group) => ({
|
|
|
+ ...group,
|
|
|
+ items: group.items.filter((item) => item.name.includes(keyword)),
|
|
|
+ }))
|
|
|
+ .filter((group) => group.items.length);
|
|
|
});
|
|
|
|
|
|
-const normalizeList = (list) => {
|
|
|
- if (!Array.isArray(list)) return [];
|
|
|
- return list
|
|
|
- .map((item, index) => ({
|
|
|
+const allCategoryItems = computed(() =>
|
|
|
+ categoryGroups.value.flatMap((group) => group.items)
|
|
|
+);
|
|
|
+
|
|
|
+const normalizeGroups = (list) => {
|
|
|
+ if (!Array.isArray(list) || !list.length) return [];
|
|
|
+ // 已是分组结构
|
|
|
+ if (list[0]?.items) {
|
|
|
+ return list
|
|
|
+ .map((group, gIndex) => ({
|
|
|
+ name: group.name || group.category_name || `分类${gIndex + 1}`,
|
|
|
+ items: (group.items || group.list || [])
|
|
|
+ .map((item, index) => ({
|
|
|
+ id: item.id ?? item.type ?? item.code ?? `${gIndex}-${index + 1}`,
|
|
|
+ name: item.name || item.type_name || item.label || "",
|
|
|
+ }))
|
|
|
+ .filter((item) => item.name),
|
|
|
+ }))
|
|
|
+ .filter((group) => group.items.length);
|
|
|
+ }
|
|
|
+ // 扁平列表兜底:按 category / group 字段分组
|
|
|
+ const map = new Map();
|
|
|
+ list.forEach((item, index) => {
|
|
|
+ const name = item.name || item.type_name || item.label || "";
|
|
|
+ if (!name) return;
|
|
|
+ const groupName = item.category_name || item.group_name || item.category || "其他";
|
|
|
+ if (!map.has(groupName)) map.set(groupName, []);
|
|
|
+ map.get(groupName).push({
|
|
|
id: item.id ?? item.type ?? item.code ?? index + 1,
|
|
|
- name: item.name || item.type_name || item.label || "",
|
|
|
- }))
|
|
|
- .filter((item) => item.name);
|
|
|
+ name,
|
|
|
+ });
|
|
|
+ });
|
|
|
+ return Array.from(map.entries()).map(([name, items]) => ({ name, items }));
|
|
|
};
|
|
|
|
|
|
const restoreSelection = () => {
|
|
|
@@ -127,12 +176,12 @@ const restoreSelection = () => {
|
|
|
const fetchCategories = async () => {
|
|
|
loading.value = true;
|
|
|
try {
|
|
|
- categoryList.value = DEFAULT_CATEGORIES;
|
|
|
+ categoryGroups.value = DEFAULT_GROUPS;
|
|
|
// const res = await VE_API.z_farm_work_record.getFarmWorkTypeList();
|
|
|
- // const list = normalizeList(res?.data);
|
|
|
- // categoryList.value = list.length ? list : DEFAULT_CATEGORIES;
|
|
|
+ // const groups = normalizeGroups(res?.data);
|
|
|
+ // categoryGroups.value = groups.length ? groups : DEFAULT_GROUPS;
|
|
|
} catch {
|
|
|
- categoryList.value = DEFAULT_CATEGORIES;
|
|
|
+ categoryGroups.value = DEFAULT_GROUPS;
|
|
|
} finally {
|
|
|
loading.value = false;
|
|
|
}
|
|
|
@@ -152,7 +201,7 @@ const toggleSelect = (item) => {
|
|
|
};
|
|
|
|
|
|
const handleSubmit = () => {
|
|
|
- const selected = categoryList.value.filter((item) => selectedIds.has(String(item.id)));
|
|
|
+ const selected = allCategoryItems.value.filter((item) => selectedIds.has(String(item.id)));
|
|
|
if (!selected.length) {
|
|
|
ElMessage.warning(t("workExecute.surveyPleaseSelect"));
|
|
|
return;
|
|
|
@@ -181,7 +230,7 @@ onMounted(() => {
|
|
|
}
|
|
|
|
|
|
.survey-entry-body {
|
|
|
- padding: 0 10px 10px 10px;
|
|
|
+ padding: 0 10px;
|
|
|
flex: 1;
|
|
|
min-height: 0;
|
|
|
display: flex;
|
|
|
@@ -200,6 +249,9 @@ onMounted(() => {
|
|
|
line-height: 24px;
|
|
|
color: #005599;
|
|
|
font-family: "PangMenZhengDao";
|
|
|
+ &.text-tip {
|
|
|
+ font-size: 16px;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
&__illust {
|
|
|
@@ -258,81 +310,107 @@ onMounted(() => {
|
|
|
.category-panel {
|
|
|
flex: 1;
|
|
|
min-height: 0;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- padding: 12px 12px 0;
|
|
|
- border-radius: 10px;
|
|
|
+ overflow-y: auto;
|
|
|
+ -webkit-overflow-scrolling: touch;
|
|
|
+ padding-bottom: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.category-card {
|
|
|
background: #fff;
|
|
|
- overflow: hidden;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 14px 12px 16px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
|
|
|
- &__title {
|
|
|
- padding-bottom: 10px;
|
|
|
- margin-bottom: 10px;
|
|
|
- font-size: 16px;
|
|
|
- font-weight: 500;
|
|
|
- color: #222222;
|
|
|
- border-bottom: 0.2px solid rgba(0, 0, 0, 0.1);
|
|
|
- }
|
|
|
+.section-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1a1a1a;
|
|
|
+
|
|
|
+ .title-icon {
|
|
|
+ position: relative;
|
|
|
+ width: 14px;
|
|
|
+ height: 14px;
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
|
- &__hint {
|
|
|
- font-size: 14px;
|
|
|
- color: rgba(34, 34, 34, 0.5);
|
|
|
+ &::before,
|
|
|
+ &::after {
|
|
|
+ content: "";
|
|
|
+ position: absolute;
|
|
|
+ width: 10px;
|
|
|
+ height: 10px;
|
|
|
+ border-radius: 50%;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ left: 0;
|
|
|
+ top: 2px;
|
|
|
+ background: #2199f8;
|
|
|
+ opacity: 0.85;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ right: 0;
|
|
|
+ top: 0;
|
|
|
+ background: #7ec8ff;
|
|
|
+ opacity: 0.9;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.category-grid {
|
|
|
- flex: 1;
|
|
|
- min-height: 0;
|
|
|
- overflow-y: auto;
|
|
|
+.tag-group {
|
|
|
display: grid;
|
|
|
grid-template-columns: repeat(3, 1fr);
|
|
|
- gap: 10px;
|
|
|
- align-content: start;
|
|
|
- padding-bottom: 16px;
|
|
|
-}
|
|
|
-
|
|
|
-.category-tag {
|
|
|
- position: relative;
|
|
|
- height: 40px;
|
|
|
- padding: 0 8px;
|
|
|
- border-radius: 8px;
|
|
|
- background: #f3f4f6;
|
|
|
- border: 1px solid transparent;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- box-sizing: border-box;
|
|
|
- cursor: pointer;
|
|
|
+ gap: 0 8px;
|
|
|
+ font-size: 14px;
|
|
|
|
|
|
- &__text {
|
|
|
- max-width: 100%;
|
|
|
- font-size: 14px;
|
|
|
- color: #1a1a1a;
|
|
|
+ .tag-item {
|
|
|
+ margin-top: 10px;
|
|
|
+ position: relative;
|
|
|
+ border-radius: 6px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ height: 36px;
|
|
|
text-align: center;
|
|
|
- }
|
|
|
-
|
|
|
- &.selected {
|
|
|
- background: #fff;
|
|
|
- border-color: #2199f8;
|
|
|
+ line-height: 36px;
|
|
|
+ cursor: pointer;
|
|
|
+ color: #000;
|
|
|
+ background: #f3f4f6;
|
|
|
+ border: 1px solid transparent;
|
|
|
+
|
|
|
+ .text {
|
|
|
+ display: inline-block;
|
|
|
+ max-width: 100%;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ vertical-align: top;
|
|
|
+ padding: 0 6px;
|
|
|
+ }
|
|
|
|
|
|
- .category-tag__text {
|
|
|
+ &.selected {
|
|
|
+ border: 1px solid #2199f8;
|
|
|
+ background: #e8f5ff;
|
|
|
color: #2199f8;
|
|
|
- }
|
|
|
|
|
|
- &::after {
|
|
|
- content: "";
|
|
|
- position: absolute;
|
|
|
- top: -1px;
|
|
|
- right: -1px;
|
|
|
- width: 18px;
|
|
|
- height: 14px;
|
|
|
- background: url("@/assets/img/home/checked-bg-top.png") no-repeat bottom right / 18px 13px;
|
|
|
+ &::after {
|
|
|
+ content: "";
|
|
|
+ position: absolute;
|
|
|
+ z-index: 9;
|
|
|
+ top: -1px;
|
|
|
+ right: -1px;
|
|
|
+ width: 18px;
|
|
|
+ height: 14px;
|
|
|
+ background: url("@/assets/img/home/checked-bg-top.png") no-repeat bottom right / 18px 13px;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.empty-tip {
|
|
|
- grid-column: 1 / -1;
|
|
|
padding: 48px 0;
|
|
|
text-align: center;
|
|
|
color: rgba(0, 0, 0, 0.35);
|