From a47156f055345618b11d98b9d0d35ee56678e426 Mon Sep 17 00:00:00 2001 From: "han\\hanst" Date: Wed, 22 Apr 2026 11:39:51 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A2=9C=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/index.js | 1 - src/views/common/home.vue | 198 ++++++++++++++++-- .../modules/sift/advancedSearchCenter.vue | 154 +++++++++++++- .../modules/sift/menuSearchFieldResolver.js | 16 +- 4 files changed, 340 insertions(+), 29 deletions(-) diff --git a/src/router/index.js b/src/router/index.js index ad769e9d..9d4358a3 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -26,7 +26,6 @@ const globalRoutes = [ { path: '/screen-cable-cop-progress', component: _import('modules/longchuang/screen-cable-cop-progress'), name: 'screen-cable-cop-progress', meta: { title: '线缆/COP生产进度看板' } }, { path: '/screen-renovation-progress', component: _import('modules/longchuang/screen-renovation-progress'), name: 'screen-renovation-progress', meta: { title: '改造项目生产进度看板' } }, { path: '/screen-factory-overview', component: _import('modules/longchuang/screen-factory-overview'), name: 'screen-factory-overview', meta: { title: '工厂综合运营看板' } }, - { path: '/production-work-report', component: _import('modules/longchuang/production-work-report'), name: 'production-work-report', meta: { title: '生产报工页面' } }, ] // 主入口路由(需嵌套上左右整体布局) diff --git a/src/views/common/home.vue b/src/views/common/home.vue index 0dcf4296..fad84601 100644 --- a/src/views/common/home.vue +++ b/src/views/common/home.vue @@ -1,10 +1,10 @@ + @@ -98,7 +134,7 @@ import { import { resolveMenuSearchFieldsByMeta } from '../modules/sift/menuSearchFieldResolver' export default { - name: 'home', + name: 'advanced-search-center', data () { return { schemeLoading: false, @@ -117,8 +153,10 @@ export default { menuId: '', itemNo: null, itemDesc: '', - caseInsensitive: true + caseInsensitive: true, + schemeColor: '#409EFF' }, + schemeColorPredefine: ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#9C27B0', '#00ACC1', '#FF7043'], symbolOptions: [ { value: 'eq', label: '等于' }, { value: 'gt', label: '大于' }, @@ -140,12 +178,18 @@ export default { in: 'in', ne: '!=', like: 'like' - } + }, + defaultSchemeColor: '#409EFF' } }, computed: { menuOptions () { - return this.menuFlatList.filter(item => item.url && !item.hasChildren && !/^https?:\/\//i.test(item.url)) + return this.menuFlatList.filter(item => + item.url && // 1. 必须有 url 属性 + !item.hasChildren && // 2. 必须没有子菜单 + !/^https?:\/\//i.test(item.url) && // 3. 必须不是 http/https 开头的外部链接 + item.url.includes('/') // 4. [新增] url 必须包含 '/' 字符 + ) } }, activated () { @@ -211,6 +255,90 @@ export default { return fieldName + ' ' + symbol + ' ' + item.formula }) }, + getSchemeColorStorageKey () { + return 'advancedSearchCenterSchemeColorMap_' + String(this.$store.state.user.id || '') + }, + loadStoredSchemeColorMap () { + try { + var text = localStorage.getItem(this.getSchemeColorStorageKey()) + var map = text ? JSON.parse(text) : {} + return map && typeof map === 'object' ? map : {} + } catch (e) { + return {} + } + }, + saveStoredSchemeColorMap (map) { + try { + localStorage.setItem(this.getSchemeColorStorageKey(), JSON.stringify(map || {})) + } catch (e) { + } + }, + normalizeSchemeColor (color) { + if (!color) { + return '' + } + var value = String(color).trim() + if (!value) { + return '' + } + if (/^#([0-9a-fA-F]{3}){1,2}$/.test(value)) { + return value.toUpperCase() + } + return '' + }, + resolveSchemeColorFromRow (row) { + var source = row || {} + var candidates = [ + source.schemeColor, + source.colorHex, + source.cardColor, + source.themeColor, + source.color + ] + for (var i = 0; i < candidates.length; i++) { + var normalized = this.normalizeSchemeColor(candidates[i]) + if (normalized) { + return normalized + } + } + return '' + }, + hexToRgba (hex, alpha) { + var value = this.normalizeSchemeColor(hex) + if (!value) { + return 'rgba(64, 158, 255, ' + alpha + ')' + } + var color = value.slice(1) + if (color.length === 3) { + color = color.split('').map(char => char + char).join('') + } + var r = parseInt(color.slice(0, 2), 16) + var g = parseInt(color.slice(2, 4), 16) + var b = parseInt(color.slice(4, 6), 16) + return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')' + }, + buildSchemeCardStyle (row) { + var color = this.normalizeSchemeColor((row || {}).schemeColor) || this.defaultSchemeColor + return { + '--scheme-accent': color, + '--scheme-bg-start': this.hexToRgba(color, 0.16), + '--scheme-bg-end': this.hexToRgba(color, 0.04), + '--scheme-border': this.hexToRgba(color, 0.28) + } + }, + upsertStoredSchemeColor (schemeKey, color) { + if (!schemeKey) { + return + } + var normalizedColor = this.normalizeSchemeColor(color) + var map = this.loadStoredSchemeColorMap() + if (normalizedColor) { + map[schemeKey] = normalizedColor + } else { + delete map[schemeKey] + } + this.saveStoredSchemeColorMap(map) + }, toSnakeCaseFieldName (name) { if (!name) { return '' @@ -236,6 +364,7 @@ export default { async loadSavedSchemeList () { this.schemeLoading = true try { + var storedColorMap = this.loadStoredSchemeColorMap() var userId = String(this.$store.state.user.id) var summaryResp = await searchSavedSchemeSummary({ userId: userId }) var summaryData = summaryResp && summaryResp.data ? summaryResp.data : {} @@ -281,11 +410,13 @@ export default { this.schemeList = summaryRows.map(item => { var key = this.buildSchemeKey(item) var menu = this.menuFlatList.find(m => String(m.menuId) === String(item.menuId)) + var rowColor = this.resolveSchemeColorFromRow(item) || this.normalizeSchemeColor(storedColorMap[key]) || this.defaultSchemeColor return Object.assign({}, item, { schemeKey: key, count: countMap[key] !== undefined ? countMap[key] : null, menuName: item.menuName || (menu ? menu.name : ('菜单' + item.menuId)), - conditionPreviewList: this.buildConditionPreviewList(detailMap[key]) + conditionPreviewList: this.buildConditionPreviewList(detailMap[key]), + schemeColor: rowColor }) }) } catch (e) { @@ -343,6 +474,7 @@ export default { this.editForm.itemNo = row.itemNo this.editForm.itemDesc = row.itemDesc this.editForm.caseInsensitive = true + this.editForm.schemeColor = this.normalizeSchemeColor(row.schemeColor) || this.defaultSchemeColor this.loadEditFields() }, newScheme () { @@ -350,7 +482,8 @@ export default { menuId: '', itemNo: null, itemDesc: '', - caseInsensitive: true + caseInsensitive: true, + schemeColor: this.defaultSchemeColor } this.editFieldList = [] this.editSelectedFieldNameSet = {} @@ -479,12 +612,17 @@ export default { this.editSaving = true try { var userId = String(this.$store.state.user.id) + var normalizedSchemeColor = this.normalizeSchemeColor(this.editForm.schemeColor) || this.defaultSchemeColor + var currentItemNo = this.editForm.itemNo + var currentMenuId = this.editForm.menuId + var currentItemDesc = this.editForm.itemDesc.trim() var payload = { menuId: this.editForm.menuId, userId: userId, dtsName: 'ADOQResult', itemNo: this.editForm.itemNo, itemDesc: this.editForm.itemDesc.trim(), + schemeColor: normalizedSchemeColor, caseSensitiveFlag: this.editForm.caseInsensitive ? 'N' : 'Y', querySavedDetailList: selectedList.map(item => { var normalizedItem = this.normalizeOriginalFieldForSql(item) @@ -508,6 +646,34 @@ export default { if (data && data.code === 0) { this.$message.success(data.msg || '保存成功') await this.loadSavedSchemeList() + if (currentItemNo) { + var currentKey = this.buildSchemeKey({ + menuId: currentMenuId, + itemNo: currentItemNo + }) + this.upsertStoredSchemeColor(currentKey, normalizedSchemeColor) + this.schemeList = this.schemeList.map(item => { + if (item.schemeKey === currentKey) { + return Object.assign({}, item, { schemeColor: normalizedSchemeColor }) + } + return item + }) + } else { + var createdRows = this.schemeList.filter(item => + String(item.menuId) === String(currentMenuId) && + String((item.itemDesc || '')).trim() === currentItemDesc) + createdRows.forEach(item => { + this.upsertStoredSchemeColor(item.schemeKey, normalizedSchemeColor) + }) + if (createdRows.length) { + var createdMap = {} + createdRows.forEach(item => { createdMap[item.schemeKey] = true }) + this.schemeList = this.schemeList.map(item => + createdMap[item.schemeKey] + ? Object.assign({}, item, { schemeColor: normalizedSchemeColor }) + : item) + } + } if (!this.editForm.itemNo) { this.newScheme() } @@ -572,12 +738,13 @@ export default { .mod-advanced-search-center { padding: 12px; } - .board-table .cell { line-height: 20px; height: 20px; } - +.board-table { + margin-top: 12px; +} .page-header { display: flex; align-items: center; @@ -600,8 +767,8 @@ export default { .scheme-card { position: relative; border-radius: 10px; - border: 1px solid #ebeef5; - background: linear-gradient(160deg, #ffffff, #f7f9fc); + border: 1px solid var(--scheme-border, #ebeef5); + background: linear-gradient(160deg, var(--scheme-bg-start, #ffffff), var(--scheme-bg-end, #f7f9fc)); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06); padding: 12px; cursor: pointer; @@ -620,7 +787,7 @@ export default { min-width: 28px; padding: 2px 8px; border-radius: 12px; - background: #1abc9c; + background: var(--scheme-accent, #1abc9c); color: #fff; font-size: 12px; text-align: center; @@ -671,4 +838,3 @@ export default { margin-top: 8px; } - diff --git a/src/views/modules/sift/advancedSearchCenter.vue b/src/views/modules/sift/advancedSearchCenter.vue index 06effe95..70798b30 100644 --- a/src/views/modules/sift/advancedSearchCenter.vue +++ b/src/views/modules/sift/advancedSearchCenter.vue @@ -15,6 +15,7 @@ class="scheme-card" v-for="row in schemeList" :key="row.schemeKey" + :style="buildSchemeCardStyle(row)" @click="gotoSchemeResult(row)">
{{ row.count === null ? '-' : row.count }}
{{ row.itemDesc || '未命名方案' }}
@@ -57,6 +58,13 @@ + + + + @@ -145,8 +153,10 @@ export default { menuId: '', itemNo: null, itemDesc: '', - caseInsensitive: true + caseInsensitive: true, + schemeColor: '#409EFF' }, + schemeColorPredefine: ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#9C27B0', '#00ACC1', '#FF7043'], symbolOptions: [ { value: 'eq', label: '等于' }, { value: 'gt', label: '大于' }, @@ -168,12 +178,18 @@ export default { in: 'in', ne: '!=', like: 'like' - } + }, + defaultSchemeColor: '#409EFF' } }, computed: { menuOptions () { - return this.menuFlatList.filter(item => item.url && !item.hasChildren && !/^https?:\/\//i.test(item.url)) + return this.menuFlatList.filter(item => + item.url && // 1. 必须有 url 属性 + !item.hasChildren && // 2. 必须没有子菜单 + !/^https?:\/\//i.test(item.url) && // 3. 必须不是 http/https 开头的外部链接 + item.url.includes('/') // 4. [新增] url 必须包含 '/' 字符 + ) } }, activated () { @@ -239,6 +255,90 @@ export default { return fieldName + ' ' + symbol + ' ' + item.formula }) }, + getSchemeColorStorageKey () { + return 'advancedSearchCenterSchemeColorMap_' + String(this.$store.state.user.id || '') + }, + loadStoredSchemeColorMap () { + try { + var text = localStorage.getItem(this.getSchemeColorStorageKey()) + var map = text ? JSON.parse(text) : {} + return map && typeof map === 'object' ? map : {} + } catch (e) { + return {} + } + }, + saveStoredSchemeColorMap (map) { + try { + localStorage.setItem(this.getSchemeColorStorageKey(), JSON.stringify(map || {})) + } catch (e) { + } + }, + normalizeSchemeColor (color) { + if (!color) { + return '' + } + var value = String(color).trim() + if (!value) { + return '' + } + if (/^#([0-9a-fA-F]{3}){1,2}$/.test(value)) { + return value.toUpperCase() + } + return '' + }, + resolveSchemeColorFromRow (row) { + var source = row || {} + var candidates = [ + source.schemeColor, + source.colorHex, + source.cardColor, + source.themeColor, + source.color + ] + for (var i = 0; i < candidates.length; i++) { + var normalized = this.normalizeSchemeColor(candidates[i]) + if (normalized) { + return normalized + } + } + return '' + }, + hexToRgba (hex, alpha) { + var value = this.normalizeSchemeColor(hex) + if (!value) { + return 'rgba(64, 158, 255, ' + alpha + ')' + } + var color = value.slice(1) + if (color.length === 3) { + color = color.split('').map(char => char + char).join('') + } + var r = parseInt(color.slice(0, 2), 16) + var g = parseInt(color.slice(2, 4), 16) + var b = parseInt(color.slice(4, 6), 16) + return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')' + }, + buildSchemeCardStyle (row) { + var color = this.normalizeSchemeColor((row || {}).schemeColor) || this.defaultSchemeColor + return { + '--scheme-accent': color, + '--scheme-bg-start': this.hexToRgba(color, 0.16), + '--scheme-bg-end': this.hexToRgba(color, 0.04), + '--scheme-border': this.hexToRgba(color, 0.28) + } + }, + upsertStoredSchemeColor (schemeKey, color) { + if (!schemeKey) { + return + } + var normalizedColor = this.normalizeSchemeColor(color) + var map = this.loadStoredSchemeColorMap() + if (normalizedColor) { + map[schemeKey] = normalizedColor + } else { + delete map[schemeKey] + } + this.saveStoredSchemeColorMap(map) + }, toSnakeCaseFieldName (name) { if (!name) { return '' @@ -264,6 +364,7 @@ export default { async loadSavedSchemeList () { this.schemeLoading = true try { + var storedColorMap = this.loadStoredSchemeColorMap() var userId = String(this.$store.state.user.id) var summaryResp = await searchSavedSchemeSummary({ userId: userId }) var summaryData = summaryResp && summaryResp.data ? summaryResp.data : {} @@ -309,11 +410,13 @@ export default { this.schemeList = summaryRows.map(item => { var key = this.buildSchemeKey(item) var menu = this.menuFlatList.find(m => String(m.menuId) === String(item.menuId)) + var rowColor = this.resolveSchemeColorFromRow(item) || this.normalizeSchemeColor(storedColorMap[key]) || this.defaultSchemeColor return Object.assign({}, item, { schemeKey: key, count: countMap[key] !== undefined ? countMap[key] : null, menuName: item.menuName || (menu ? menu.name : ('菜单' + item.menuId)), - conditionPreviewList: this.buildConditionPreviewList(detailMap[key]) + conditionPreviewList: this.buildConditionPreviewList(detailMap[key]), + schemeColor: rowColor }) }) } catch (e) { @@ -371,6 +474,7 @@ export default { this.editForm.itemNo = row.itemNo this.editForm.itemDesc = row.itemDesc this.editForm.caseInsensitive = true + this.editForm.schemeColor = this.normalizeSchemeColor(row.schemeColor) || this.defaultSchemeColor this.loadEditFields() }, newScheme () { @@ -378,7 +482,8 @@ export default { menuId: '', itemNo: null, itemDesc: '', - caseInsensitive: true + caseInsensitive: true, + schemeColor: this.defaultSchemeColor } this.editFieldList = [] this.editSelectedFieldNameSet = {} @@ -507,12 +612,17 @@ export default { this.editSaving = true try { var userId = String(this.$store.state.user.id) + var normalizedSchemeColor = this.normalizeSchemeColor(this.editForm.schemeColor) || this.defaultSchemeColor + var currentItemNo = this.editForm.itemNo + var currentMenuId = this.editForm.menuId + var currentItemDesc = this.editForm.itemDesc.trim() var payload = { menuId: this.editForm.menuId, userId: userId, dtsName: 'ADOQResult', itemNo: this.editForm.itemNo, itemDesc: this.editForm.itemDesc.trim(), + schemeColor: normalizedSchemeColor, caseSensitiveFlag: this.editForm.caseInsensitive ? 'N' : 'Y', querySavedDetailList: selectedList.map(item => { var normalizedItem = this.normalizeOriginalFieldForSql(item) @@ -536,6 +646,34 @@ export default { if (data && data.code === 0) { this.$message.success(data.msg || '保存成功') await this.loadSavedSchemeList() + if (currentItemNo) { + var currentKey = this.buildSchemeKey({ + menuId: currentMenuId, + itemNo: currentItemNo + }) + this.upsertStoredSchemeColor(currentKey, normalizedSchemeColor) + this.schemeList = this.schemeList.map(item => { + if (item.schemeKey === currentKey) { + return Object.assign({}, item, { schemeColor: normalizedSchemeColor }) + } + return item + }) + } else { + var createdRows = this.schemeList.filter(item => + String(item.menuId) === String(currentMenuId) && + String((item.itemDesc || '')).trim() === currentItemDesc) + createdRows.forEach(item => { + this.upsertStoredSchemeColor(item.schemeKey, normalizedSchemeColor) + }) + if (createdRows.length) { + var createdMap = {} + createdRows.forEach(item => { createdMap[item.schemeKey] = true }) + this.schemeList = this.schemeList.map(item => + createdMap[item.schemeKey] + ? Object.assign({}, item, { schemeColor: normalizedSchemeColor }) + : item) + } + } if (!this.editForm.itemNo) { this.newScheme() } @@ -626,8 +764,8 @@ export default { .scheme-card { position: relative; border-radius: 10px; - border: 1px solid #ebeef5; - background: linear-gradient(160deg, #ffffff, #f7f9fc); + border: 1px solid var(--scheme-border, #ebeef5); + background: linear-gradient(160deg, var(--scheme-bg-start, #ffffff), var(--scheme-bg-end, #f7f9fc)); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06); padding: 12px; cursor: pointer; @@ -646,7 +784,7 @@ export default { min-width: 28px; padding: 2px 8px; border-radius: 12px; - background: #1abc9c; + background: var(--scheme-accent, #1abc9c); color: #fff; font-size: 12px; text-align: center; diff --git a/src/views/modules/sift/menuSearchFieldResolver.js b/src/views/modules/sift/menuSearchFieldResolver.js index ec372bc1..d347c917 100644 --- a/src/views/modules/sift/menuSearchFieldResolver.js +++ b/src/views/modules/sift/menuSearchFieldResolver.js @@ -17,8 +17,16 @@ function extractTemplateContent(vueSource) { return '' } var text = typeof vueSource === 'string' ? vueSource : (vueSource.default || '') - var match = text.match(/') + if (end <= start) { + return '' + } + return text.substring(start, end) } function normalizeLabel(rawLabel, fallbackName) { @@ -48,12 +56,12 @@ function parseSearchFieldsFromVueSource(vueSource) { var blocks = template.match(formItemRegex) || [] var orderSeq = 1 blocks.forEach(block => { - var modelMatch = block.match(/v-model\s*=\s*"searchData\.([A-Za-z0-9_]+)"/) + var modelMatch = block.match(/v-model\s*=\s*["']searchData\.([A-Za-z0-9_]+)["']/) if (!modelMatch || !modelMatch[1]) { return } var fieldName = modelMatch[1] - var labelMatch = block.match(/(?:\s:label|\slabel)\s*=\s*"([^"]*)"/) + var labelMatch = block.match(/(?:\s:label|\slabel)\s*=\s*["']([^"']*)["']/) var fieldCaption = normalizeLabel(labelMatch ? labelMatch[1] : '', fieldName) var dataType = /