diff --git a/frontend/src/composables/useColumnPreferences.js b/frontend/src/composables/useColumnPreferences.js new file mode 100644 index 0000000..760e06e --- /dev/null +++ b/frontend/src/composables/useColumnPreferences.js @@ -0,0 +1,54 @@ +import { ref } from 'vue' + +export function useColumnPreferences(storageKey) { + const hidden = ref([]) + + const load = () => { + try { + const raw = localStorage.getItem(storageKey) + if (!raw) return + const parsed = JSON.parse(raw) + if (Array.isArray(parsed)) { + hidden.value = parsed.filter((x) => typeof x === 'string') + } + } catch { + hidden.value = [] + } + } + + const save = () => { + try { + localStorage.setItem(storageKey, JSON.stringify(hidden.value)) + } catch { + /* quota / private mode — ignore */ + } + } + + const isVisible = (key) => !hidden.value.includes(key) + + const toggle = (key) => { + const i = hidden.value.indexOf(key) + if (i >= 0) hidden.value.splice(i, 1) + else hidden.value.push(key) + save() + } + + const setGroupVisible = (groupKeys, visible) => { + if (visible) { + hidden.value = hidden.value.filter((k) => !groupKeys.includes(k)) + } else { + const set = new Set(hidden.value) + groupKeys.forEach((k) => set.add(k)) + hidden.value = [...set] + } + save() + } + + const reset = () => { + hidden.value = [] + save() + } + + load() + return { hidden, isVisible, toggle, setGroupVisible, reset } +} diff --git a/frontend/src/views/material/materialColumns.js b/frontend/src/views/material/materialColumns.js new file mode 100644 index 0000000..4b67a4b --- /dev/null +++ b/frontend/src/views/material/materialColumns.js @@ -0,0 +1,52 @@ +export const COLUMN_GROUPS = [ + { key: 'material', label: '材料信息' }, + { key: 'supplier', label: '品牌与供应商' }, + { key: 'case', label: '案例信息' }, +] + +const fmt = (v) => (v === null || v === undefined || v === '' ? '-' : v) + +export const MATERIAL_COLUMNS = [ + // ==== A. 材料信息 ==== + { group: 'material', key: 'name', label: '材料名称', minWidth: 180, showOverflowTooltip: true }, + { group: 'material', key: 'major_category_display', label: '材料大类', width: 100 }, + { group: 'material', key: 'material_category', label: '细分种类', minWidth: 140, showOverflowTooltip: true }, + { group: 'material', key: 'material_subcategory', label: '材料子类', minWidth: 140, showOverflowTooltip: true }, + { group: 'material', key: 'stage_display', label: '阶段', width: 130, showOverflowTooltip: true }, + { group: 'material', key: 'importance_level_display', label: '重要等级', width: 110 }, + { group: 'material', key: 'status_display', label: '状态', width: 100 }, + { group: 'material', key: 'cost_compare', label: '成本比较(%)', width: 120, formatter: (r) => fmt(r.cost_compare) }, + { group: 'material', key: 'cost_desc', label: '成本说明', minWidth: 160, showOverflowTooltip: true }, + { group: 'material', key: 'advantage', label: '优势', minWidth: 200, slot: 'tags' }, + { group: 'material', key: 'advantage_desc', label: '优势说明', minWidth: 160, showOverflowTooltip: true }, + { group: 'material', key: 'application_scene', label: '应用场景', minWidth: 200, slot: 'tags' }, + { group: 'material', key: 'application_desc', label: '应用说明', minWidth: 160, showOverflowTooltip: true }, + { group: 'material', key: 'replace_type_display', label: '替代类型', width: 120, showOverflowTooltip: true }, + { group: 'material', key: 'connection_method', label: '连接方式', width: 120, showOverflowTooltip: true }, + { group: 'material', key: 'construction_method', label: '施工方式', width: 120, showOverflowTooltip: true }, + { group: 'material', key: 'limit_condition', label: '使用限制', minWidth: 140, showOverflowTooltip: true }, + { group: 'material', key: 'spec', label: '规格', width: 120, showOverflowTooltip: true }, + { group: 'material', key: 'standard', label: '执行标准', width: 120, showOverflowTooltip: true }, + { group: 'material', key: 'quality_level', label: '质量', width: 90, slot: 'stars' }, + { group: 'material', key: 'durability_level', label: '耐久', width: 90, slot: 'stars' }, + { group: 'material', key: 'eco_level', label: '环保', width: 90, slot: 'stars' }, + { group: 'material', key: 'carbon_level', label: '碳', width: 90, slot: 'stars' }, + { group: 'material', key: 'score_level', label: '综合评分', width: 110, slot: 'stars' }, + + // ==== B. 品牌与供应商 ==== + { group: 'supplier', key: 'brand_name', label: '品牌', minWidth: 140, showOverflowTooltip: true }, + { group: 'supplier', key: 'factory_short_name', label: '供应商简称', minWidth: 140, showOverflowTooltip: true }, + { group: 'supplier', key: 'factory_name', label: '供应商全称', minWidth: 180, showOverflowTooltip: true }, + { group: 'supplier', key: 'factory_cooperation_mode_display', label: '合作模式', width: 110 }, + { group: 'supplier', key: 'factory_location', label: '省-市', width: 140, formatter: (r) => [r.factory_province, r.factory_city].filter(Boolean).join('-') || '-' }, + { group: 'supplier', key: 'contact_person', label: '对接人', width: 100, showOverflowTooltip: true }, + { group: 'supplier', key: 'contact_phone', label: '对接电话', width: 150, showOverflowTooltip: true }, + + // ==== C. 案例信息 ==== + { group: 'case', key: 'landing_project', label: '落地项目', minWidth: 140, showOverflowTooltip: true }, + { group: 'case', key: 'cases', label: '案例', minWidth: 200, showOverflowTooltip: true }, + { group: 'case', key: 'handler', label: '经办人', width: 100, showOverflowTooltip: true }, + { group: 'case', key: 'remark', label: '备注', minWidth: 160, showOverflowTooltip: true }, +] + +export const ALL_COLUMN_KEYS = MATERIAL_COLUMNS.map((c) => c.key)